如何交换Git提交的两个父级的顺序?
合并提交是至少有两个父级的提交。这些父母是按特定顺序排列的 如果我当前在分支如何交换Git提交的两个父级的顺序?,git,Git,合并提交是至少有两个父级的提交。这些父母是按特定顺序排列的 如果我当前在分支主机,并且我合并到分支功能,我将创建一个新的提交,其第一个父级是来自主机的提交,第二个提交是来自功能的提交。通过运行git log--first parent,这个顺序尤其明显 * The merge commit |\ | * The commit from `feature` * | The commit from `master` 假设我现在意识到顺序是错误的:我打算通过运行git checkout功能将分支
主机
,并且我合并到分支功能
,我将创建一个新的提交,其第一个父级是来自主机
的提交,第二个提交是来自功能
的提交。通过运行git log--first parent
,这个顺序尤其明显
* The merge commit
|\
| * The commit from `feature`
* | The commit from `master`
假设我现在意识到顺序是错误的:我打算通过运行git checkout功能将分支master
合并到feature
;git合并主机
。我想交换合并提交的父级顺序,但我不想再次经历解决所有合并冲突的麻烦。我该怎么做
* The merge commit
|\
* | The commit from `feature`
| * The commit from `master`
一种方法是模拟合并。那我们怎么做呢 假设您有如下提交图:
* (master) Merge branch 'feature'
|\
| * (feature) feature commit
* | master commit
. .
. .
. .
不用找了
我们想将master
合并到feature
中,但我们想保留更改,因此首先我们切换到master
,从中“手动”更新头部参考,以指向feature
,同时不更改工作树
git checkout master
git symbolic-ref HEAD refs/heads/feature
symbolic ref
命令类似于git签出功能
,但不触及工作树。因此,master
中的所有更改都将保留
撤消旧的合并
现在,我们在工作树中有了来自合并的所有更改。因此,我们通过重置master
继续“撤消”合并。如果您不愿意将引用丢失到合并提交上,可以创建一个临时标记或分支
(如果要保留提交消息,现在是将其复制到保存位置的好时机。)
现在,您的提交树应该与合并之前一样
假装合并
这是令人讨厌的部分。我们想骗git相信我们正在合并。我们可以通过在.git
文件夹中手动创建一个MERGE\u HEAD
文件来实现这一点,该文件包含要合并的提交的哈希值。所以我们这样做:
git rev-parse master > .git/MERGE_HEAD
如果您使用的是GitBash,git现在会告诉您它当前正在合并
要完成合并,我们只需提交
git commit
# Enter your commit message
一切都结束了。我们重新创建了合并提交,但使用了交换的父级。因此,您提交的历史现在应该如下所示:
* (feature) Merge branch 'master'
|\
| * (master) master commit
* | feature commit
. .
. .
. .
如果您需要任何进一步的信息,请毫不犹豫地询问。事实上,我最近学到了一个非常酷的命令,它可以完全满足您的需要:
git commit-tree -p HEAD^2 -p HEAD^1 -m "Commit message" "HEAD^{tree}"
这将基于当前的HEAD创建一个新的commit,但假设它的父对象是HEAD^2,HEAD^1(注意,这是相反的顺序)
git commit tree将新版本打印为输出,因此您可以将其与git重设结合使用:
git reset --hard $(git commit-tree -p HEAD^2 -p HEAD^1 -m "New commit message" "HEAD^{tree}")
更新-从来没有那么容易,是吗 我发现我建议的原始指令中存在一个缺陷:当使用路径参数执行
签出时,您可能希望从工作路径中删除一个文件,因为它不在要签出的提交中;但是你可能会感到惊讶
与任何历史重写一样,值得注意的是,您可能不应该这样做(使用这些答案中的任何一种方法,包括这一种)来进行您已经推动的任何合并。也就是说
前面的答案很好,但如果您希望避免使用(或需要知道)管道命令或其他git内部工作-如果这比使用Jared的解决方案那样的一行程序更重要-那么这里有一个替代方案:
这个解决方案的整体结构与zeeker的类似,但当他使用管道命令操纵HEAD或“技巧”git完成合并时,我们将只使用瓷器
就像以前一样
* (master) Merge Commit
| \
| * (feature) fixed something
* | older commit on master
让我们开始:
1)标记旧合并
git checkout feature
git commit
git tag -d badmerge
我们实际上会使用这个标签。(如果你想写下缩写的SHA1,那就行了;但这里的要点是让修复程序更友好,所以…)
现在我们有了
* (master) [badmerge] Merge Commit
| \
| * (feature) fixed something
* | older commit on master
2)将合并从硕士历史中删除
git branch -f master master^
很简单:
* [badmerge] Merge Commit
| \
| * (feature) fixed something
* | (master) older commit on master
3)开始新的合并
git checkout feature
git commit
git tag -d badmerge
如果我们现在运行git merge master
,我们知道合并将失败;但我们不想重做手动冲突解决。如果我们有办法将badmerge中的数据覆盖到新的合并提交上,那么我们就都准备好了。。。而我们做到了
要启动合并(a)而不创建要清理的冲突状态,但(b)保持合并提交处于打开状态,以便我们可以“修复”它:
4)覆盖来自badmerge的已解析/合并数据
git checkout feature
git commit
git tag -d badmerge
确保我们在repo的根目录中,然后我们可以执行git checkout badmerge--.
(注意,我们提供了一个路径(.)到git checkout
,因此它只更新工作树和索引);但这实际上可能是个问题
如果合并导致文件被删除(但它们现在在我们的工作树中),那么上面的命令将不会删除它们。因此,如果我们不确定,我们有几个选项可以避免这种可能性:
我们可以先清理工作树。。。但我们确实需要小心不要删除.git文件夹,并且可能需要对忽略文件中的任何内容进行特殊处理
在简单的情况下-.git是唯一的。-文件,没有任何内容被忽略-我们可以这样做
rm -rf *
git checkout badmerge -- .
或者,如果这看起来太危险,另一种方法是跳过rm
,执行git checkout-badmerge--.
,然后与badmerge进行比较,看看是否有什么需要清理
5)完成合并
git checkout feature
git commit
git tag -d badmerge
您可能希望使用(例如,git replace--graft HEAD ^2 HEAD ^1
)而不是重写历史记录。它创建一个替换并提交
编辑:<