我必须在许多回购中恢复对许多分支上的文件所做的更改。我知道我可以使用git checkout hash filename然后推送那个更改。
问题是,我只知道在我想要恢复的实际提交之前有两次提交。
如何在此之前询问git两个提交的哈希是什么,并使用它来正确还原?
答案 0 :(得分:2)
我不清楚您是否需要从某个特定提交中查找向后或转发。
所有这一切的关键是要认识到Git并不关心分支名称。 Git关心的是提交图。无论何时处理此类问题,都应该绘制图形。
以下是一些示例图表。轮o
个节点表示提交。 *
是您手中拥有ID的提交,我给了#34;可能有趣"提交一个字母的名字:
...--A--o--*--o--B--...--o <-- branchname
^ ^
2 before 2 after
这张图非常简单。在提交A
之前,提交*
有两个,所以如果你有*
的哈希 - 让我们说1234567
- 你可以写{ {1}}。 1234567~2
表示&#34;返回两个第一父母&#34; (见下文)。提交提交~2
更加困难(见下文)。
但我们可能会有这个混乱:
B
此处,提交 o--D--o <-- branch1
/
/ E--o <-- branch2
A--o / / \
\ / / \
...--B--o--*--o--F-...-o <-- branch3
\ /
o--C----o
和A
两个 B
之前的两个步骤,而所有*
,C
, D
和E
是之后的两个步骤。
在Git中,更容易找到&#34;之前的提交,因为Git的内部箭头都指向&#34;向后&#34;。分支名称,它们都在右侧,因此指向分支上的最新提交 - 这实际上是分支在Git中的工作方式:名称指向提示提交,并将提示提交点向后(在这些图中向左)提交给先前的提交 - 只需在找到提交时启动Git。
给定提交,Git跟随向后箭头到该提交的父级。此处F
是合并提交,将*
和A--o
行组合在一起。
B--o
的两个父母的顺序不是图表中显示的内容,但让我们说*
行实际上是通过第二个父母。让我们说A
再次*
。然后1234567
提交1234567~2
(不是B
),因为A
仅跟随第一个父母。要查找~
,您可以撰写A
(在1234567^2
之前命名上方o
),然后添加*
或^1
以便返回~1
的另一位家长。
换句话说,这两个提交现在是A
和1234567~2
。 (有更多方法可以写这个。)但最简单的方法是运行1234567^2~1
并观察图形 - Git将垂直绘制它,但是你会看到git log --graph --oneline 1234567
中出现两条线,这样您就可以找到*
和A
。
如果有两种以上的方法可以进行两次以上的提交,那么&#34;两步返回&#34;,您将在图中看到,就像我们为多次提交做的那样&#34;前进两步&#34;。但是......要找到提交,这是&#34;前进两步&#34;显然要困难得多。
同样,Git从最新的(最右边的图纸) tip 提交开始并向后工作。我画了三件最终合并到B
, 1 小费的内容,但请注意,提交branch3
- 这也是向前两步 - 只能 < / em>从D
向后搜索。
Git无法向前搜索&#34;或者&#34;前进&#34;来自提交。向前移动的所有操作(有一个!)实际上是向后移动。基本上,我们从所有可能的起点开始,即所有分支名称/提示。 2 然后我们有Git列表一切< / em>返回到我们想要的提交,当且仅当它们是提交的后代 3 时才打印这些提交的哈希ID。看着。
打印所有这些ID的命令是branch1
(实际上只是git rev-list
, 4 告诉只打印提交的哈希ID而不是记录提交。)标志表示&#34;给我后代&#34;是git log
,必须与几条信息相结合。特别是,我们必须告诉Git哪个提交是&#34;有趣的&#34;一个,我们用--ancestry-path
前缀(这次不是后缀)来做:
^
git rev-list --ancestry-path ^1234567 --branches
告诉Git哪些提交停止了图遍历,将打印修订集限制为^1234567
的后代。 1234567
像往常一样告诉Git,从开始搜索:从所有分支提示。 (我们还可以添加--branches
,或使用--tags
来说明所有引用:所有分支,所有标记,存储(如果有)以及其他可能存在的内容。但是,--all
或--branches
,可能就是你想要的。)
现在,--branches --tags
的问题在于它只会溢出所有修订ID。这不仅包括提交git rev-list
,C
,D
和E
,还包括所有&#34;无趣的&#34;提交F
后提交的o
提交。有许多可能的解决方案,但也许最简单的方法是回到使用*
:这将不仅仅打印(完整)哈希,打印(缩写)哈希和提交消息,再加上绘制图形。现在,您可以通过图表来查找提前两个提交&#34;。
(当然,由于git log --graph --oneline
和git log
基本上是相同的命令,您也可以运行git rev-list
。由于输出是一个,因此不需要git rev-list --graph
无论如何,每行提交ID。但是--oneline
是一个 plumbing 命令,意味着在脚本中使用,并且不是非常用户友好,而git rev-list
用于与用户交互,并尝试更直接有用。)
1 这个三父合并是一个名为&#34; octopus merge&#34;在实践中并不常见。更典型的是,您有两个合并,一个将git log
引入branch2
,另一个 - 更早或更晚 - 将现在未命名的底行提交行引入{{1} }。
2 我们可能也想从标签开始。例如,请考虑此图片段:
branch3
这里,版本0.98alpha被视为&#34;坏&#34;,并且从标签可到达的两个提交不再在任何分支上。但是如果事实证明这两个提交中有一个提供了一些宝贵的数据或代码,你仍然可以通过从标记开始找到它,并且如果需要,可以向后工作到之前的提交。标记为branch3
的提交两步返回标记为...--o--*--o--o <-- branch
\
o--o <-- tag: v0.98alpha
,因此即使没有从标记*
开始,您也会发现它。
3 在Git使用的这些有向图中, ancestor 是你可以通过从一些提交开始并向后工作而达到的任何提交:提交本身,任何一个父母,任何父母和他们的父母。父母 - 任何祖父母 - 等等。这与&#34;祖先&#34;的含义相同。对于你自己,除了在Git中,你被认为是你自己的祖先。
因为Git的箭头指向后方,所以很容易找到祖先。
术语&#34;后代&#34;同样的工作:你是你自己的后代,但你的孩子,孙子孙女和后孙也是你的后代,正如你所期望的那样。 Git实际上不能直接找到这些孙子孙女,但是如果提交了一对对,那么就会有一个非常简单的测试#34;提交 Y 提交 X &#34;的后代:我们只是反向测试。要使 Y 成为 X 的后代, X 必须是 Y 的祖先。由于Git 可以回答&#34;是祖先&#34;问题,我们扭转了测试并得到了答案。
4 branch
和v0.98alpha
之间存在一些显着差异,因此他们并非完全相同的命令。但它们是使用相同的源代码构建的,并且可以完全相同,具体取决于选项。他们默认设置了不同的选项:git log
打印人类可读的东西,使用寻呼机,使用颜色输出等等;而git rev-list
打印机器可读的东西,而不使用寻呼机。使一个人像另一个人一样行动所需的旗帜也不总是显而易见的。
答案 1 :(得分:1)
使用git log查看对文件所做的修改:
git log myfile
当您想出要恢复的提交哈希时,请使用
git checkout thehash myfile
答案 2 :(得分:0)
您可以使用git rev-list --parents -n 2 <commithash>
进行哈希之前的提交2。