跳转至

6.Git日志和历史

你在你的仓库里一直很忙,添加文件,做修改,撤销修改,做智能的提交,并有清晰的信息。但随着时间的推移,你越来越难记住你做了什么,什么时候做的。

当你把你的项目搞得一团糟时(不是如果,而是时),你会希望能够回溯历史,找到一个有效的提交,并把你的项目倒回那个时间点。本章告诉你如何做。

查看Git历史

Git会记录你在仓库中做的几乎所有事情。在前几章中,当你使用git log命令时,你已经看到了它的作用。

然而,你有很多方法可以查看git log提供的数据,这些数据可以告诉你一些关于你的仓库和历史的令人难以置信的有趣的事情。事实上,你甚至可以用git log来为你的版本库创建一个图形,以便更好地了解正在发生的事情。

Vanilla git log

打开你的终端程序,执行git log,看看你已经习惯了的基本的、香草味的版本库历史:

commit 477e542bfa35942ddf069d85fbe3fb0923cfab47 (HEAD -> main)
Author: Chris Belanger <chris@razeware.com>
Date:   Wed Jan 23 16:49:56 2019 -0400

    Adding .gitignore files and HTML

commit ffcedc2397503831938894edffda5c5795c387ff
Author: Chris Belanger <chris@razeware.com>
Date:   Tue Jan 22 20:26:30 2019 -0400

    Adds all the good ideas about management

commit 84094274a447e76eb8f55def2c38b909ef94fa42
Author: Chris Belanger <chris@razeware.com>
Date:   Tue Jan 22 20:17:03 2019 -0400

    Removes terrible live streaming ideas

commit 67fd0aa99b5afc18b7c6cc9b4300a07e9fc88418
Author: Chris Belanger <chris@razeware.com>
Date:   Tue Jan 22 19:47:23 2019 -0400

    Moves platform ideas to website directory

这将显示ancestral commits的列表--也就是构成当前head的历史提交的集合。在这种情况下,这就是你的版本库的main分支中的最新提交。按Q退出这个视图。

基本的git log命令显示了这个分支的所有祖先的提交。如果你只想看几个--比如说,三个?

限制结果

这很简单,只要执行下面的命令就可以显示你想看的提交数量,从最近的开始:

git log -3

然后,Git将只显示最近的三个提交。你可以替换上面例子中的3,以显示你喜欢的任何数量的提交。

这样就比较容易管理了,但还是有很多细节。如果有一种方法可以查看提交信息,并过滤掉所有其他的额外信息,那不是很好吗?

有的!有的 执行下面的命令可以看到一个更紧凑的版本库历史视图:

git log --oneline

你会看到一个快速、紧凑的提交历史视图,可以说比git log的原始输出更易读:

~/GitApprentice/ideas $ git log --oneline
477e542 (HEAD -> main) Adding .gitignore files and HTML
ffcedc2 Adds all the good ideas about management
8409427 Removes terrible live streaming ideas
67fd0aa Moves platform ideas to website directory
0ddfac2 Updates book ideas for Symbian and MOS 6510
6c88142 Adding some tutorial ideas
.
.
.

这也显示了一个提交的short hash。虽然你还没有深入研究过哈希值,但每个提交都有长哈希值和短哈希值,它们在版本库中唯一地识别一个提交。

例如,如果我用git log -1看一下我的版本库中最近的提交的第一行(那是数字"1",不是字母 "l"),我看到如下:

commit 477e542bfa35942ddf069d85fbe3fb0923cfab47 (HEAD -> main)

现在,为了比较,我用git log -1 --oneline(是的,你可以用git log堆叠多个选项)查看同样的单一提交,我得到如下结果:

477e542 (HEAD -> main) Adding .gitignore files and HTML

短哈希值就是长哈希值的前七个字符;在本例中是477e542。对于一般规模的开发项目来说,7个十六进制数字可以提供超过25亿的短哈希值,所以不同提交之间哈希值碰撞的可能性相当小。

