Git 之术与道 -- 索引

接上文Git 之术与道 -- 对象,现在,我们的 dota-game 项目中已经有了下面这些对象:

每个对象都有一个 SHA-1 校验和(40位)。我们可以直接通过这个校验和来索引对象。不过,谁会喜欢记忆这些超长的哈希值呢?

分支

在大多数版本控制系统中,创建新的分支意味着对现有代码执行一次物理拷贝。项目的规模越大,时间开销也就越大。而在 Git 中,分支的创建与合并是非常轻量级的操作。所谓分支,仅仅是一个指向最近一次提交的指针而已。

默认的分支叫做 master 分支。master 这个名字并没有什么特殊之处,你当然可以改作其他名字。分支信息被存储在 .git/refs/heads/ 目录下面,文件内容就是分支所指向的 commit 对象:

$ ls .git/refs/heads/
master
$ cat .git/refs/heads/master
c5cbfa0f491087c575d8856632451f8d8763b94f

现在,新建另一个名为 develop 的分支,并提交一些新的内容:

$ git checkout -b develop
$ echo -n "print 'The answer to life the universe and everything is 42.'" > answer.py
$ git add answer.py
$ git commit -m "third commit"
$
$ ls .git/refs/heads/
develop master
$ cat .git/refs/heads/develop
d23dd7b9d38b5560ef5cd8cb3b3b7744a29d808c
master & develop branches

分支是 Git 的杀手级特性。你应该在工作流中广泛使用分支,例如:开发分支和发布分支应该区分开来;新功能应该在独立的特性分支上开发等等。不过,太方便了也可能会带来问题,稍不注意,你的代码库就会变得分支众多,版本混乱。

HEAD

Git 怎么知道我们当前在什么分支上呢?.git/HEAD这个文件记录了当前分支的信息:

$ cat .git/HEAD
ref: refs/heads/develop

HEAD 关键字可以直接拿过来使用,它代表了最近的一次提交:

$ git log HEAD    // 等效于 git log d23dd7
$ git show HEAD   // 等效于 git show d23dd7

标签

这篇文章已经讲过,Git 中有两种标签,一种标签会在 .git/objects 目录下面创建一个实实在在的对象(注解标签),另一种标签仅仅在 .git/refs/tags 目录中创建一个文件而已(轻量级标签)。我们前面创建的两个标签在项目中的位置如下图所示:

Lightweight tag & annotated tag

与分支不同,标签的指向是死的,一经创建,它就永远指向同一个地方。

引用日志

HEAD reflogs

执行命令 git reflog,你会看到类似这种格式的信息:

$ git reflog
d23dd7b HEAD@{0}: checkout: moving from master to develop
c5cbfa0 HEAD@{1}: checkout: moving from develop to master
d23dd7b HEAD@{2}: checkout: moving from master to develop
c5cbfa0 HEAD@{3}: checkout: moving from develop to master
d23dd7b HEAD@{4}: commit: third commit
c5cbfa0 HEAD@{5}: checkout: moving from master to develop
c5cbfa0 HEAD@{6}: commit: second commit
2bafd8d HEAD@{7}: commit (initial): first commit

也许你已经猜到,reflog 记录的是 HEAD 的变更历史。HEAD 的每一次变化,都会在 Git 中留下足迹,这样才不会迷失。

不小心执行了 git reset ?没关系,完全可以轻松撤销回之前的状态:

git reset HEAD@{1}

分支 reflogs

除了 HEAD reflog,每一个分支也有自己的 reflog。分支 reflog 记录了分支的演进历史:

$ git reflog show master
c5cbfa0 master@{0}: commit: second commit
2bafd8d master@{1}: commit (initial): first commit
$
$ git reflog show develop
d23dd7b develop@{0}: commit: third commit
c5cbfa0 develop@{1}: branch: Created from HEAD

时间 reflogs

比如,我想查看一下昨天提交了哪些更新,可以使用这个命令:

$ git diff @{yesterday}

下面所列的时间格式都是可用的(注意单复数):

yesterday
1.minute.ago
2.hours.ago
3.days.ago
4.weeks.ago
1.month.ago
2.years.ago
2015-09-11.23:00:00

祖先引用(Ancestry References)

分支的合并会导致新的 commit 对象有多个父级对象。Git 会把形如 d23dd7^ 的索引解析为该对象的“直接父级对象”(也就是与该对象处在相同分支的父对象)。d23dd7^2 则表示该对象的“第二父级对象”(也就是被合并分支上的父对象)。

dota-game 项目中执行下列操作:

$ git checkout master            // 切换到主分支
$ echo -n "print 'Maybe PHP is not the best language in the universe.'" > main.py
$ git add . 
$ git commit -m "fourth commit"  // 新建提交
$ git merge develop              // 合并 develop 分支
Object structure

通过索引 34a775^ 可以拿到 d7afc1 这个对象;通过索引 34a775^2 可以拿到 d23dd7 这个对象。

$ git show 34a775^
commit d7afc18b714fc0e6d7086c1284d0f2bf1e958d37
fourth commit
...
$ git show 34a775^2
commit d23dd7b9d38b5560ef5cd8cb3b3b7744a29d808c
third commit
...

另一个容易与此混淆的符号是 ~~ 符号沿着当前分支一直向前回溯。。索引 34a775~ 同样拿到 d7afc1 这个对象,因此可以说 34a775~ 等价于 34a775^。不同的是,索引 34a775~2 拿到的是 c5cbfa 对象。Git 会把 ~2 解析为“父级对象的父级对象”(也就是爷爷级对象)。

$ git show 34a775~
commit d7afc18b714fc0e6d7086c1284d0f2bf1e958d37
fourth commit
...
$ git show 34a775~2
second commit
...

这里不太容易理解,千万不要混淆哦。

图示:

Ancestry References

区间(Commit Ranges)

多重索引

Git 允许同时查看多个分支上的提交历史,你可以在命令中指明多个分支:

$ git log master develop

排除

你也许想看一下那些在 develop 分支上而不在 master 分支上的提交,这在合并分支的时候非常有用。可以使用 ^ 符号指明需要排除的分支:

$ git log ^master develop

因为这个功能在合并分支的时候会被频繁地用到,所以 Git 提供了专门的“双点”(..)符号:

$ git log master..develop

这和上一条命令是等价的,都会摘选出那些在 develop 分支上而不在 master 分支上的提交。

互斥

有些提交只在 develop 分支上,有些提交只在 master 分支上。“三点”(...)符号帮助你选出那些在其中某一条分支上而不同时在两条分支上的提交:

$ git log master...develop

总结

  1. 在 Git 中,分支的管理十分便利且强大;
  2. HEAD 指示了当前所在的分支;
  3. 与分支不同,标签相当于一个永久性的路桩,一旦被创建,它就会始终指向同一个对象;
  4. 引用日志提供了一种灵活的方式,当我们需要非线性地访问提交历史时非常有用;
  5. ^~ 符号帮助你引用到当前对象的祖先;
  6. 管理分支的时候,在命令中显式地指明提交区间,可以有选择地获取提交历史。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容