【问题标题】:Git checkout to a commit 2 commits before hashGit checkout to a commit 2 commits before hash
【发布时间】:2017-04-22 09:32:26
【问题描述】:

我必须在许多分支、许多 repos 上恢复对文件所做的更改。我知道我可以使用 git checkout hash filename 然后推送该更改。

问题是,我只知道在我想要恢复的实际提交之前有两个提交。

我如何询问 git 在此之前两次提交的哈希值是多少,并使用它来正确还原?

【问题讨论】:

  • 如果修订历史是 A-B-C-D-E(E 是最新的),并且您想恢复到 C,您是说您知道 A,还是您知道 E?
  • git 提交知道他们的父母,但因为他们可以有任意数量的孩子(分支),所以“x 提前提交”的捷径是没有意义的。因此,如果您正在寻找 2 次提交,答案是 hash~2,但如果要提前 2 次提交,则没有简单的方法
  • 所以我知道一个 git 哈希值,它在 E 或 E 之前。它可能是最新的,也可能不是最新的。所以假设我知道 E。我需要对 C 说 git checkout。
  • 另见here

标签: git hash git-checkout


【解决方案1】:

我不清楚您是否需要从某个特定提交中向后向前查看。

不过,所有这一切的关键是要认识到 Git 根本不关心分支 名称。 Git 关心的是提交图。每当您处理此类问题时,您都应该绘制图表

以下是一些示例图。一轮o 节点代表提交。 * 是您手中拥有其 ID 的提交,我给出了“可能有趣”的提交单字母名称:

...--A--o--*--o--B--...--o   <-- branchname
     ^           ^
   2 before    2 after

这个图表很简单。提交A 在提交* 之前出现两个,所以如果你有* 的哈希——假设它是1234567——你可以写1234567~2~2 的意思是“回去两个第一父母”(见下文)。查找提交 B 更加困难(见下文)。

但我们可能会遇到这样的混乱:

                o--D--o   <-- branch1
               /
              /  E--o     <-- branch2
      A--o   /  /    \
          \ /  /      \
...--B--o--*--o--F-...-o   <-- branch3
            \         /
             o--C----o

这里,AB 的提交都是* 之前的两个步骤,而所有CDEF 都是两个步骤之后

在 Git 中,查找“之前”的提交要容易得多,因为 Git 的内部箭头都指向“向后”。分支名称,它们都在右侧,因此指向分支上的 latest 提交——这实际上是分支在 Git 中的工作方式:名称指向 tip 提交,并且提示提交向后(在这些图中向左)指向更早的提交——简单地让 Git 开始查找提交。

给定一个提交,Git 会按照向后箭头指向该提交的父级。这里* 是一个合并提交,将A--oB--o 行放在一起。

* 的两个父级的顺序并未显示在图表本身中,但假设 A 行实际上是通过 second 父级找到的。假设* 又是1234567。然后1234567~2 是提交B(不是A),因为~ 只跟随first 父母。要查找A,您可以写1234567^2(在* 之前命名上面的o),然后添加^1~1 以返回一个父级到A

换句话说,两个提交现在是1234567~21234567^2~1。 (有更多的写法。)但最简单的方法是运行git log --graph --oneline 1234567 并观察图形——Git 会垂直绘制它,但你会看到两条线从* 出来,这会让你找到两者AB

如果有两种以上的方法可以达到两个以上“后退两步”的提交,您会在图表中看到这一点,就像我们对许多“前进两步”的提交所做的那样。但是......要找到“向前两步”的提交明显要困难得多。

同样,Git 从最新的(我们图纸中最右边的)tip 提交开始并向后工作。我画了三件东西,它们最终合并到 branch3,1 的提示中,但请注意,提交 D(这也是向前迈出的两步)only 可以通过以下方式找到从branch1向后搜索。

没有“向前搜索”

Git 无法从提交中“向前搜索”或“向前一步”。所有向前移动的操作(有一个!)实际上都是通过向后移动来工作的。基本上,我们从所有可能的起点开始,即所有分支名称/提示。2然后我们有Git list everything 返回我们想要的提交,当且仅当它们是我们正在查看的提交的 后代3 时打印这些提交的哈希 ID。

