git版本控制

查看提交历史 git log

如果想要查看git的提交历史,可以使用git log命令。本文会介绍一些常用的命令选项,比如--p--stat--pretty等。

默认的git log命令会显示所有提交历史:

$ git log
commit ccf42476e007db608813193659a5b4a0d3241df3
Author: wm <[email protected]>
Date:   Fri Dec 29 11:31:54 2017 +0800

    change .gitignore

commit da2b5d3db249617ec4b79eb843681600ed141cc9
Author: wm <[email protected]>
Date:   Fri Dec 29 11:07:36 2017 +0800

    add a.txt to .gitignore

commit eb691cca8f9a227d321924ffd094bcfae03d5314
Author: wm <[email protected]>
Date:   Fri Dec 29 11:05:52 2017 +0800

    rename README.txt -> README.md

commit 7c14f66174395dfc30ced57840ad5e9f108c9a0c
Author: wm <[email protected]>
Date:   Thu Dec 28 23:47:35 2017 +0800

    delete b.txt

# 省略

每个提交的格式都相似,单词commit(提交)后面跟的一大串(实际是40个)字符,就是这个提交的名字,是一个SHA-1序列,独一无二。接下来显示作者以及提交日期,最后显示提交的描述。

如果只想查看最近的几次提交记录,可以使用-(n)选项,比如-2选项只会显示最近的两次提交:

$ git log -2
commit ccf42476e007db608813193659a5b4a0d3241df3
Author: wm <[email protected]>
Date:   Fri Dec 29 11:31:54 2017 +0800

    change .gitignore

commit da2b5d3db249617ec4b79eb843681600ed141cc9
Author: wm <[email protected]>
Date:   Fri Dec 29 11:07:36 2017 +0800

    add a.txt to .gitignore

使用-p选项可以查看每次的更改:

$ git log -p -1
commit ccf42476e007db608813193659a5b4a0d3241df3
Author: wm <[email protected]>
Date:   Fri Dec 29 11:31:54 2017 +0800

    change .gitignore

diff --git a/.gitignore b/.gitignore
index eaa5fa8..e69de29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +0,0 @@
-a.txt

使用-stat选项可以查看每次的简略统计信息:

$ git log --stat -1
commit ccf42476e007db608813193659a5b4a0d3241df3
Author: wm <[email protected]>
Date:   Fri Dec 29 11:31:54 2017 +0800

    change .gitignore

 .gitignore | 1 -
 1 file changed, 1 deletion(-)

使用--pretty选项可以让信息以友好的方式显示,比如--pretty=oneline会让每条提交信息在一行显示,此外还有shortfullfuller可以用:

$ git log -3 --pretty=oneline
ccf42476e007db608813193659a5b4a0d3241df3 change .gitignore
da2b5d3db249617ec4b79eb843681600ed141cc9 add a.txt to .gitignore
eb691cca8f9a227d321924ffd094bcfae03d5314 rename README.txt -> README.md

--pretty=format可以自定义输出的内容:

$ git log -3 --pretty=format:"%h - %an, %ar : %s"
ccf4247 - wm, 30 hours ago : change .gitignore
da2b5d3 - wm, 31 hours ago : add a.txt to .gitignore
eb691cc - wm, 31 hours ago : rename README.txt -> README.md

上面的例子中哈希值名字是以简短的开头的几个字母来表示的。

--pretty=format常用的选项如下:

选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 --date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

git log的常用选项如下:

选项 说明
-p 按补丁格式显示每个更新之间的差异
--stat 显示每次更新的文件修改统计信息
--shortstat 只显示 --stat 中最后的行数修改添加移除统计
--name-only 仅在提交信息后显示已修改的文件清单
--name-status 显示新增、修改、删除的文件清单
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)
--graph 显示 ASCII 图形表示的分支合并历史
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)

除此之外,git log还有很多输出限制选项,比如--since--until就很常用,其中选项--since会显示某个时间点之后的提交历史,而--until会显示某个时间点之前的历史,下面是一个例子:

