预备
在进行git的各种操作之前,一定要先明白git的三个分区是什么,否则你无法理解git的原理。
本地 Git 的三个分区分别是:working directory,stage/index area,commit history
。
working directory
是[工作目录],也就是我们肉眼能看到的文件- 当我们在
working directory
中执行git add
相关命令后,就会把working directory
中的修改添加到[暂存区]stage area
- 当
stage area
中存在修改时,我们使用git commit
相关命令之后,就会把stage area
中的修改添加到[提交历史]commit history
中,也就是HEAD
指针指向的位置
- 任何修改只要进入
commit history
,基本上可以认为永远不会丢失了。每一个commit
都有一个唯一的Hash
值,我们经常说的HEAD
或者master
分支,都可以理解为一个指向某个commit
的指针 commit history
和stage area
区域的状态,可以通过git status
来查看,commit history
区域的提交历史,可以通过git log
来查看
本地 Git 极简教程
需求一,如何把work dir中的修改加入stage
使用git add
相关的命令就行了。顺便一提,add有个别名叫做stage,也就是说你可能见到git stage
相关的命令,这个命令和git add
命令是完全一样的。
风险等级:无风险
理由:不会改变任或撤销任何已作出的修改,而且还会将work dir中未追踪的修改(Untracked file)添加到暂存区stage中进行追踪
需求二,如何把stage中的修改还原到work dir中
这个需求很常见,也很重要,比如我先将当前work dir
中的修改添加到stage
中,然后又对work dir
中的文件进行了修改,但是又后悔了,如何把work dir
中的全部或部分文件还原成stage中的样子呢?
来个实际场景,我先新建两个文件,然后把他们都加到stage:
$ touch a.txt b.txt
$ git add .
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
然后我又修改了a.txt文件:
$ echo hello world >> a.txt
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
Changes not staged for commit:
modified: a.txt
现在,我后悔了,我认为不应该修改a.txt,我想把它还原成stage中的空文件,怎么办?
答案是,使用 checkout 命令:
$ git checkout a.txt
Updated 1 path from the index
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
输出显示从index区(也就是stage区)更新了一个文件,也就是把work dir中a.txt文件还原成了stage中的状态(一个空文件)。
当然,如果work dir中被修改的文件很多,可以使用通配符全部恢复成stage:
$ git checkout .
有一点需要指出的是,checkout命令只会把被「修改」的文件恢复成stage的状态,如果work dir中新增了新文件,你使用git checkout .是不会删除新文件的。
风险等级:中风险。
理由:在work dir做出的「修改」会被stage覆盖,无法恢复。所以使用该命令你应该确定work dir中的修改可以抛弃。
需求三,将stage区的文件添加到history区。
就是 git commit 相关的命令,一般我们就是这样用的:
$ git commit -m '一些描述'
再简单提一些常见场景, 比如说commit完之后,突然发现一些错别字需要修改,又不想为改几个错别字而新开一个commit到history区,那么就可以使用下面这个命令:
$ git commit --amend
这样就是把错别字的修改和之前的那个commit中的修改合并,作为一个commit提交到history区。
风险等级:无风险。
理由:不会改变任或撤销任何已作出的修改,而且还会将stage区的修改加入history区并分配一个 Hash 值。只要不乱动本地的.git文件夹,进入history的修改就永远不会丢失。
需求四,将history区的文件还原到stage区。
这个需求很常见,比如说我用了一个git add .一股脑把所有修改加入stage,但是突然想起来文件a.txt中的代码我还没写完,不应该把它commit到history区,所以我得把它从stage中撤销,等后面我写完了再提交。
$ echo aaa >> a.txt; echo bbb >> b.txt;
$ git add .
$ git status
On branch master
Changes to be committed:
modified: a.txt
modified: b.txt
如何把a.txt从stage区还原出来呢?可以使用 git reset 命令:
$ git reset a.txt
$ git status
On branch master
Changes to be committed:
modified: b.txt
Changes not staged for commit:
modified: a.txt
你看,这样就可以把a.txt文件从stage区移出,这时候进行git commit相关的操作就不会把这个文件一起提交到history区了。
上面的这个命令是一个简写,实际上reset命令的完整写法如下:
$ git reset --mixed HEAD a.txt
其中,mixed是一个模式(mode)参数,如果reset省略这个选项的话默认是mixed模式;HEAD指定了一个历史提交的 hash 值;a.txt指定了一个或者多个文件。
该命令的自然语言描述是:不改变work dir中的任何数据,将stage区域中的a.txt文件还原成HEAD指向的commit history中的样子。就相当于把对a.txt的修改从stage区撤销,但依然保存在work dir中,变为unstage的状态。
风险等级:低风险。
理由:不会改变work dir中的数据,会改变stage区的数据,所以应确保stage中被改动数据是可以抛弃的。
需求五,将work dir的修改提交到history区。
先git add然后git commit
就行了,或者一个快捷方法是使用命令git commit -a
。
风险等级:无风险。
需求六,将history区的历史提交还原到work dir中。
比如,比如我从 GitHub 上clone了一个项目,然后乱改了一通代码,结果发现我写的代码根本跑不通,于是后悔了,干脆不改了,我想恢复成最初的模样,怎么办?
依然是使用checkout
命令,但是和之前的使用方式有一些不同:
$ git checkout HEAD .
Updated 12 paths from d480c4f
这样,work dir和stage中所有的「修改」都会被撤销,恢复成HEAD指向的那个history commit
。
注意,类似之前通过stage恢复work dir的checkout命令,这里撤销的也只是修改,新增的文件不会被撤销。
当然,只要找到任意一个commit的 HASH 值,checkout命令可就以将文件恢复成任一个history commit中的样子:
$ git checkout 2bdf04a some_test.go
Updated 1 path from 2bdf04a
# 前文的用法显示 update from index
比如,我改了某个测试文件,结果发现测试跑不过了,所以就把该文件恢复到了它能跑过的那个历史版本……
风险等级:高风险。
理由:这个操作会将指定文件在work dir的数据恢复成指定commit的样子,且会删除该文件在stage中的数据,都无法恢复,所以应该慎重使用。
其他技巧
需求一,合并多个commit。
比如说我本地从17bd20c
到HEAD有多个commit
,但我希望把他们合并成一个commit
推到远程仓库,这时候就可以使用reset
命令:
$ git reset 17bd20c
$ git add .
$ git commit -m 'balabala'
reset
相当于把HEAD
移到了17bd20c这个commit
,而且不会修改work dir
中的数据,所以只要add
再commit
,就相当于把中间的多个commit
合并到一个了。
需求二,由于HEAD指针的回退,导致有的commit在git log命令中无法看到,怎么得到它们的 Hash 值呢?
再重复一遍,只要你不乱动本地的.git文件夹,任何修改只要提交到commit history中,都永远不会丢失,看不到某些commit只是因为它们不是我们当前HEAD位置的「历史」提交,我们可以使用如下命令查看操作记录:
$ git reflog
比如reset,checkout等等关键操作都会在这里留下记录,所有commit的 Hash 值都能在这里找到,所以如果你发现有哪个commit突然找不到了,一定都可以在这里找到。
需求三,怎么解决冲突?
记住,Git 虽然高大上,但也不要迷恋,一定要懂得借助先进的工具。
比较流行的代码编辑器或者 IDE 都会集成方便的可视化 Git 工具,至于解决冲突,可视化的表现方式不是比你在命令行里git diff看半天要清晰明了得多?只需要点点点就行了。
所以说,只要明白本文讲的这些基本操作,够你用的了,平时能用图形化工具就多用图形化工具,毕竟工具都是为人服务的。