打印所有这些 ID 的命令是 git rev-list(实际上只是 git log4 被告知只打印提交的哈希 ID 而不是记录提交。)flag 上面写着“给我后代”是--ancestry-path,它必须结合更多的信息。特别是,我们必须告诉 Git 哪个提交是“有趣的”提交,并且我们使用 ^ 前缀(这次不是后缀)来做到这一点:

git rev-list --ancestry-path ^1234567 --branches

^1234567 告诉 Git 哪个提交停止了图遍历 并且 将打印的修订集限制为 1234567 的后代。 --branches 告诉 Git,像往常一样,从所有分支提示中开始搜索。 (我们也可以添加--tags,或使用--all 表示所有引用:所有分支、所有标签、存储(如果有的话)以及其他可能存在的内容。但是,--branches ,或--branches --tags,可能就是你想要的。)

现在,git rev-list 的问题在于它只会溢出所有修订 ID。这不仅包括提交CDEF,还包括提交* 之后的所有“无趣”o 提交。对此有许多可能的解决方案,但也许最简单的方法是返回使用git log --graph --oneline:这将代替仅打印(完整)散列,打印(缩写)散列和提交消息,并绘制图形。现在您可以通过观察图表来查找“提前两次”的提交。

(当然,由于git loggit rev-list基本上是同一个命令,你也可以运行git rev-list --graph。不需要--oneline,因为无论如何输出都是每行一个提交ID。但是@ 987654390@ 是一个 plumbing 命令,用于脚本中,对用户不是很友好,而git log 用于与用户交互,并试图更直接有用。)


1这种三父合并是一种叫做“章鱼合并”的东西,在实践中并不常见。更典型的情况是,你会进行两次合并,一次将 branch2 合并到 branch3,另一次(或早或晚)将现在未命名的底行提交合并到 branch3

2我们可能还想从标签开始。例如,考虑这个图形片段:

...--o--*--o--o   <-- branch
         \
          o--o    <-- tag: v0.98alpha

在这里,0.98alpha 版本被认为是“坏的”,并且可以从标签访问的两个提交不再位于 any 分支上。但是,如果事实证明这两个提交中的一个包含一些宝贵的数据或代码,您仍然可以通过从标记开始找到它,如果需要,可以向后工作到上一个提交。此处标记为* 的提交两步后退也在分支branch 上,因此即使没有从标记v0.98alpha 开始,您也会发现。

3在 Git 使用的这些有向图中,祖先是您可以通过从某个提交开始并向后工作可以达到的任何提交:提交本身,它的任何父母,任何父母的父母——任何祖父母——等等。这与你自己的“祖先”含义相同,只是在 Git 中,你被认为是你自己的祖先。

因为 Git 的箭头指向后方,所以很容易找到祖先。

“后代”一词的作用相同:您是自己的后代,但您的子孙后代也是您的后代,正如您所期望的那样。 Git 实际上无法直接找到这些孙辈,但是给定一对提交,有一个非常简单的测试“是提交Y提交的后代 X":我们只是反转测试。 Y 要成为 X 的后代,X 必须是 Y 的祖先。由于 Git 可以回答“是祖先”的问题,我们反转测试并得到我们的答案。

4git loggit rev-list 之间存在一些显着差异,因此它们完全不是同一个命令。但是它们是从相同的源代码构建的,并且 可以 做完全相同的事情,具体取决于选项。他们只是默认设置了不同的选项:log 打印人类可读的东西,使用寻呼机,使用颜色输出等等;而rev-list 打印机器可读的东西并且不使用寻呼机。使一个人表现得像另一个人所需的标志也不总是显而易见的。

【讨论】:

  • 非常详细。谢谢。我需要提交 A。~2 为我工作!
【解决方案2】:

使用 git log 查看对文件所做的修改:

git log myfile

当你想出要恢复的提交哈希时,使用

git checkout thehash myfile

【讨论】:

    【解决方案3】:

    您可以使用git rev-list --parents -n 2 &lt;commithash&gt; 在哈希之前到达提交 2。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-11
      • 2011-02-02
      • 2012-11-23
      • 2018-03-22
      • 1970-01-01
      • 2022-12-01
      相关资源
      最近更新 更多