凌云的博客

行胜于言

跟我一起学Git (四) 提交

分类:Git| 发布时间:2014-03-03 22:05:51


识别提交(Identifying Commits)

你可以通过显示或隐式的方法来引用一个提交。 显式就是使用SHA1码,而隐式就是使用如HEAD之类的名称。

绝对的提交名称(Absolute Commit Names)

每个提交的SHA1码都是全局唯一,这个全局的意思是在所有的代码库中,而不仅仅是在当前的代码库中。

refs和symrefs

ref是一个用于引用object store中对象的SHA1 hash ID。 虽然它可以引用任何类型的对象,但它通常只引用commit object。 本地分支,远程分支和标签都是ref。 符号引用(symref)是一个用于间接指向Git对象的名称。 每个符号引用都有一个显式的以refs/开头的保存在.git/refs/directory的全称。

在refs/命名空间中通常有以下几种类型的符号引用:

  • refs/heads/ref # 本地分支
  • refs/remotes/ref # 远程追踪分支
  • refs/tags/ref # 标签

对于这些命名空间中的符号引用我们既可以使用全称也可以使用缩写, 比如一个本地的分支dev是refs/heads/dev的缩写, origin/master是/refs/remotes/origin/master的缩写。

如果你使用了缩写,而且在不同的命名空间中都有这个分支(标签)那么会怎样呢? Git会根据以下顺序找到第一个符合的

  • .git/ref
  • .git/refs/ref
  • .git/refs/tags/ref
  • .git/refs/heads/ref
  • .git/refs/remotes/ref
  • .git/refs/remotes/ref/HEAD

第一条规则通常是寻找 HEAD, ORIG_HEAD, FETCH_HEAD, 和 MERGE_HEAD。

Git维护了几个特殊的symref用于特殊的目的

  • HEAD HEAD 总是引用当前分支中最近的一个提交
  • ORIG_HEAD 有些操作,不然merge和reset会将上一个版本的HEAD保存在ORIG_HEAD中
  • FETCH_HEAD 当使用远程代码库时,git fetch会将所有的分支HEAD fetched 记录在.git/FETCH_HEAD中,而FETCH_HEAD就是上一个分支的fetched的缩写, 并且只有在git fetch后有效
  • MERGET_HEAD MERGET_HEAD是在合并时正在合并到HEAD的那个提交的引用

可以通过使用git symbolic-ref来管理符号引用

相对的提交名称(Relative Commit Names)

可以用符号引用(如HEAD)加上~或^来相对的引用提交

如下图所示:C^1(C^)表示第一个父节点C^2(C^^)表示第二个父节点:

如下图所示:C~1(C~)表示第一个父节点,C~2(C~~)表示第一个祖父节点:

例子(用webpy的代码库):

$ git rev-parse master
6b86a402f243b65748d1c76d9b910b90125a9e4c
$ git show-branch --more=35 | tail -10
[master~15] Added ChangeLog.
[master~16] Fixed datestr error on windows. (closes #155)
[master~17] test requires MySQL-python not PyMySQL.
[master~18] Fixed Travis-CI failures.
[master~19] Integrated @benhoyt's optimizations to web.cookies. (closes #148)
[master~20] Merge branch 'master' of github.com:webpy/webpy
[master~20^2] Merge pull request #152 from jzellman/master
[master~20^2^2] Add GroupedDropdown in form api which supports the <optgroup> ta
g in HTML selects
[master~21] Disabled testPooling for sqlite.
[master~22] Added app.stop() by making server a global variable. #122
$ git rev-parse master~3^2
411908a9c2432b0caf4d8d21daed886f9f6afc2b

提交历史

查看旧的提交

查看提交的主要命令当然是git log了,但是这个命令有很多的选项,参数等。我们来看看一些比较常用的。

git log commit 如果你提供了_commit_那么git log会从_commit_开始显示从_commit_开始往后的提交,比如:

$ git log master
commit 6b86a402f243b65748d1c76d9b910b90125a9e4c
Merge: 2010d39 32c891c
Author: Anand Chitipothu <anandology@gmail.com>
Date:   Wed May 29 07:21:56 2013 -0700

Merge pull request #227 from iamFIREcracker/patch-1

Support '204 No Content' status code

commit 32c891c64bd838ad972239f81254fdfb0c784bf4
Author: Matteo Landi <matteo@matteolandi.net>
Date:   Mon May 6 08:35:44 2013 +0300

Support '204 No Content' status code

commit 2010d39b179a5912ee40221066d843447e84bb1c
Author: Anand Chitipothu <anandology@gmail.com>
Date:   Thu Mar 7 22:23:15 2013 +0530

Fixed handling of long numbers in sqlify. closes #213. (tx cjrolo)

commit 0fbe0da16a6ea283cdfbc7cff72564af264b6a43
...

当然显示所有的日志对我们来说不是很实用, 其实我们可以提供一个区间让git log只显示这个区间的日志。 指定区间的语法为:git log since..until 这样git log就会显示从_since_到_until_的日志

$ git log --pretty=short --abbrev-commit master~12..master~10
commit 914ceb7
Merge: 3861897 c5c0e89
Author: aaronsw <me@aaronsw.com>

Merge pull request #173 from sandesh247/master

commit c5c0e89
Author: Sandesh Singh <sandesh247@gmail.com>

use 'port' as keyword for pgdb

commit 3861897
Author: Aaron Swartz <me@aaronsw.com>

forgot to return port in dburl2dict

这样git log就显示出了第10和第11个master分支之前的提交

可以通过-p输出提交的patch或者修改,--stat参数可以输出提交中有多少个文件改变了并且统计每个文件都多少行改变

可以通过git show命令来显示object store中的对象

$ git show HEAD
$ git show origin/master:setup.py

第一个命令显示HEAD指向的提交对象,后一个命令显示origin/master分支中的setup.py文件

提交图表

Git使用有向无环图(DAG)来维护提交图表, 可以通过命令gitk来查看提交图表(gitk不属于git的子命令)

提交区间(Commit Ranges)

很多Git命令都允许你指定一个提交的区间,但是这是最简单的情况, 实际上它允许你指定“包含”和“排除”哪些提交。 区间_start_.._end_是指_end_可到达但是_start_不可到达的集合(记住Git是有向图)。 如果没有指定_start_或者_end_那么没有指定的则被认为是HEAD。 A...B(3个点)表示A或B可到达的集合,但不包括A和B都包括的集合(即是 对称差分运算) 这种集合也可以表示为:

$ git rev-list A B --not $(git merge-base --all A B)

查找提交(Finding Commits)

使用git bitset

这个命令通常用于当你发现代码库有问题(比如不能编译)时,指定一个区间。 然后Git会按二分法checkout这个区间的代码然后由你判断此版本的代码库是否有问题, 然后根据你的回答逐渐收缩范围,最后确定哪个是出问题的提交。

使用git blame

git blame用于输出特定文件的每一行是由谁在哪个提交修改的

使用Pickaxe

可以通过git log -Sstring 来查找差异中包含string的提交