是否可以在不更改当前提交中的文件的情况下修改父提交?
Is it possible to modify a parent commit without changing the files in the current commit?
在某些情况下,为了代码审查或更清晰的版本历史,我想将现有提交拆分为两个。但是,在拆分提交时,您会发现出现某些不一致的情况,或者您必须修改新的父提交以维护构建。但是,我往往不得不做很多手工工作才能 track/maintain 我编写的最终代码。
是否可以在不修改最终提交的树哈希的情况下运行变基。
例如我们有 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
但是我们想 "split" 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).
在您展示的具体示例中,它不应该:您应该遇到合并冲突。
对于其他个案例,当然,你是对的。
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
的现有 b运行ch 指向提交 C
,并且 HEAD
附加到 A
.
你然后 运行 git rebase -i <hash-of-C>
或类似的。这为您提供了一份待办事项列表,您可以选择 "edit" A
。 Git现在:
将 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
,其父项是 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
完成了变基。由于没有提交要复制——就变基而言,你已经完成了最后一个提交的复制,A
——这是变基的最后一步,即剥离 b运行 ch name off the original commit chain 并将其移动到指向与 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 天后。
在某些情况下,为了代码审查或更清晰的版本历史,我想将现有提交拆分为两个。但是,在拆分提交时,您会发现出现某些不一致的情况,或者您必须修改新的父提交以维护构建。但是,我往往不得不做很多手工工作才能 track/maintain 我编写的最终代码。
是否可以在不修改最终提交的树哈希的情况下运行变基。
例如我们有 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
但是我们想 "split" 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).
在您展示的具体示例中,它不应该:您应该遇到合并冲突。
对于其他个案例,当然,你是对的。
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
的现有 b运行ch 指向提交 C
,并且 HEAD
附加到 A
.
你然后 运行 git rebase -i <hash-of-C>
或类似的。这为您提供了一份待办事项列表,您可以选择 "edit" A
。 Git现在:
将 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
,其父项是 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
完成了变基。由于没有提交要复制——就变基而言,你已经完成了最后一个提交的复制,A
——这是变基的最后一步,即剥离 b运行 ch name off the original commit chain 并将其移动到指向与 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 天后。