当你增加到大规模的Git仓库,并持续数年甚至数十年时,两个提交的哈希值相同的可能性就成为现实了。

旧版本的Git允许你配置仓库使用的哈希字符数,但最近的Git版本(大约从2017年开始)会动态调整这一设置,以适应项目的规模,所以你通常不必担心这个问题。

Note

你是否想知道为什么有些命令的选项前面是单破折号,而其他选项前面是双破折号? 这可以追溯到基于命令行的操作系统的历史中。一般来说,带有双破折号的命令是命令的"长形式",是为了使其清晰。 例如,你以前用过的git log -p命令,显示了你提交的文件的差异。但是还有一条命令,它的区别仅仅在于选项是大写的:git log -P,它做的事情完全不同。 由于所有这些命令可能会有点混乱,特别是在大小写重要的情况下,许多现代命令行工具提供了长形式的命令替代品,以便更清楚地了解特定选项的意图。 在上面的例子中,你可以交替使用git log --patchgit log -p,因为它们的意思完全相同。--patch选项更清楚,但-p更紧凑。

储存库的图形化视图

那么,git log还能做什么?嗯,Git有一些简单的方法来显示你的仓库的分支历史。执行下面的命令可以看到你的仓库历史的"树状"结构的一个相当详细的视图:

git log --graph

按空格键翻阅一些结果(或用方向键滚动),你会看到我在早期版本的仓库中合并了一个分支:

.
.
.
 commit fbc46d3d828fa57ef627742cf23e865689bf01a0
| Author: Chris Belanger <chris@razeware.com>
| Date:   Thu Jan 10 10:18:14 2019 -0400
|
|     Adding files for article ideas
|
*   commit 5fcdc0e77adc11e0b2beca341666e89611a48a4a
|\  Merge: 39c26dd cfbbca3
| | Author: Chris Belanger <chris@razeware.com>
| | Date:   Thu Jan 10 10:14:56 2019 -0400
| |
| |     Merge branch 'video_team'
| |
| * commit cfbbca371f4ecc80796a6c3fc0c084ebe181edf0
| | Author: Chris Belanger <chris@razeware.com>
| | Date:   Thu Jan 10 10:06:25 2019 -0400
| |
| |     Removing brain download as per ethics committee
.
.
.

如果你再往下翻一点,你会看到我在master上创建分支的地方:

* | commit 39c26dd9749eb627056b938313df250b669c1e4c
| | Author: Chris Belanger <chris@razeware.com>
| | Date:   Thu Jan 10 10:13:32 2019 -0400
| |
| |     I should write a book on git someday
| |
* | commit 43b4998d7bf0a6d7f779dd2c0fa4fe17aa3d2453
|/  Author: Chris Belanger <chris@razeware.com>
|   Date:   Thu Jan 10 10:12:36 2019 -0400
|
|       Adding book ideas file
|
* commit becd762cea13859ac32841b6024dd4178a706abe
| Author: Chris Belanger <chris@razeware.com>
| Date:   Thu Jan 10 09:49:23 2019 -0400
|
|     Creating the directory structure
|
* commit 73938223caa4ad5c3920a4db72920d5eda6ff6e1
  Author: crispy8888 <chris@razeware.com>
  Date:   Wed Jan 9 20:59:40 2019 -0400

      Initial commit

但这还是太多信息了。你怎么能把这个树状视图折叠起来,只看提交信息,但仍能看到分支历史?这就对了--通过堆叠git log的选项。

Q键退出,然后执行下面的命令,就可以看到一个更浓缩的视图:

git log --oneline --graph

你会看到一个漂亮的、紧凑的历史和分支结构的视图:

