一、传送
二、基本概念
① 工作区
所谓工作区,就是你在电脑里能看到的目录,可以简单地理解为我们的项目根目录就是工作区。
② 版本库
所谓版本库,就是在项目根目录下执行git init
命令之后,会在项目根目录下生成一个.git目录,正常情况下,这个.git目录是隐藏的,这个.git目录就是版本库。
③ 暂存区
所谓暂存区,就是.git版本库目录中存放的index文件,所以我们也会把暂存区叫作索引(index)区。
所以,完成一次提交包括三个步骤,首先对工作区中的内容进行修改,然后通过add命令将工作区的修改纳入到暂存区中,最后将暂存区的修改通过commit命令提交到版本库中。
三、status命令
git status
命令用于查看当前工作区中所发生的修改,以及其中哪些修改已经添加到了暂存区中,简单说,就是status命令可以查看工作区中有哪些文件还没有被追踪,哪些被追踪的文件被修改后有没有被添加到暂存区(当前在工作区还是在暂存区)。
> git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: foo.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
Untracked files:
(use "git add <file>..." to include in what will be committed)
bar.txt
可以看到,bar.txt是新添加的文件,还没有被git追踪;index.html是已经被追踪的文件,在工作区中修改后还没有提交到暂存区;foo.txt是在工作区中修改后同时将修改添加到了暂存区中。
四、diff命令
① git diff
: 不带任何参数的git diff
命令,是比较工作区和暂存区的差别。比如,工作区中某个文件发生了修改,但是还未添加到暂存区中,此时工作区和暂存区是有差别的,通过git diff
命令即可查看到工作区的修改内容。
② git diff --cached
: 带上了--cached参数,此时同git diff --staged
,比较的是暂存区和最后一次提交的差别。比如,工作区内容修改后,同时将工作区的修改添加到了暂存区中,那么此时,暂存区和最新一次提交是有差别的,因为目前最新一次提交是上次的提交。
③ git diff head
: head表示的最后一次提交(最新的提交),是比较(工作区和暂存区)与最后一次提交的差别,即可以同时查看工作区、暂存区与最后一次提交的区别。
五、回退修改
① 取消工作区的修改: 如果对工作区的内容进行了修改,但是该修改还没有添加到暂存区,那么可以通过git checkout -- <file>
的方式来取消工作区的修改。
② 取消暂存区的修改: 如果对工作区的内容进行了修改,同时该修改已经添加到了暂存区,那么可以通过reset命令来重置暂存区,因为工作区的修改提交到了暂存区,现在暂存区和最后一次提交出现了差别,所以我们可以将暂存区重置到head位置,即最后一次提交的位置,即可恢复暂存区,即git reset head <file>
,head后面不加文件名则表示重置暂存区中的所有文件。
③ 回退到某个版本: 如果经过一系列操作后,想要回到某个版本,那么可以通过git reset --hard <commit-id>
回到指定的提交位置。不加--hard,也可以回到指定的提交位置,但是不会清空工作区的修改,只会清空暂存区的修改,而加上--hard则表示完全回到指定的提交位置。
需要注意的是,git reset 还可以传入--soft参数,即软回退,回退到目标版本后,这之间的修改不会丢弃,还是会放到暂存区中。
// 在一个文件中添加1,产生A提交
> git commit -m "A"
// 在一个文件在移除刚刚添加的1,产生B提交
> git commit -m "B"
// 软回退到上一个提交
> git reset --soft head~1
// 通过git status查看到,移除1的修改还在暂存区中
> git status
// 再次软回退到上一个提交
> git reset --soft head~1
// 通过git status查看到,没有任何修改了,因为添加1和移除1在暂存区中抵消了
> git status
六、.gitignore文件
对应项目中的一些文件,我们可能不想被版本库追踪。我们可以在项目根目录下新建一个.gitignore文件,然后将不需要被版本库追踪的文件添加到.gitignore文件中。需要注意的是,.gitignore文件只能忽略还未被版本库追踪的文件。对于已经被版本库追踪的文件,如果也想忽略,怎么办呢?已经被版本库追踪的文件之所以不能被忽略,是因为该文件在暂存区中,所以如果我们把该文件从暂存区中删除不就可以了吗。
> git rm --cached <file>
> git add <file>
> git commit --message 'untrack some file'
不过,该方法有一个不好的地方就是从暂存区中删除后,需要提交一次。我们还可以通过 git update-index --assume-unchanged <file>
那么该文件就不会被版本库追踪了,也不需要将其加到.gitignore文件中。如果想恢复,那么可以执行git update-index --no-assume-unchanged <file>
七、stash命令
当我们在某些事情进行到中间的时候,突然发现需要先快速修复某个问题,但是我们又不想丢弃之前的工作,那么我们可以通过 git stash
命令将这些工作区和暂存区的修改都保存到本地。执行git stash
命令后,工作区和暂存区立即变干净了,此时可以着手一些更紧急的bug,等bug修复后,再之前的工作恢复出来,可以通过 git stash pop
将栈顶的stash恢复。也可以通过git stash list
查看所有的stash,然后恢复指定的stash,如:
> git stash
Saved working directory and index state WIP on master: 3f5aa1b add bar.txt
> git stash list
stash@{0}: WIP on master: 3f5aa1b add bar.txt
> git stash pop stash@{0}
git stash pop
命令恢复的时候,会将恢复的stash从list中删除。而git stash apply
也可以用于恢复stash,但是其不会将stash从list中删除。
恢复的时候,不仅可以在原来的分支上恢复,而是可以在任何一个分支上恢复,但是如果使用git stash pop
命令恢复的过程中,出现了冲突,那么也不会将stash从list中删除。但是我们可以手动删除,执行git stash drop <stash名称>
命令即可。
// 从 stash list 中删除stash@{0}
> git stash drop stash@{0}
需要注意的是,我们使用stash的时候存储的是已经被git版本库追踪的文件的修改,而那些未被git版本库追踪的文件,比如新建的文件,是不需要stash的,即使使用git stash命令也无法将新建的文件stash起来,也就是说,我们进行git操作的时候,不需要管那些未被git版本库追踪的文件,直接操作即可。
使用git stash pop恢复工作区的修改的时候:
如果当前工作区中有未提交的修改并且该内容与要恢复的stash中的内容有冲突,那么stash会失败。
如果工作区中未提交的修改与要恢复的stash中的内容没有冲突的话则可以stash成功。
如果工作区中没有未提交的修改,但是要恢复的stash中的内容恢复之后如果会产生冲突,那么可以stash成功,但是不会从stash list中删除。
八、revert命令
当我们发现前面的某一次提交存在着bug,那么我们可以用revert命令取消那次提交,并且在分支末尾自动追加一次提交记录。和reset不一样,reset是重置分支指针,如果分支指针被重置之后,那么之后的提交都会跟着丢弃,而revert则只会撤销那一次提交,后面的提交都保留。如果revert的那次提交修改了某个文件,同时该次之后的提交也修改了该文件,那么revert的时候就会存在冲突,git并不会仅仅撤销该次提交的修改内容,而是直接回到该次提交之前的内容,所以可能会丢弃之后的提交对该文件的修改,如:
// 假如master分支进行了三次提交,都是修改的同一个文件,第一次提交在第一行新增了一个1,第二次提交新增了一个2,第三次提交新增了一个3,现在要revert第二次提交
> git revert head~1
// 此时会出现冲突,因为第三次提交也对该文件进行了修改,而取消第二次提交仅仅只是回到第二次提交之前的内容,即文件中只有一个1,此时应该根据需要解决冲突,如果仅仅想要去掉2,那么可以先保留目前的修改(包括1、2、3),然后解决冲突去掉2,再添加到暂存区
> git add .
// 继续revert
> git revert --continue
// 此时会在分支head处生成一次提交记录
对于revert之后不存在冲突的,那么就会立即撤销当前修改并生成一次提交记录。
提交虽然被revert了,但那次修改还是存在的,只是给了一次机会取消那次提交所修改的内容,末尾追加了一次提交,如果通过reset重置分支指针到那次提交,还是可以恢复到那次提交对应的内容的。
现在我们revert的都是普通的提交,还有一种是合并时产生的提交,对于合并时候自动生成的那次提交,不能像普通提交那样revert,需要带上-m参数,后面根1或者2,1表示撤销非活跃分支上的那些修改,2表示撤销当前活跃分支上的那些修改,即当前分支上的修改。
// 如果master分支上进行了一次提交commit-1,然后在此基础上创建一个bug分支,然后在bug分支上进行了一次提交commit-2,再切换回master分支进行了一次提交commit-3,此时在master分支上合并bug分支的修改,自动创建了一个次合并提交181bb8,现在需要revert这次合并提交
> git revert -m 1 181bb8
// 此时bug分支上的修改commit-2将会重master分支上移除
需要注意的是,虽然revert取消了merge的那次提交,但是其实bug分支还是处于已经merge过的状态,此时再执行git merge bug
是无法再次将bug分支的修改合并回来的,因为已经合并过了,revert只是撤销了修改的内容而已,如果想要再次merge回来,那么可以把刚才revert合并提交时自动生成的那次提交也revert掉,这样就可以再次把bug分支上的修改merge回来了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。