Git: 恢复整个仓库

Git: revert entire repository

tl;dr: Revert my entire local repository to a specific point in time. I want all branches to be reverted to that point. I want all commits that came after that point in time to be completely gone. I guess a good way of depicting it would be to cut off all the tips of all the branches (as you would see in SourceTree) that are newer than a specific time.

假设我有 3 个分支,每个分支实现 "a way of doing it"。事实上,我想让这三个分支完全分开。但是,当我发现一个错误时,我想在所有三个分支中修复它,例如。那是我犯错的地方,我正在尝试以 git 的方式修复它。我将错误的分支合并到所有这三个分支中。 (git 合并分支的方式与我预期的略有不同。)事实上,我很想将 整个 git 存储库 恢复到最后一个现实时间点 没问题的地方。

为了做到这一点,我已经尝试了几个小时。我尝试使用 git reset --hard <commit>,但这并没有恢复存储库本身。似乎只有工作目录。

实际上,git reset --hard <commit> 会还原存储库中有关当前分支和工作目录的信息(如果没有 --hard,它只会还原存储库中的信息,而不会影响您的工作目录)。

如果你只有三个分支,最简单的方法就是 git checkout 每个分支,然后 git reset --hard <commit> 提交满足你要求的一些内容。

代替<commit>,您还可以使用时间规范,例如HEAD@{yesterday},这可能会稍微简化一些事情。有关指定日期(以及您可以引用提交对象的无数其他方式)的详细信息,请参阅 git-rev-parse man pageSPECIFYING REVISIONS 部分。

如果您有多个分支,您可能会稍微自动化一些,可能从 git for-reach-ref refs/heads/ 开始获取分支列表,但我还没有完成那个特定的解决方案。

简短版

听起来你想要三个独立的 git reset --hards。

TL;DR 长版

让我画一个图表,我认为它代表了您之前(并希望返回)和之后的情况:

  --- x           <-- badbranch
 /
... - o - o - o   <-- branch1
 \
  \-- o - o - o   <-- branch2
   \
    - o - o       <-- branch3

这是 "before you did three merges":branch1 至少有 3 次唯一提交,branch2 至少有 3 次唯一提交,branch3 至少有 2 次(这些是o 个节点)。

同时,第四个分支(上面的 badbranch)有一个 "bad" 提交 x

然后你做了:

$ git checkout branch1; git merge badbranch
$ git checkout branch2; git merge badbranch
$ git checkout branch3; git merge badbranch

(加上可能的解决方案,但我假设所有合并都很容易合并 创建的合并提交,如下所示)。

提醒一下,merge 所做的是获得两个差异,一个用于当前分支提示(假设 branch1)与合并基础(返回 [=27 中的某处) =] 一系列提交),一个用于要合并的提示(x)与相同的合并基础。然后在筛选掉所有已经存在于base-to-tip diff中的变化后,它将base-to-x的diff应用到当前分支的tip;如果一切顺利,它会根据结果进行新的提交:

  --- x               <-- badbranch
 /     `---------\
... - o - o - o - M   <-- branch1

我们还不需要查看 branch2branch3,但如果需要,暂时忽略 branch1 会有所帮助。这次我们只画 branch3,包括它的合并(我们称其为 M3 以区别于 M):

  --- x           <-- badbranch
 /      \
...      \
 \        \
  \        \
   \        \
    - o - o - M3   <-- branch3

现在让我们看看 git reset 做了什么,图形方面的。 (您已经知道 --hard 它会重置您的工作树。)

让我们回过头来考虑 branch1:

$ git checkout branch1

上面有错误的合并 M。出于显而易见的原因,我将放大绘图使其更高一点,然后我将同样向上移动 M

    - x               <-- badbranch
   /    \
  /      ------ M     <-- branch1
 /             /
... - o - o - o

(幸运的是,尽管现在看起来像卡通狗的头,但仍然很明显,它与以前的图形是一样的)。

当您 运行 git reset 使用提交 ID 或使用指定提交 ID 的名称时,它会查看当前分支(现在 branch1)并移动该标签到目标提交。在这种情况下,假设您使用 HEAD^ 或最尖端的提交 ID o;两者都给出相同的结果,将 branch1 向下移动以指向 o。现在图表是:

    - x               <-- badbranch
   /    \
  /      ------ M
 /             /
... - o - o - o       <-- branch1

"bad" 合并提交 M 不再直接可见,因为您获得的视图(使用 git log 或图形查看器)从分支提示开始,不再显示合并提交 M。所以你看到的是这样的:

    - x               <-- badbranch
   /
  /
 /
... - o - o - o       <-- branch1

这是合并之前的内容。

当然,分支 branch2branch3 仍然有它们的 "bad" 合并,但您可以通过检查 those 轻松解决这个问题分支并在每个分支上执行 git reset --hard HEAD^

每次重置都会将当前分支一次提交备份到您进行 "bad" 合并之前的位置。您必须为每个提交重复,因为每个分支过去和现在都是独立的,因为每个合并都是真正的合并,而不是 "fast forward".


如果某些分支 did 得到 "fast forward",您必须更加小心:HEAD^ 可能不再满足每个分支重启。但一般来说,将每个分支重置为所需的提交 ID(您可以在这些分支的引用日志中找到)就可以解决问题。