~/GitApprentice/ideas $ git log --oneline --graph
* 477e542 (HEAD -> main) Adding .gitignore files and HTML
* ffcedc2 Adds all the good ideas about management
* 8409427 Removes terrible live streaming ideas
* 67fd0aa Moves platform ideas to website directory
* 0ddfac2 Updates book ideas for Symbian and MOS 6510
* 6c88142 Adding some tutorial ideas
* ce6971f Adding empty tutorials directory
* 57f31b3 Added new book entry and marked Git book complete
* f65a790 (origin/main, origin/HEAD) Updated README.md to reflect current working book title.
* c470849 (origin/master) Going to try this livestreaming thing
* 629cc4d Some scratch ideas for the iOS team
* fbc46d3 Adding files for article ideas
*   5fcdc0e Merge branch 'video_team'
|\
| * cfbbca3 Removing brain download as per ethics committee
| * c596774 Adding some video platform ideas
| * 06f468e Adding content ideas for videos
* | 39c26dd I should write a book on git someday
* | 43b4998 Adding book ideas file
|/
* becd762 Creating the directory structure
* 7393822 Initial commit

查看非传统的历史

不过,Git并没有向你展示完整的历史。它只显示在main分支上发生的事情的历史。要让Git显示它所知道的所有事情的完整历史,请在前面的命令中加入--all选项:

git log --oneline --graph --all

你会看到,在master上有一个origin/clickbait分支,而Git之前没有告诉你:

* 477e542 (HEAD -> main) Adding .gitignore files and HTML
* ffcedc2 Adds all the good ideas about management
* 8409427 Removes terrible live streaming ideas
* 67fd0aa Moves platform ideas to website directory
* 0ddfac2 Updates book ideas for Symbian and MOS 6510
* 6c88142 Adding some tutorial ideas
* ce6971f Adding empty tutorials directory
* 57f31b3 Added new book entry and marked Git book complete
* f65a790 (origin/main, origin/HEAD) Updated README.md to reflect current working book title.
* c470849 (origin/master) Going to try this livestreaming thing
* 629cc4d Some scratch ideas for the iOS team
| * e69a76a (origin/clickbait) Adding suggestions from Mic
| * 5096c54 Adding first batch of clickbait ideas
|/
* fbc46d3 Adding files for article ideas
*   5fcdc0e Merge branch 'video_team'
|\
| * cfbbca3 Removing brain download as per ethics committee
| * c596774 Adding some video platform ideas
| * 06f468e Adding content ideas for videos
* | 39c26dd I should write a book on git someday
* | 43b4998 Adding book ideas file
|/
* becd762 Creating the directory structure
* 7393822 Initial commit

使用Git shortlog

Gitgit log提供了一个非常方便的伙伴,那就是git shortlog。这是一个获得提交摘要的好方法,也许可以用在你的应用程序的发布说明中。有时"错误修复和性能改进"还不够详细,你知道吗?

执行下面的命令,看看谁对这个仓库做了提交:

git shortlog

我看到这个版本库的提交集合如下:

Chris Belanger (18):
      Creating the directory structure
      Adding content ideas for videos
      Adding some video platform ideas
      Removing brain download as per ethics committee
      Adding book ideas file
      I should write a book on git someday
      Merge branch 'video_team'
      Adding files for article ideas
      Some scratch ideas for the iOS team
      Going to try this livestreaming thing
      Added new book entry and marked Git book complete
      Adding empty tutorials directory
      Adding some tutorial ideas
      Updates book ideas for Symbian and MOS 6510
      Moves platform ideas to website directory
      Removes terrible live streaming ideas
      Adds all the good ideas about management
      Adding .gitignore files and HTML

crispy8888 (1):
      Initial commit
.
.
.

我可以看到我有18个提交到这个仓库 - 然后是这个crispy8888的家伙,他创建了最初的仓库。嗯,他真好。这里面可能还有其他用户的修改,包括你自己。

你会注意到,与标准的git log命令不同,git shortlog是按时间顺序排列提交的。从总结的角度看,这比以反向时间顺序显示所有内容更有意义。

到目前为止,你已经看到了如何使用git loggit shortlog来给你一个高层次的版本库历史视图,以及你想要的细节。但有时你想看看版本库中的某个特定动作。你知道你想搜索什么,但你真的需要滚动所有的输出来检索你要找的东西吗?

