在使用 Git 进行开发时,为了能更加灵活地回滚,我们往往会在开发分支上频繁提交很多 commits。然而,当我们使用 Gerrit 作为 code review 平台时,要求我们 push 出去的分支只能比远程分支多一个 commit。因此,在开发分支完成开发后,我们会创建一个 code review 分支,将开发分支上的所有 commits 通过 git merge --squash dev-branch 的方式合并过来,形成一个单个的 commit。

然而,当 reviewer 提出修改意见时,我们需要进行进一步的修改。如果我们回到开发分支进行修改,并继续频繁提交,那么当我们完成所有修改后,就无法在提交分支上再次进行 git merge --squash dev-branch 了。因为 code review 分支已经包含了开发分支本轮 review 之前的修改内容,这会导致在 merge 时出现大量冲突。一个解决方案是,对前一次 git merge --squash 之后新提交的所有的 commits, 依次做 git cherry-pick -n,把所有这些 commits 的内容应用到当前 index 下而不生成新的提交,全部完成后做一次 git commit --amend。但这样一来我们就必须专门记住前一次 merge 的位置,如果之后的修改又提交了很多次的话,cherry-pick 一大串的 commits 操作还是很繁琐的。

面对这种情况,很多人可能会选择开始 code review 之后就放弃开发分支,直接在 code review 分支上进行修改,然后使用 git commit --amend 命令进行提交。但这样做就失去了在开发分支上频繁提交带来的便利性和安全感了。

其实,解决这个问题的方法并不复杂。由于开发分支和 code review 分支有共同的远程祖先,而在 code review 分支上我们只需要一个 commit,只关心开发分支的最终状态,不在乎中间有多少次提交记录,因此,我们没有必要非得通过 merge 来让 code view 分支和开发分支内容保持一致,而是把开发分支的最新状态强行覆盖到 code review 分支上就好。

要实现这一点,最方便快捷的方式就是使用 git checkout --no-overlay dev-branch . 命令。下面我们来详细分析和解释这条命令。

首先,我们需要明确,git checkout dev-branchgit checkout dev-branch file 这两条命令在 Git 中的作用是不同的:

接下来,我们来看一下 git checkout dev-branch . 这条命令:这条命令会将当前目录以及其子目录中的文件更新为在指定分支上最新的 commit 所对应的内容。这条命令不会改变 HEAD 指针的指向,也就是说,你仍然在当前的分支上工作,只是工作目录中的文件被更新了。

最后,我们来看一下 --no-overlay 参数的含义。在默认的覆盖模式下,git checkout 命令不会从索引或工作树中删除任何文件。当指定 --no-overlay 参数时,索引和工作树中存在但在目标分支中不存在的文件将被删除,以使它们与目标分支完全匹配。执行完上面所说的这条命令之后,我们只需要执行 git commit --amend,就可以推送并开始下一轮 code review 了。

希望这篇文章对于大家使用 Gerrit 能有所帮助!