把 git 分支恢复出厂设置

尽管你可能没有用过,但手机和电脑,都有一个恢复出厂设置的选项。如果不幸把一切搞得一团糟,还有机会可以重新来过。那么,如果把 git 仓库搞成了一团糟,能不能也恢复出厂设置,变成和远程仓库一模一样的内容呢?

你的心中可能已经有了一个答案:

1
2
3
cd ..
rm -rf <git_repo>
git clone <git_repo>

与这种费时费力的做法相比,其实还有一个更加优雅的方案。

首先咱们来考虑放松这个问题的要求。既然工作空间(workspace)总是建立在某个分支(branch)之上,绝大多数情况下,如果只关心当前的工作空间,那么只要对一个分支做恢复出厂设置的操作即可,而不需要重置整个仓库。具体来说,我们把目标改成,让本地仓库的某个分支与其追踪的(tracked)远程仓库分支的内容完全一致。

下一步,把恢复出厂设置拆解成几个可执行的部分。此时可能出现的情况有如下几种:

  1. 远程仓库由于最近的提交(commit),包含一些本地仓库分支中没有的内容。因此,在重置的过程中,必然包含从远程仓库获取最新信息的步骤。
  2. 本地仓库的分支中含有需要被舍弃的内容。
  3. 本地仓库中可能包含未被 git 追踪的文件,它们也需要清理。

将上述三种情形逐一翻译成指令,就是这个问题的解答:

1
2
3
git fetch
git reset --hard @{upstream}
git clean -ffdx

git fetch 从远程仓库拉取所有的更新,但不对当前的工作空间做任何改动。

git reset --hard @{upstream} 将当前工作空间中被 git 追踪的文件强制重设为远程仓库对应分支的内容。@{upstream} 是一个特别的符号,用来指代当前本地分支追踪的远程分支。它也可以简写为 @{u}

最后,git clean -ffdx 删除其它所有未被 git 追踪的文件。这其中,-d 表示同时删除文件夹,-x 表示同时删除被 .gitignore 除外的文件,-ff 表示同时删除子文件夹中的 .git 目录,也就是删除 git 子项目。

经过这一套组合拳,我们成功达成了将工作空间恢复出厂设置的任务。

这篇文章源自于近期的工作。当时我负责公司某个模块的持续集成(continuous integration)流水线的开发工作。根据要求,该流水线需要定期将开发分支的代码合并(merge)到发布分支。在此过程中,有一台机器专职负责运行自动化测试。如果测试通过,就将合并的结果上传到远程仓库,否则什么都不做。在每次测试前,为了构建正确干净的测试环境,都需要将开发分支和发布分支恢复出厂设置。我将解决这一问题的方法写成本文,希望你可以有所收获。