Git提供了一些很好的搜索功能,你可以用它来寻找某个特定文件的信息,甚至是许多文件的特定变化。

搜索Git历史

想象一下,你只想看看这个叫crispy8888的家伙在仓库里的提交。Git给你提供了过滤git log输出到特定作者的能力。

执行下面的命令:

git log --author=crispy8888 --oneline

Git向你展示了这个家伙所做的一个改变:

7393822 Initial commit

如果你想搜索一个由两个或多个部分组成的名称,只需将该名称用引号括起来:

git log --author="Chris Belanger" --oneline

你也可以搜索版本库的提交信息,与谁做出的修改无关。

执行下面的命令,找到提交信息中含有"idea"一词的提交:

git log --grep=ideas --oneline

你应该看到与下面类似的东西:

ffcedc2 Adds all the good ideas about management
8409427 Removes terrible live streaming ideas
67fd0aa Moves platform ideas to website directory
0ddfac2 Updates book ideas for Symbian and MOS 6510
6c88142 Adding some tutorial ideas
629cc4d Some scratch ideas for the iOS team
fbc46d3 Adding files for article ideas
43b4998 Adding book ideas file
c596774 Adding some video platform ideas
06f468e Adding content ideas for videos

Note

想知道grep是什么意思?grep是指一个命令行工具,代表"全局搜索正则表达式和打印"。grep是一个非常有用和强大的命令行工具,grep已经被公认为是一个动词,意味着"搜索",特别是与正则表达式结合使用。

如果你只对一个文件感兴趣呢?这在Git中很容易做到。

执行下面的命令可以看到books/book_ideas.md的所有完整提交信息:

git log --oneline books/book_ideas.md

你会看到只有该文件的所有提交:

57f31b3 Added new book entry and marked Git book complete
39c26dd I should write a book on git someday
43b4998 Adding book ideas file

你还可以看到发生在某一特定目录下的文件的提交情况:

git log --oneline books

这显示了该目录中发生的所有变化,但并不清楚哪些文件被改变了。

为了更清楚地了解该目录中哪些文件被改变了,你可以在该命令的顶部添加--stat选项:

git log --oneline --stat books

这将向你显示关于这个目录中的变化的以下细节,以便你可以看到什么被改变了,甚至可以瞥见被改变的程度:

ffcedc2 Adds all the good ideas about management
 books/management_book_ideas.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
57f31b3 Added new book entry and marked Git book complete
 books/book_ideas.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
39c26dd I should write a book on git someday
 books/book_ideas.md | 1 +
 1 file changed, 1 insertion(+)
43b4998 Adding book ideas file
 books/book_ideas.md | 9 +++++++++
 1 file changed, 9 insertions(+)
becd762 Creating the directory structure
 books/.keep | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

你也可以搜索提交本身的实际内容,也就是提交的变更集。这可以让你在提交的内容中寻找感兴趣的特定词汇,甚至是整个代码片段。

用下面的命令找到你的代码中所有涉及"Fortran"一词的提交内容:

git log -S"Fortran"

你会看到以下内容:

commit 43b4998d7bf0a6d7f779dd2c0fa4fe17aa3d2453
Author: Chris Belanger <chris@razeware.com>
Date:   Thu Jan 10 10:12:36 2019 -0400

    Adding book ideas file

只有一次提交,即最初添加图书创意文件的地方。但是,这还是不够详细。你能想起哪个选项可以用来显示提交中的实际变化吗?

没错,就是-p选项。执行上面的命令,但这次要在最后加上-p选项:

git log -S"Fortran" -p

你现在会看到更多的细节:

commit 43b4998d7bf0a6d7f779dd2c0fa4fe17aa3d2453
Author: Chris Belanger <chris@razeware.com>
Date:   Thu Jan 10 10:12:36 2019 -0400

    Adding book ideas file

