在某些情况下,为了代码审查或更清晰的版本历史,我想将现有的提交分成两部分。但是,在拆分提交时,您会发现出现某些不一致的情况,或者您必须修改新的父提交以维护构建。但是,我往往不得不做大量的手动工作来跟踪/维护我编写的最终代码。
是否可以在不修改最终提交的树哈希的情况下运行 rebase 。
例如我们有 3 个提交,A
、B
和 C
:
这是提交 A
# test_file.py
# this is commit A
does_stuff()
这是一个新的提交 B
# test_file.py
# this is commit B
does_stuff()
这是一个新的提交 C
# test_file.py
# this is commit C
does_stuff()
原始树的样子
... -- C -- A
但是我们想将A
“拆分”为两个提交
... -- C -- B -- A
当我们使用交互式 rebase 创建 B
时,A
的注释也会更新以显示 # this is commit B
(假设除了这个文件之外,还有其他一些更改)。但是,我们希望保持评论不变。注意:A
将由于具有不同的父级而具有不同的整体哈希,但其树哈希应与之前相同。
最佳答案
不可能修改任何 提交。相反,您将一些现有的提交集复制到一组新的提交中。这实际上是个好消息,因为这意味着原始提交仍然可用。
When we create
B
using an interactive rebase,A
's comment will also get updated to show# this is commit B
(assuming there's a few other changes other than just this one file).
在您展示的具体示例中,它不应该:您应该遇到 merge 冲突。
当然,对于其他情况,您是对的。
However, we want to keep the comment as is. Note:
A
will have a different overall hash due to having a different parent, but its tree hash should remain identical to before.
请记住,您开始于:
... -- C -- A
我会画成:
...--C--A <-- branchname (HEAD)
表示一些名为 branchname
的现有分支要提交的点 C
, 和 HEAD
附加到 A
.
然后你跑了git rebase -i <hash-of-C>
或类似的。这为您提供了待办事项列表,您可以选择“编辑”A
.现在开始:
将 HEAD 分离到 rebase 的目标:
A <-- branchname / ...--C <-- HEAD
复制提交
A
(在这种情况下,使用精确复制/快进,以便它重新使用A
本身;如果你喜欢--no-ff
,你可以禁用它,尽管最终它没有区别):A <-- HEAD, branchname / ...--C
或:
A <-- branchname / ...--C--A' <-- HEAD
(使用
--no-ff
强制复制)。
此时您将进行一些更改并运行 git add
和 git commit --amend
将当前提交推开并制作HEAD
指向新提交 B
, 其 parent 是 C
.假设您没有使用 --no-ff
;结果是:
A <-- branchname
/
...--C--B <-- HEAD
(如果你确实使用了 --no-ff
,还有一个额外的 A'
没有名字卡在外面;它会在大约一个月内被垃圾收集。然后我们必须调用下一个副本 A"
来告诉它们分开,所以我们假设您没有使用 --no-ff
。)
现在您想从提交 A
中获取文件 ,以及来自提交 A
的提交消息,并进行新的提交。自 branchname
仍然指向原始提交 A
,就这样做:
$ git checkout branchname -- . # assumes you're at the top level of your repo
$ git commit -C branchname # or -c if you want to edit it again
现在你有:
A <-- branchname
/
...--C--B--A' <-- HEAD
此时你用 git rebase --continue
完成了 rebase .由于没有提交要复制——你已经完成了最后一次提交的复制,A
, 就 rebase 而言——这是 rebase 的最后一步,即将分支名称从原始提交链中剥离并将其移动到指向与 HEAD
相同的提交。 , 同时重新附加 HEAD
:
A <-- ORIG_HEAD
/
...--C--B--A' <-- branchname (HEAD)
作为副作用,rebase 设置了 ORIG_HEAD
记住在哪里branchname
用于指向,因此很容易确保一切正常工作并最终达到所需状态:
git diff ORIG_HEAD HEAD
如果那是错误的,你可以 git reset --hard ORIG_HEAD
,导致:
A <-- branchname (HEAD)
/
...--C--B--A' <-- ORIG_HEAD
注意其他命令,包括git reset
, 设置 ORIG_HEAD
(这就是他们在这里交换的原因)。最终这两个提交中的一个将被完全放弃,除了 reflog 条目,当这些条目到期时,无法访问的提交将真正消失。此类提交的默认到期时间为 30 天。
关于git - 是否可以在不更改当前提交中的文件的情况下修改父提交?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51071162/