从零开始的 Git 代码管理指南(一)提交

四、提交

4.1 一个文件的旅行

在前文当中,提到Git当中的“提交”这一概念,相信很多人都在各个地方看到这个图片或者类似的图片:

这一图片描述了一个文件在Git当中的状态转换,起初,所有文件都不被Git跟踪,通过git add命令,可以将一个文件添加到Git的暂存区,通过git reset可以将文件从暂存区取出(通过git status可以看到Git提示的详细的命令),通过Git Commit可以将暂存区中的全部文件添加到Git的索引当中。

事实上,一个文件可以在不同的状态中有不同的内容,一个文件显然在Staging当中与Git索引当中的内容不同,而Staging当中的文件内容又可以与工作目录下的文件有所不同,而在部分IDE当中,甚至支持将文件中的一部分添加到暂存区的功能。(如下图,通过单击+图标,可以将这一行改动添加到Git暂存区当中)

VSCode当中对部分暂存的支持

4.2 从历史版本中恢复

前文提到,Git是一个版本管理系统,那么其自然支持从获取一个文件的历史,在Git当中,检出一个文件的历史版本只需要

$ git checkout revision filename

这里,filename也可以是目录名,表示检出整个目录。通常,revision是一个提交 ID,但也可以是其它可以用来表示一个提交的别的东西,比如一个分支名等。

对于将整个目录切到一个历史版本上的需求,Git当然也是支持的,如果我们将文件名留空,则表示将整个Git版本树切到一个历史版本上。当我们切过去的时候,Git会要求当前“脏”的文件不能被编辑,显然这样的要求是为了防止自己的本地修改被覆盖,因而要求用户对每一个潜在的文件丢失知情。

当检出一个历史版本的时候,之前的头部(HEAD)指针有可能不被任何其它的指针所指向,这会导致唯一检出之前的版本的方式是检出一个提交ID,同事,“悬空”的提交ID可能会被Git垃圾回收所删除,这时,Git会发现这一潜在问题,并提示你通过创建分支的方式来避免这一问题。

4.3 婊人

我曾经干过一件事,有一次看到了几行很差的代码,于是大喊“这是谁写的”,几秒钟之后,我一声不吭地无视这个问题,因为我在历史版本中发现是老板,这个功能就是Git所提供的blame命令。

➜  blog.matsu.dev git:(git-playbook) ✗ git blame content/about.md
cec20456 (Guo Song 2019-04-05 21:40:26 +0800  1) ---
c5ed7a3e (Song Guo 2020-08-30 20:26:53 +0800  2) title: "About / Resume"
cec20456 (Guo Song 2019-04-05 21:40:26 +0800  3) hidden: true
cec20456 (Guo Song 2019-04-05 21:40:26 +0800  4) date: 2019-04-05T21:31:58+08:00
cec20456 (Guo Song 2019-04-05 21:40:26 +0800  5) ---

上面这一段就是git blame命令的输出,可以看到,在这个书橱里,从左到右分别是Commit ID,提交者的姓名和提交时间,行号和内容。在IDE或者网页上,还提供了更直观的显示:

某项目的在GitHub上的git blame输出

4.4 更正

当我们在本地创建了提交,又发现提交的内容有疏漏或者添加了多余的内容到提交当中,这时候就需要“更正”操作,即commit命令的amend选项。这个选项使用起来十分简单,使用git add等命令将更改暂存,然后使用git commit --amend来更正提交即可。

你还会被提示修改提交信息。

开始使用git的新手往往会犯下两种错误其一是不会使用--amend,导致提交历史中有大量的无意义的fix提交,其二是滥用amend,导致提交过大,同时历史丢失。此时需要正确认识commit,虽然提交的大小不存在统一的标准,但一个共识是,任何一个足以自洽的一段代码,都值得用一个单独的commit来记录。

当然这个问题也可以反过来思考,即当发现Bug的时候,是否*需要且仅需要*撤回一个提交即可。

4.5 撤回

当一个提交上面又有了许多新的提交,却发现一个提交引入了一个恶性缺陷,好在这个功能没有被新的提交依赖,这时候,先别急着一行一行地注释代码,不如直接使用git revert功能来撤回一个提交。

使用git revert并在后面跟上需要撤回的提交ID,git会自动暂存回滚的提交内容,并尝试提交它,有时会遇到冲突,此时需要手动修正这些冲突,并按照提示继续即可。相比于自己注释代码,使用Git来撤回提交显著减少了错误发生的概率。

一个好的提交,也应当是一个方便撤回的提交。

关于提交的详细实现,将在后文继续分析

版本历史

2020/09/06 初始版本