我有一个250 files changed
的拉取请求,我只更改了其中的5〜10。
这是一个包含4个提交的目录,其中一个是revert
的{{1}}和另一个分支,而这个分支包含〜240个merge
。
看起来像这样
files changed
我只想还原
<some commits I made after this>
<squashed commit of:
minor change
working on abcd..
Revert "Merge branch 'settlement-statement' into rents-proration"
This reverts commit 0e44eae, reversing
changes made to a39e7fd.
some more change>
我认为将Revert "Merge branch 'settlement-statement' into rents-proration"
This reverts commit 0e44eae, reversing
changes made to a39e7fd.
分支合并回我的分支将解决此问题,但是并不能解决问题。
如何解决此问题,而无需查看settlement-statement
中的所有文件并选择我的更改?
答案 0 :(得分:3)
更新-我分两个阶段编写了原始答案,因此有些混乱。信息保持不变,但我要对其进行清理...
取决于您如何squash
进行提交,您有两种非常相似的情况之一。粗略地说:
1)如果您从一个分支压缩到另一个分支(例如,被git merge --aquash
压缩),则您将拥有一个类似
x -- AB!MCD -- E <--(branch_for_pr)
A -- B -- !M -- C -- D <--(work_branch)
其中!M
恢复了一些先前的合并,而AB!MCD
是A
,B
,!M
,C
,和D
。
2)如果您将提交“压缩”在分支上,例如git rebase -i
,图形更像
x -- AB!MCD -- E <--(branch_for_pr)
A -- B -- !M -- C -- D <--(branch_for_pr@{2})
此处branch_for_pr@{2}
是引用日志条目。该工具显示的日志条目不如当前的ref明显,因此看起来A
.. D
似乎“消失了”,但是(至少在发生壁球的本地克隆上)他们希望不是。 (的确,reflog条目最终会过期-默认情况下大约3个月后,或者您明确将其过期。在reflog条目过期之后,gc
可以删除旧的提交,除非与此同时,您已经完成了使它们“可访问”的操作。此外,reflog也不会在push
或fetch
上共享。)
您将使用以下命令找到reflog条目
git reflog branch_for_pr
,然后可以在其上放置新的分支或标签,直到您不再需要它为止,从而消除了reflog到期将其隐藏起来的风险。如果这样做,结果将看起来像第一个图(来自上面的(1))。
此条目的其余部分假定实际分支指向D
。那么...如何进行?
最简单的方法是还原!M
上的branch_for_pr
。这看起来似乎违反直觉,但是您几乎可以还原任何内容。它不一定是HEAD
的祖先。因此,您需要一个可解析为!M
的表达式-它可以是其提交ID,或者在本示例中为work_branch~2
之类。
git checkout branch_for_pr
git revert work_branch~2
要了解的一些背景知识:
提交只是具有少量元数据的项目的快照。元数据包括一个“父”列表,该列表将快照相对于其他快照的历史记录进行放置。许多git命令都对提交的 patch 进行操作-即,该提交与其父级(或合并时,其父级之一)之间的区别。提交的元数据不不包含任何特殊关系来“由此提交还原的提交”或“被压缩为该提交的提交”;该信息实际上丢失了。
如上所述,“还原”只是一种以某种方式组装与其父项相关的提交的简便方法,该提交受另一提交与其其父级的关系的支配。 git
并不表示该特定提交是通过还原另一个提交(而不是其他提交)而生成的。
同样,“压缩”只是指定要应用于HEAD
以创建新提交的一组更改的简便方法。最终的提交不会“知道”它是壁球。
因此,这些事情可能会改变您对尝试做的事情的思考方式,并影响您解决此问题的方式。对于“重新进行合并”的提议解决方案,还有一个更直接的问题是,git对于合并的还原有点奇怪。还原合并后,重新合并就不那么容易了。您要么必须从新的提交中重新生成分支(以使重新合并成为可能),要么只需还原还原-我建议您直接在最终分支上进行还原。
这个想法有很多变化,可能会产生“更清洁”的历史;但这是最简单的方法,甚至可以避免使事情更加混乱。
答案 1 :(得分:2)
您不能从南瓜中拉出一个提交。您如愿以偿,它们被压缩为一个。
幸运的是,Git花费了数周时间才能丢弃任何东西。您未压缩的分支可能仍然存在。希望您的存储库看起来像这样。
A - B - C - D [master]
|\
| E [feature]
\
F - G - H - I
feature
是您的分支。 F - G - H - I
是您未压缩的原始提交。 E
被F - G - H - I
压在一起。我们想将feature
重新放到I
上。
I
未被分支或标签引用。我们可以使用reflog
找到它。这是每次HEAD
移动时的历史记录。每次签出,重新设置基准并重置。您正在寻找类似的东西。
c6a7e90 (HEAD -> master) HEAD@{0}: rebase -i (finish): returning to refs/heads/master
c6a7e90 (HEAD -> master) HEAD@{1}: rebase -i (squash): second
40c7031 HEAD@{2}: rebase -i (squash): # This is a combination of 2 commits.
4c79819 HEAD@{3}: rebase -i (start): checkout HEAD^^^
7fd34b9 HEAD@{4}: rebase -i (finish): returning to refs/heads/master
7fd34b9 HEAD@{5}: rebase -i (start): checkout master
7fd34b9是I
,这是压扁之前分支的提交ID。 c6a7e90是E
,即您压缩后的提交。然后,您可以git reset --hard 7fd34b9
将feature
放回原位。
可以在Git书中找到有关Maintenance and Data Recovery和Reset Demystified的更多信息。
这就是为什么不压缩整个要素分支的原因。重要的细节都丢失了历史和审阅者。你为什么改变那条线?这可能是四个混合原因之一。 OTOH,您可以充分了解未压缩的分支eash的“压缩”视图; Github将为您的PR提供一个,也可以执行git diff master
。但是,您不能反其道而行之,也不能从压缩的分支中获得未压缩的更改。重要信息丢失。
应该保留挤压以消除无趣的更改,例如拼写错误和“我想在三个提交之前以不同的方式进行更改”。