If I have a commit in the past that points to one parent, but I want to change the parent that it points to, how would I go about doing that?
Amb*_*ber 376
用git rebase.它是Git中的通用"take commit(s)and plop it/them on a different parent(base)"命令.
但是要注意一些事情:
由于提交SHA涉及其父项,因此当您更改给定提交的父项时,其SHA将更改 - 在开发行中将更改其后的所有提交(比它更新)的SHA也将更改.
如果您正在与其他人合作,并且您已经将提交的问题公开推送到他们撤回的地方,那么修改提交可能是一个坏主意™.这是由于#1,因此在尝试弄清楚由于您的SHA不再与"相同"提交相匹配时发生的事情时,其他用户的存储库将会遇到混淆.(有关详细信息,请参阅链接手册页中的"从上游重新恢复"部分.)
也就是说,如果你目前在一个分支上有一些你希望转移到新父级的提交,它看起来像这样:
git rebase --onto <new-parent> <old-parent>
Run Code Online (Sandbox Code Playgroud)
这会将当前分支之后 <old-parent>的所有内容移动到顶部<new-parent>.
Chr*_*sen 35
如果事实证明你需要避免重新定义后续提交(例如,因为历史重写是站不住脚的),那么你可以使用git替换(在Git 1.6.5及更高版本中可用).
# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.
replace_first_parent() {
old_parent=$(git rev-parse --verify "${1}^1") || return 1
new_parent=$(git rev-parse --verify "${2}^0") || return 2
new_commit=$(
git cat-file commit "$1" |
sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
git hash-object -t commit -w --stdin
) || return 3
git replace "$1" "$new_commit"
}
replace_first_parent B A
# …---o---A---o---o---…
# \
# C---b---b---…
#
# C is the replacement for B.
Run Code Online (Sandbox Code Playgroud)
在建立了上述替换后,对对象B的任何请求都将实际返回对象C.除了第一个父项(相同的父项(第一个除外),同一个树之外,C的内容与B的内容完全相同)相同的提交消息).
默认情况下,替换处于活动状态,但可以通过使用git--no-replace-objects选项(在命令名之前)或通过设置环境变量来启用替换.可以通过推送(除正常之外)共享替换.GIT_NO_REPLACE_OBJECTSrefs/replace/*refs/heads/*
如果您不喜欢commit-munging(使用上面的sed完成),那么您可以使用更高级别的命令创建替换提交:
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
Run Code Online (Sandbox Code Playgroud)
最大的区别在于,如果B是合并提交,则此序列不会传播其他父项.
Jak*_*ski 30
请注意,更改Git中的提交要求必须更改其后的所有提交.如果您已经发布了历史的这一部分,那么就不鼓励这样做了.有人可能已经在改变之前建立了他们的历史工作.
替代解决方案git rebase中提到琥珀的反应是使用移植机制(见Git中移植的定义Git的词汇和文档.git/info/grafts文件在Git仓库布局文档)改变提交父,检查它做正确的事情与一些浏览器的历史(gitk,git log --graph等等,然后使用git filter-branch(如其联机帮助页的"示例"部分所述)使其永久化(然后删除移植物,并选择删除备份git filter-branch或重新定义存储库的原始引用):
echo "$commit-id $graft-id" >> .git/info/grafts git filter-branch $graft-id..HEAD
注意 !!!该解决方案是从底垫的解决方案不同的是git rebase将重订/移植的变化,而移植物为基础的解决方案只会重新设置父级提交原样,完全没有考虑到老上级和新的父之间的差异!
Mar*_*ato 22
为了澄清上述答案并无耻地插入我自己的脚本:
这取决于你是否要"重新"或"重新".一个底垫中,通过所建议的琥珀,到处移动的diff.一个重新设置父级,所建议的Jakub和克里斯,四处移动快照整个树.如果你想重新表达,我建议使用git reparent而不是手动完成工作.
假设您在左侧有图片,并且您希望它看起来像右侧的图片:
C'
/
A---B---C A---B---C
Run Code Online (Sandbox Code Playgroud)
重新定位和重新定位将产生相同的图片,但定义C'不同.随着git rebase --onto A B,C'将不包含任何引入的变化B.与git reparent -p A,C'将是相同的C(除了B不会在历史中).
当然,几个小时前,当我尝试与OP完全相同的事情时,@Jakub的回答对我有所帮助。
然而,git replace --graft 现在是关于移植的更简单的解决方案。另外,该解决方案的一个主要问题是过滤器分支使我失去了未合并到 HEAD 分支中的每个分支。然后,git filter-repo就完美无缺地完成了工作。
$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force
Run Code Online (Sandbox Code Playgroud)
我前段时间提出过这样的问题,所以完整的答案可以在这里找到。
了解更多信息:查看文档中的“重新移植历史记录”部分
| 归档时间: |
|
| 查看次数: |
73946 次 |
| 最近记录: |