‘壹’ 【学了就忘】Git操作 — 48.Git存储
有时当你在项目的一个分支上,已经工作一段时间后,所有东西都进入了混乱的状态, 而这时你想要切换到另一个分支做一点别的事情。 问题是,你不想仅仅因为这点别的事,而把刚刚做了一半的工作进行一次提交。
针对这个问题,可以使用 git stash 命令来解决。(stash:贮藏)
git stash 命令会处理工作目录的脏状态,即:根据文件的修改与暂存的改动,然后将未完成的修改保存到一个栈上, 而你可以在任何时候重新应用这些改动(甚至在不同的分支上)。
现在本地版本库中情况如下:
有两分支, master 分支和 dev 分支。
此时正在开发 dev 分支中的 stash.txt 文件。
下面开始演示 git stash 命令用法。
1)从未完成工作 dev 分支,切换到 master 工作。
2)在 master 分支工作完成之后,在切换回 dev 分支。
3)删除Git存储栈中的内容
在工作中通常使用 git stash pop 命令,代替 git stash apply 和 git stash drop 命令。
‘贰’ 4、git add和git commit
git中的文件对象,最终都是存储在.git/objects目录中,以二进制形式存储的。git既松散存储,也进行差异化存储。每次修改一个文件,会在.git/objects目录中,生成一个二进制对象,当再次修改时,又生成另一个二进制对象,这即是松散存储。但是当工作区的二进制对象数超过6700个时(数目可配置),git会进行一次打包行为,针对相同文件进行差异化存储来减少文件数。
git大概总共有100多个命令,命令不是很多,但是每个命令,都可以带很多参数,功能很强大。git命令总共分为两类:高级命令和底层命令。
git add和git commit都属于高级命令,高级命令是通过调用多个底层命令来完成操作,这一节希望大家能彻底搞明白git add和git commit.
举个例子,假如我们新建一个工程,并新建了一个test.go文件,在这个文件里边随便写一些内容,下面我们用底层命令来生成一个完整的git历史记录:
第一步:针对test.go文件生成二进制存储对象
git hash-object命令就是针对某一个文件生成它的二进制存储对象,并返回它的sha1签名码,假设返回的是""
第二步:更新索引文件
这一步是将我们上一步生成的对象以及它的路径添加到索引中。用到了我们上一节讲过的updae-index命令。
以上两步命令和git add test.go命令完全等价。
第三步:生成目录树对象
调用write-tree命令之后,git会针对当前索引区的目录结构,生成一个tree对象,并返回这个tree对象的sha1签名码,假设返回:d8329f
第四步:生成commit对象
echo后边单引号里边的内容就是我们的提交message了。第三步和第四步就是完成了一次git commit命令的操作。
大家可以试着操作一下以上四部命令,并观察一下.git/objects目录和工作区git状态的变化。
‘叁’ git是什么工具
git是目前世界上最先进的分布式版本控制工具(没有之一)。
Git认为其数据更像是一系列微型文件系统的快照。使用Git,每次提交或保存项目状态时,Git基本上都会记录当时所有文件的外观,并存储对该快照的引用。
为了提高效率,如果文件没有改变,Git不会再次存储文件,只是指向它已存储的上一个相同文件的链接。Git认为它的数据更像是一个快照流,会将数据作为项目的快照存储一段时间。
几乎每一项行动都是本地的
Git中的大多数操作只需要本地文件和资源来运行 - 通常不需要来自网络上另一台计算机的信息。
当您在Git中执行操作时,几乎所有操作都只将数据添加到Git数据库。很难让系统做任何不可撤销的事情或者以任何方式擦除数据。与任何VCS一样,您可能会丢失或搞乱尚未提交的更改,但在将快照提交到Git之后,很难丢失,尤其是在您经常将数据库推送到另一个存储库时。
‘肆’ Git的存储机制
答:
git在正确的抽象层次上处理存储空间压缩,使得程序比上代(如svn)和同时代(如bzr)的VCS大为简化。
之前的VCS从节省空间出发,想当然地把delta作为基础数据结构。这样的最大问题(远不是唯一问题)是:delta设计时没有考虑到的修改方式就无法表达。例如,svn早期版本及更早的VCS不能记录文件rename/,所以只要重命名文件,列出历史时重命名之前的历史就显示不出来。
git的存储层(plumbing layer)提供的接口里,每个commit都记录完整的目录树。存储空间压缩问题完全在存储层内部解决:
‘伍’ ideagit本地分支保存在哪里
储存库。git是一个免费的、开源的分布式版本控制系统。ideagit本地分支保存在储存库。ideagit可以快速高效地处理从小型到大型的各种项目,易于学习,占地面积小,性能极快。
‘陆’ git的基本使用
git 采用的是分布式仓库,本地和远程仓库都是一个包含完整记录的仓库,如果某一处被格式化了,还以从另一个仓库获取,并不会有大影响(只要之前进行了同步仓库)。git 可以在断网的情况下,提交到本地仓库,之后联网再推到远程仓库。每次提交或者保存,git都会记录下所有文件状态,若未改变的文件,git不会再次存储,只会链接到之前已经存储的文件。
git文件一共有四种状态:
以下为git中存在的,并在跟踪的
创建本地.gitignore
创建一个global .gitignore
指定仓库排除
参考 Ignoring files
这里遇到一个问题:当我不修改系统默认的提交信息时(或者只是换行不输入任何信息),然后关掉编辑器会出现提交不成功,并会提示
Aborting commit e to empty commit message.
解决方法 :必须输入新的提交信息,即在原来的基础上,至少要加入新的行数据。
git rm *~ 删除所有以~这个结尾的文件
限定输出
更多限定
Table 3. Options to limit the output of git log
参考至: Git Basics - Undoing Things
`eg
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend`
展示仓库名
fetch、pull、push
(==注==:origin 是用git clone到本地之后,自动命名,如果在你push之前,有人先push了,那么push会失败,必须fetch下来合并,并且运行起来,才能在push)
指定一个提交(commit)点作为tag,并不保存额外的信息、
包含tagger 名字、邮箱、日期、tagging msg等信息
-m 指定标记消息,若没有,系统会自动采用默认的编辑器
给之前提交(commit)加上tag
分享tags
默认情况下,git push 并不会将本地的tag,push到远程服务器上,用以下两个命令行推到远程仓库上
检出Tags
必须新建一个分支,然后将要检出的tags,指定到这新分支上
为了是命令更简单、更容易明白和操作,可以给命令添加别名如下:
运行的时候直接打 git ci可以
可视化命令 gitk
‘柒’ Git底层数据结构和原理之三:存储模型
git 区别与其他 vcs 系统的一个最主要原因之一是:git 对文件版本管理和其他 vcs 系统对文件版本的实现理念完成不一样。这也就是 git 版本管理为什么如此强大的最核心的地方。
SVN 等其他的 VCS 对文件版本的理念是以文件为水平维度,记录每个文件在每个版本下的 delta 改变。
Git 对文件版本的管理理念却是以每次提交为一次快照,提交时对所有文件做一次全量快照,然后存储快照引用。
Git 在存储层,如果文件数据没有改变的文件,Git 只是存储指向源文件的一个引用,并不会直接多次存储文件,这一点可以在 pack 文件中看见。如下图所示:
存储随着需求和功能的不断复杂,git 版本的不断更新,但是主要的存储模型还是大致不变。如下图所示:
‘捌’ Git工作原理
按照官方文档的描述,Git是这样定义的
.git目录是Git的核心,每一个变动都会存储在.git文件夹中,Git的相关命令本质上也是读取.git文件夹下的内容
.git目录下有几个重要的文件/文件夹
提交和文件是Git中的主要组成,也叫git对象,Git中的许多命令都和git对象有关
git对象分为下面3类
git对象存储在.git目录下的objects文件夹中,Git会将git对象压缩成二进制文件,git对象的文件名即sha-1算法得到的hash值,按照2/38的形式保存(前两位是文件夹的名称,剩下38位是文件名,这样做可以防止文件夹的内容过多,提高查找效率)
对于commit对象,hash值也被称为commitid
可以使用以下命令查看git对象中的内容
git cat-file -p <hash>
通过查看三种git对象的内容,不难发现如下的组织关系
其实可以吧hash看成每个对象的指针,Git通过指针将众多git对象串联起来,来实现对项目的版本控制
用户通过Git命令读写.git文件夹,达到获取信息或变更版本的目的
Git一开始被设计成供VCS使用的工具集合而不是一整套用户有好的VCS,它还包含了许多的底层命令,一般被称为plumbing命令(底层命令),而用户日常使用Git命令被称为porcelain命令(高层命令),porcelain命令实际是是对plumbing命令的封装
一次完整的提交过程会包含如下过程
使用porcelain命令的话是非常简单的
如果使用plumbing命令就会复杂很多,但是可以更好地理解其背后的工作原理
通过前文的内容不难发现,每次生成的commit对象会包含上一个commit对象的hash,即当前的commit包含上一个commit的指针,许多个commit对象串联起来就形成了分支
所以,Git的分支本质上是指向commit对象的可变指针
而HEAD代表当前commit的指向,.git/refs/heads/<branchname>文件的内容就是该commit对象的hash