diff --git a/books/book_ideas.md b/books/book_ideas.md
new file mode 100644
index 0000000..f924368
--- /dev/null
+++ b/books/book_ideas.md
@@ -0,0 +1,9 @@
+# Ideas for new book projects
+
+- [ ] Hotubbing by tutorials
+- [x] Advanced debugging and reverse engineering
+- [ ] Animal husbandry by tutorials
+- [ ] Beginning tree surgery
+- [ ] CVS by tutorials
+- [ ] Fortran for fun and profit
+- [x] RxSwift by tutorials

这下好了! 现在你可以看到该提交的内容,即Git发现了"Fortran"这个术语。

在本章中你已经学到了不少关于git log的知识,可能比一般的Git用户知道的还要多。随着你在工作流程中越来越多地使用Git,随着你的项目历史从几个月增长到几年,你会发现git log最终会成为你最好的朋友,而且比你的大脑更善于回忆事情。

挑战

说到大脑,你为什么不锻炼一下你的大脑,并通过接受本章的四个挑战来巩固你在本章中学到的技能呢?

挑战1:显示将项目标记为"完成"的提交的所有细节

在这个挑战中,你需要找到所有将项目标记为"已完成"的提交;也就是说,在括号内有一个"x"的提交,像这样:

[x]

你需要搜索上述字符串,你需要使用一个选项,不仅要显示基本的提交细节,还要显示提交的变化集内容。

挑战2:找到所有提到"流"的提交信息

你要在提交的messages中寻找你或其他人在提交消息本身中使用"streaming"一词的地方,而不一定是提交的内容。提示。你在本章前面学到的那个名字很奇怪的命令是什么?

挑战3:获取视频目录的详细历史记录

在这个挑战中,你需要显示在videos目录内发生的一切,就Git而言。但是,再一次,关于提交的基本信息是不够的。你还需要显示关于该差异的全部细节。所以你要在命令的末尾加上一个熟悉的选项......或者你可以?

挑战4:查找包含"iOS 13"的所有提交的详细信息

在这最后一个挑战中,你需要找到其差异包含"iOS 13"一词的提交。这听起来与上面的挑战1相似,但如果你试图使用与该挑战相同的命令,你将找不到任何结果。但相信我,至少有一个结果在那里。提示。你是否记得搜索资料库的"全部"?

关键点

  • git log本身显示的是当前HEAD的祖先提交的基本的、普通的视图。
  • git log -p显示一个提交的差异。
  • git log -``n显示最近n次的提交。
  • git log --oneline显示简短的哈希值和提交信息的简洁视图。
  • 你可以在git log上叠加选项,如git log -8 --oneline,以浓缩的形式显示最近的8个提交。
  • git log --graph显示你的版本库的粗略但可行的图形表示。
  • git log --all显示版本库中其他分支的提交,而不仅仅是当前HEAD的祖先。
  • git shortlog显示提交的摘要,按作者分组,以时间顺序递增。
  • git log --author="<authorname>"让你搜索某个特定作者的提交。
  • git log --grep="<term>"让你搜索某个特定术语的提交信息。
  • git log <path/to/filename>将只显示与该文件相关的提交。
  • git log <directory>将显示某个特定目录下的文件的提交。
  • git log -stat显示了每个提交的范围和规模的概况。
  • git log -S"<term>"可以让你在提交的变更集中搜索某个特定术语的内容。

接下来去哪?

你已经学习了大量关于Git如何工作的知识,提交如何工作,暂存区如何工作,如何撤销你无意做的事情,如何忽略文件,以及如何利用git log的力量来解开仓库的秘密。

但有一件事你还没有真正接触到,那就是Git之所以如此优雅和有用的原因:它强大的分支模型。

事实上,Git的分支机制是它区别于其他大多数版本控制系统的原因,因为它与大多数开发者进行项目的方式配合得非常好。

在下一章中,你将学习main的真正含义,如何创建分支,Git如何"思考"你的仓库中的分支,本地和远程仓库的区别,如何切换分支,如何删除分支等等。