将git父指针设置为不同的父级
如果我在过去提到了一个指向父母的承诺,但是我想改变它指向的父母,那么我该如何去做呢?
使用git rebase
。 在Git中,它是通用的“在commit(s)并将其放在不同的父(基)”命令上。
有些事情要知道,但是:
-
由于提交SHA涉及他们的父母,所以当你改变给定提交的父对象时,其SHA将改变 – 在开发线中的所有提交(比它更新)的SHA也将改变。
-
如果您正在与其他人一起工作,并且已经将公开提交的内容推送到了他们所在的位置,修改提交可能是一个糟糕的想法™。 这是由#1引起的,因此当试图找出由于SHA不再与“相同”提交相匹配而发生的事情时,其他用户的存储库将会遇到混淆。 (有关详细信息,请参阅链接的手册页上的“从上游重新启动”部分)。
也就是说,如果您目前正在某个分支上提交您想要移动到新父项的提交,则会看起来如下所示:
git rebase --onto <new-parent> <old-parent>
这会将当前分支中的<old-parent>
之后的所有内容移动到<new-parent>
之上。
如果事实certificate,你需要避免重新绑定后续的提交(例如,因为历史重写将是站不住脚的),那么你可以使用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 BA # …---o---A---o---o---… # \ # C---b---b---… # # C is the replacement for B.
在build立上述replace的情况下,对于对象B的任何请求将实际上返回对象C.除了第一父对象(相同的父对象(除了第一对外),同一棵树外,C的内容与B的内容完全相同,相同的提交消息)。
replace在默认情况下处于活动状态,但可以通过使用--no-replace-objects
选项(在命令名称之前)或通过设置GIT_NO_REPLACE_OBJECTS
环境variables来进行GIT_NO_REPLACE_OBJECTS
。 replace可以通过推送refs/replace/*
(除了正常的refs/heads/*
)来共享。
如果你不喜欢commit-munging(用上面的sed完成),那么你可以使用更高级别的命令创build你的replace提交:
git checkout B~0 git reset --soft A git commit -CB git replace B HEAD git checkout -
最大的区别在于,如果B是合并提交,则这个序列不会传播额外的父项。
请注意,在Git中更改提交需要更改其后的所有提交。 如果你已经发表了这部分历史,这是不鼓励的,有人可能会build立他们的历史上的工作,这是在变化之前。
在Amber的回应中提到的git rebase
替代解决scheme是使用移植机制(参见Git术语表中的Git移植的定义以及Git Repository Layout文档中的.git/info/grafts
文件的文档)来更改提交的父项,检查它是否正确的东西与一些历史浏览器( gitk
, git log --graph
等),然后使用git filter-branch
(如其手册页的“示例”部分所述),使其永久(然后删除嫁接,并可以删除由git filter-branch
或reclone repository备份的原始参考):
echo“$ commit-id $ graft-id”>> .git / info / grafts git filter-branch $ graft-id..HEAD
注意 !!! 这个解决scheme与rebase解决scheme的不同之处在于git rebase
会改变 /移植变化 ,而基于移植的解决scheme只是按照原样重新提交提交,而没有考虑到旧父母和新父母之间的差异!
为了澄清上面的答案,并无耻地插入我自己的脚本:
这取决于你是否想要“变基”或“重新”。 正如Amber所build议的,一个rebase会围绕diff进行移动。 正如Jakub和Chris所build议的那样,重新组合了整棵树的快照 。 如果你想重新提交,我build议使用git reparent
而不是手动完成工作。
对照
假设你有左边的图片,你想让它看起来像右边的图片:
C' / A---B---C A---B---C
重新装修和重新装修将产生相同的情况,但是C'
的定义不同。 使用git rebase --onto AB
, C'
不会包含由B
引入的任何更改。 使用git reparent -p A
, C'
将与C
相同(除了B
将不在历史中)。