$ git log --since=2.days --pretty=format:'%h %cr : %s'
ccf4247 33 hours ago : change .gitignore
da2b5d3 34 hours ago : add a.txt to .gitignore
eb691cc 34 hours ago : rename README.txt -> README.md
7c14f66 2 days ago : delete b.txt
389faae 2 days ago : add b.txt
a2e55d5 2 days ago : delete a.txt
381fe8e 2 days ago : add a.txt
136fc21 2 days ago : git is covenient

上面的例子以--pretty=format指定的格式显示出了两天以内的提交历史。

git log常用的输出限制选项:

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交
--until, --before 仅显示指定时间之前的提交
--author 仅显示指定作者相关的提交
--committer 仅显示指定提交者相关的提交
--grep 仅显示含指定关键字的提交
-S 仅显示添加或移除了某个关键字的提交

取出 git checkout -- FILE

有的时候难免会写了一些错误的内容,如果想要将工作目录中的文件从暂存区或者git仓库恢复出来,需要使用git checkout命令。git checkout命令,顾名思义,取出的意思。这个命令可以将一个分支的内容取出来放到工作目录,也可将本分支暂存区或者git仓库中的内容取出来放到工作目录,因此它可以用来切换分支,也可以用来恢复文件,这里只讲第二个,也就是恢复文件的用法。

现在让我们在README.md中胡乱的加一句:

$ echo "asdfgh" >> README.md 
$ cat README.md 
Hi, Git!
Git is a free and open source distributed version control system.
Git is very easy to learn.
Git is very fast and convenient.
asdfgh
$ git status
On branch master
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

显然,现在git提示我们README.md文件有修改,但是我们发现文件只是多了一句毫无意义的话,现在我们想要恢复修改之前的版本,那么就需要使用git checkout -- FILE命令(当然可以直接删除最后一句话,这只是一个人为制造的例子):

$ git checkout -- README.md
$ cat README.md 
Hi, Git!
Git is a free and open source distributed version control system.
Git is very easy to learn.
Git is very fast and convenient.
$ git status
On branch master
nothing to commit, working directory clean

上面的例子中,在checkout后,README.md文件恢复成了在修改之前(上次提交时)的样子,并且工作目录是干净的。

git checkout -- FILE命令按下面的逻辑运行:

  • 如果该文件已经保存到暂存区,那么恢复到暂存区文件的状态。
  • 如果该文件还没有保存到暂存区,那么恢复到上次提交时的状态。

请在执行此命令时务必写上命令中的--

git reset的三个用法与git reflog

git reset,重置。常见的有三个用法,分别是--mixed--soft--hard

  • --mixed:默认命令选项,即不写命令选项时执行此命令选项。仅仅重置暂存区(index)至给定提交,不重置工作目录。
  • --soft:暂存区与工作目录都不会被重置,仅仅把HEAD指向给定提交。执行后效果是自给定提交以来所有的改变都是未提交的。
  • --hard:重置暂存区和工作目录,并且将HEAD指向给定提交。这个命令很危险,因为它会丢失自给定提交以后所有的更改。

下面我们更改README.md,并且将其提交到暂存区:

$ echo "asdfgh" >> README.md
$ git add README.md 
$ git status -s
M  README.md

我们现在README.md被添加了一行无用的内容,并且添加到了暂存区,为了重置暂存区,需要git reset HEAD README.md命令,效果是仅仅将暂存区的README.md重置至HEAD(即最近提交),不会重置工作目录:

$ git reset HEAD README.md
Unstaged changes after reset:
M   README.md
$ git status -s
 M README.md
$ cat README.md | tail -1
asdfgh

可见,暂存区被重置了,而工作目录并没有被重置。

假设我们没有发现这个问题,这个问题被提交了,有没有补救的方法呢?

$ git commit -a -m "add a wrong line asdfgh to README.md"
$ git status -s
$ cat README.md | tail -2
Git is very fast and convenient.
asdfgh
$ git log --pretty=oneline -3
af490893aba74d956c57583078ec0df343a06aae add a wrong line asdfgh to README.md
ccf42476e007db608813193659a5b4a0d3241df3 change .gitignore
da2b5d3db249617ec4b79eb843681600ed141cc9 add a.txt to .gitignore

