本文讲解使用 git 等工具查看提交历史的几种方式。
git log
在我理解中,实际上后面讲的各种方法都是基于 git log
命令行工具,且对于程序员而言,命令行才是正统;
默认不用任何参数的话,git log
会按提交时间列出所有的更新,最近的更新排在最上面。每次更新都有一个 SHA-1
校验和、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交说明。git log
命令行会默认打开一个 vim 编辑器,输入 :q
退出查看。
常用选项
-p
选项展开显示每次提交的内容差异;
-2
则仅显示最近的两次更新;
--word-diff
选项。可以将其添加到 git log -p 命令的后面,从而获取单词层面上的对比。在书籍、论文这种很大的文本文件上进行对比的时候;
--stat
,仅显示简要的增改行数统计;
作者(author)和提交者(committer)的差别,作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。
示例
查看 Git 仓库中,2018 年 9 月期间,Junio Hamano 提交的但未合并的测试脚本(位于项目的 t/ 目录下的文件),命令:
git log --pretty="%h - %s" --author=gitster --since="2018-09-01" --before="2018-09-30" --no-merges -- t/
git log -p -2
选项除了显示基本信息之外,还在附带每次 commit 的变化。常用于代码审查,或者快速浏览某个搭档提交的 commit 的变化。
git log -U1 --word-diff
这里并没有平常看到的添加行或者删除行的信息。这里的对比显示在行间。新增加的单词被 {+ +} 括起来,被删除的单词被 [- -] 括起来。在进行单词层面的对比的时候,你可能希望上下文( context )行数从默认的 3 行,减为 1 行,那么可以使用 -U1 选项。
git log --stat
每个提交都列出修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计。 还有个常用的 --pretty 选项,可以指定使用完全不同于默认格式的方式展示提交历史。比如用 oneline 将每个提交放在一行显示,这在提交数很大时非常有用。另外还有 short,full 和 fuller 可以用,展示的信息或多或少有些不同。
用 oneline 或 format 时结合 --graph
选项,可以看到开头多出一些 ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。在我们之前提到的 Grit 项目仓库中可以看到:
git log --pretty=format:"%h %s" --graph
git log --pretty=oneline
format,可以定制要显示的记录格式,这样的输出便于后期编程提取分析:
git log --pretty=format:"%h - %an, %ar : %s"
列出常用的格式占位符写法及其代表的意义。
选项 | 说明 |
---|---|
%H | 提交对象(commit)的完整哈希字串 |
%h | 提交对象的简短哈希字串 |
%T | 树对象(tree)的完整哈希字串 |
%t | 树对象的简短哈希字串 |
%P | 父对象(parent)的完整哈希字串 |
%p | 父对象的简短哈希字串 |
%an | 作者(author)的名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者修订日期(可以用 -date= 选项定制格式) |
%ar | 作者修订日期,按多久以前的方式显示 |
%cn | 提交者(committer)的名字 |
%ce | 提交者的电子邮件地址 |
%cd | 提交日期 |
%cr | 提交日期,按多久以前的方式显示 |
%s | 提交说明 |
列出所有最近两周内的提交: git log --since=2.weeks
or git log --since=2weeks
还可以给出若干搜索条件,列出符合的提交。用 --author 选项显示指定作者的提交,用 --grep 选项搜索提交说明中的关键字。(如果要得到同时满足这两个选项搜索条件的提交,就必须用 --all-match 选项。否则满足任意一个条件的提交都会被匹配出来)
git log选项是路径(path),如果只关心某些文件或者目录的历史提交,可在 git log 选项的最后指定路径。因为是放在最后位置上的选项,所以用两个短划线(–)隔开之前的选项和后面限定的路径名。
汇总表格如下
选项 | 说明 |
---|---|
-p | 按补丁格式显示每个更新之间的差异。 |
--word-diff | 按 word diff 格式显示差异。 |
--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(后跟指定格式)。 |
--oneline | --pretty=oneline --abbrev-commit 的简化用法。 |
-(n) | 仅显示最近的 n 条提交;不常用,Git 在输出所有提交时会自动调用分页程序(less),要看更早的更新只需翻到下页即可。 |
--since, --after | 仅显示指定时间之后的提交。 |
--until, --before | 仅显示指定时间之前的提交。 |
--author | 仅显示指定作者相关的提交。 |
--committer | 仅显示指定提交者相关的提交。 |
gitk
安装 Git 之后就自带 gitk 图形化工具,用 Tcl/Tk 写成,相当于 git log 命令的可视化版本,凡是 git log 可以用的选项,都能用在 gitk 上。在 git 代码库下输入 gitk 命令即可打开图形化工具。
Tortoise Git
一般使用Tortoise的都是SVN老用户,我也是。右键有很多选项。已不再使用。
IDEA-version control-log
自从使用 IDEA 之后,基本上也不会再去定位到本地的代码仓库,然后右键打开一个命令行窗口,输入命令 git log 或者 gitk,而是就地直接使用 IDEA 的 version control——log 功能,IDEA 默认也是可以看到 graph 形式的 git log,而且也支持各种条件搜索,发布简洁地看到提交历史。
比如最前面的编辑框可以输入查询条件,随后的 regex 或者 match case 表示查询条件的匹配模式;
随后的 branch 可以选择哪个 branch 的提交历史记录;
User和Date,见名知义。
Paths
一般情况下,path派不上用场。因为在使用IDEA时,一个Window打开一个Git项目,如果打开多个项目,即有多个Git地址时,path可以对Git项目进行选择,否则全部项目的Git Tree一起看,会乱得一批。
source tree
试用过 source tree,比 Tortoise Git 更专业,毕竟是收费软件,把版本控制管理做到极致,破解方法参考版本控制工具 Git 使用笔记。一般情况下,只要你熟悉 Git 的原理之后,就不需要所谓的图形化工具,也就不需要 source tree 这样的专业工具;不过对于有些版本控制很严格的项目,比如实施 Git Flow 开发模式的项目,branch 或者 tag 就会比较复杂,此时 source tree 就会派上用场。
附图 Git Flow
IDEA 甚至提供 Git Flow 插件
Git Flow 参考:
参考1
参考2
git reflog
reference log,查看引用日志。参考git reflog
里面提到:
记录的是本地仓库的所有 git 操作,比如
git pull
都会记录下来。原文:record when the tips of branches and other references were updated in the local repository.
引用日志信息只存在于本地——这是一个记录你在你自己的仓库里做过什么的日志。其他人拷贝的仓库里的引用日志不会和你的相同;而你新克隆一个仓库的时候,引用日志是空的,因为你在仓库里还没有操作。git show HEAD@{2.months.ago}
这条命令只有在你克隆一个项目至少两个月时才会有用——如果你是五分钟前克隆的仓库,那么它将不会有结果返回。
git reflog show
accepts any of the options accepted bygit log
。git reflog show
命令是git log
命令的超集。
其余的子命令包括:
git reflog [show] [log-options] [<ref>]
git reflog expire [--expire=<time>] [--expire-unreachable=<time>]
[--rewrite] [--updateref] [--stale-fix]
[--dry-run | -n] [--verbose] [--all | <refs>…]
git reflog delete [--rewrite] [--updateref]
[--dry-run | -n] [--verbose] ref@{specifier}…
git reflog exists <ref>
git log vs git reflog
git log:commit 的版本日志,版本回退后,使用git log 看不到回退版本号之后的版本记录;
git reflog:使用git 命令进行操作的日志,包括当前步骤所在哪个版本(一个commit 产生一个版本,指定版本回退只能回退到该commit) 以及操作的具体内容;版本回退后,仍然可以看到所有的版本记录,查看每个操作步骤所在的版本,可以根据版本号自由前进后退。
总之:如果在回退以后又想再次回到之前的版本,git reflog 可以查看所有分支的所有操作记录(包括commit和reset的操作),包括已经被删除的commit记录,git log则不能察看已经删除了的commit记录