Answers:
使用git rebase。这是Git中的通用“获取提交并将其放到其他父级(基础)上”命令。
但是,有些事情要知道:
由于提交SHA涉及其父级,因此,当您更改给定提交的父级时,其SHA也会更改-在开发领域中,紧随其后(比更新更早)的所有提交的SHA也会更改。
如果您正在与其他人一起工作,并且已经将有问题的提交公开推到了他们将其拉到的位置,则修改提交可能是Bad Idea™。这是由于#1所致,因此,其他用户的存储库在尝试找出由于SHA不再匹配“相同”提交而与他们的SHA匹配而发生的情况时,会引起混乱。(有关详细信息,请参见链接的手册页的“从UPSTREAM REBASE恢复”部分。)
就是说,如果您当前正在某个分支上,并且有一些提交要移到新的父级,那么它将看起来像这样:
git rebase --onto <new-parent> <old-parent>
这将移动一切后 <old-parent>在当前分支坐在上面<new-parent>来代替。
--onto是用来干什么的!即使阅读此答案,文档也始终对我完全晦涩!
git rebase --onto <new-parent> <old-parent>告诉我有关如何使用rebase --onto到目前为止我已阅读的所有其他问题和文档的所有内容。
rebase this commit on that one或git rebase --on <new-parent> <commit>。在这里使用老父母对我没有任何意义。
git rebase <new-parent>。
如果事实证明您需要避免重新建立后续提交(例如,因为历史记录重写将是站不住脚的),则可以使用git replace(在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.
建立上述替换后,对对象B的任何请求实际上都会返回对象C。C的内容与B的内容完全相同,除了第一个父对象(相同的父对象(第一个父对象除外),相同的树,相同的提交消息)。
默认情况下,替换是活动的,但是可以通过使用git--no-replace-objects选项(在命令名之前)或通过设置环境变量来关闭替换。可以通过推动共享替换(除了正常操作)。GIT_NO_REPLACE_OBJECTSrefs/replace/*refs/heads/*
如果您不喜欢提交查询(上面用sed完成),则可以使用更高级别的命令来创建替换提交:
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
最大的区别是,如果B是合并提交,则此序列不会传播其他父级。
refs/replace/引用层次结构。如果只需要执行一次,则可以git push yourremote 'refs/replace/*'在源存储库和git fetch yourremote 'refs/replace/*:refs/replace/*'目标存储库中进行。如果需要多次执行,则可以将这些refspecs添加到remote.yourremote.push配置变量(在源存储库中)和remote.yourremote.fetch配置变量(在目标存储库中)。
git replace --grafts。不确定何时添加,但其整个目的是创建一个与提交B相同但具有指定父级的替换。
请注意,在Git中更改提交要求还必须更改其后的所有提交。如果您发布了历史的这一部分,则不建议这样做,并且可能有人将其工作建立在变更之前的历史上。
替代解决方案git rebase中提到琥珀的反应是使用移植机制(见的Git的定义在移植物的Git词汇表和文档.git/info/grafts文件在Git仓库布局文件)到提交更改父,检查它做正确的事情与一些浏览器的历史(gitk,git log --graph等等),然后使用git filter-branch(如其联机帮助页的“示例”部分中所述)将其永久化(然后删除嫁接,并可选地删除由git filter-branch或relone存储库支持的原始引用):
echo“ $ commit-id $ graft-id” >> .git / info / grafts git filter-branch $ graft-id..HEAD
注意 !!!该解决方案与rebase解决方案的不同之处在于,它git rebase可以对更改进行 rebase /移植,而基于嫁接的解决方案将仅按原样重新提交,而不考虑旧父母与新父母之间的差异!
git-replace+ git-filter-branch?在我的测试中,filter-branch似乎尊重替换,重新树了整棵树。
git filter-branch重写历史记录,尊重嫁接和替换(但是在重写的历史记录中,替换将无意义);推送将导致不方便的更改。git replace创建就地可转让替代品;推送将快速进行,但是您需要推送refs/replace以转移替换内容以更正历史记录。HTH
为了澄清以上答案,并毫不客气地插入我自己的脚本:
这取决于您是要“变基”还是“变基”。根据Amber的建议,rebase在diff周围移动。正如雅库布(Jakub)和克里斯(Chris)所建议的那样,一个父母在整个树的快照周围移动。如果您想复习,我建议使用而不是手动进行工作。git reparent
假设您的图片在左侧,并且您希望它看起来像右侧的图片:
C'
/
A---B---C A---B---C
重定基和重定基将产生相同的图像,但是对的定义C'不同。使用git rebase --onto A B,C'将不包含引入的任何更改B。使用git reparent -p A,C'将与相同C(除非B将不在历史记录中)。
reparent脚本;它工作得很漂亮;强烈推荐。我还建议创建一个新branch标签,并checkout在调用之前创建到该分支(因为分支标签将移动)。