糟糕,现在已经被提交了,怎么办?这就要用到--hard了,它就将工作目录、暂存区重置,并且将HEAD指向给定提交:

$ git reset --hard HEAD^
HEAD is now at ccf4247 change .gitignore
$ cat README.md | tail -2
Git is very easy to learn.
Git is very fast and convenient.
$ git status -s
$ git log --pretty=oneline -2
ccf42476e007db608813193659a5b4a0d3241df3 change .gitignore
da2b5d3db249617ec4b79eb843681600ed141cc9 add a.txt to .gitignore

HEAD在重置前指向最新的提交,HEAD^表示最新提交的前一次提交,HEAD^^表示最新提交的前一次提交的前一次提交……为了简便,HEAD~99表示表示最新提交的前99次提交。

所以上例中的git reset --hard HEAD^将工作目录和暂存区全部重置到前一次提交,并且将HEAD指向前一次提交,后面的命令结果显示确实是这样。

git reset --hard ccf4247可以达到同样的效果,它使用给定提交的哈希字符串代替HEAD^,不用把哈希字符串写全,但是最少要保证唯一性,我建议最少写7位。

可见,git reset --hard会将给定提交之后的所有修改丢弃,所以请谨慎使用。

但是现在我们发现最后那句无厘头的话好像还挺有意思,能不能再找回来呢?(你不觉得有意思是正常的,但是书总得往下写)

我们发现git log里的记录已经没有了,怎么办呢?

git reflog记录了所有的索引变化,所以使用该命令可以查到索引变化的日志,便可以从中找到给定提交的哈希字符串了!

$ git reflog
ccf4247 HEAD@{2}: reset: moving to HEAD^
af49089 HEAD@{3}: commit: add a wrong line asdfgh to README.md

从上面看到,添加那行无厘头句子的哈希字符串是af49089...,所以使用git reset --hard af49089就可以啦!

$ git reset --hard af49089
HEAD is now at af49089 add a wrong line asdfgh to README.md
$ cat README.md | tail -2
Git is very fast and convenient.
asdfgh
$ git log --pretty=oneline -2
af490893aba74d956c57583078ec0df343a06aae add a wrong line asdfgh to README.md
ccf42476e007db608813193659a5b4a0d3241df3 change .gitignore
$ git status -s
$ 

现在都找回来啦哈哈!

最后,来说说--soft选项,它不会重置工作目录与暂存区,只会将HEAD指向给定提交。效果是自给定提交以来所有修改都是未提交状态。

现在我们将再向README.md中添加两句话,并且分别提交:

$ echo "qwerty" >> README.md
$ git commit -a -m "add qwerty to README.md"
[master 721f8ff] add qwerty to README.md
 1 file changed, 1 insertion(+)
$ echo "zxcvbn" >> README.md
$ git commit -a -m "add zxcvbn to README.md"
[master 64e53ee] add zxcvbn to README.md
 1 file changed, 1 insertion(+)
$ git status -s
$
$ git log --pretty=oneline -3
64e53eeb47738ffd81ff7770758b774f60080b46 add zxcvbn to README.md
721f8ffb54bd93e7d75aed6ff9bf93e2f9eb8ca1 add qwerty to README.md
af490893aba74d956c57583078ec0df343a06aae add a wrong line asdfgh to README.md

好的,现在我们确定这两个提交都是正确无误的,希望它们能“合并”成一个提交,怎么办?

git reset --soft将帮助我们解决这个问题,它不会修改工作目录和暂存区,只修改HEAD的指向。

$ git reset --soft af49089
$ git status -s
M  README.md
$ cat README.md | tail -2
qwerty
zxcvbn

上面的例子中,新添加的两行都成了未提交的内容。现在提交就可将他们合并成一个提交:

$ git commit -a -m "add two wrong line to README.md"
[master 14bd627] add two wrong line to README.md
 1 file changed, 2 insertions(+)
$ git log --pretty=oneline -2
14bd6272cde43128877f191364361a7194ba6e6c add two wrong line to README.md
af490893aba74d956c57583078ec0df343a06aae add a wrong line asdfgh to README.md

你可能感兴趣的:(git版本控制)