在分支 temp 中,我创建了一个新分支 temp2 。我对文件进行了重排,然后对其进行了一些清理。我承诺。
后来我发现,如果我进行了重新整理,提交,然后进行清理,然后提交,那么我的历史将更加清晰。
换句话说,我只想将此提交分为两个阶段,两个提交。
好吧,我记得重新安排的部分。因此,我切换到分支 temp 并再次进行了重新排列,而没有进行清理。我的想法是我可以将提交从 temp2 提交到temp。
我所拥有的:
------ before ----- rearrangement <-- temp
\
\
\
------- rearrangement plus cleanup <-- temp2
我想要什么:
------ before ----- rearrangement ----- rearrangement plus cleanup <-- temp
首先,我尝试将 temp2 重新设置为 temp ,但是遇到合并冲突。这让我感到惊讶,因为它没有要求合并任何内容。我以为重新定基只是更改了附加提交的位置。所以我放弃了。
然后,我尝试将“重新安排+清理”从 temp2 挑选到 temp 。但是我 still 发生合并冲突。真的让我感到惊讶,因为我认为这是完全不可能的。所以我也放弃了。
我被困住了。我不明白这里发生了什么。 git不是基于差异的。提交只是快照。那么为什么会有冲突呢?我要做的只是按一定顺序排列这些快照。为什么这么难,我该怎么办?
答案 0 :(得分:2)
还没有在命令行中尝试过,但是步骤应该相似:
rearrangement plus cleanup
提交。答案 1 :(得分:2)
使用:
git checkout temp
hash=$(git log --no-walk --pretty=format:%B temp2 | git commit-tree -p temp temp2^{tree})
git merge --ff-only $hash
将提交C
复制到新的提交C'
,该提交再次使用日志消息和C
的树,然后调整分支名称temp
指向新的提交。
我以为重新定位只是在附加提交的地方发生了变化...
那是关键错误!
如果我拿起您的图表并缩小名称,我们将从以下内容开始:
A--B <-- temp
\
C <-- temp2
git checkout temp2; git rebase temp
所做的(或试图做的)是将提交C
复制到新的提交C'
,该提交在某种程度上与C类似,但具有B
作为其父母。如果成功,最终结果将是:
C' <-- temp2
/
A--B <-- temp
\
C [abandoned]
我想你已经知道了;您错在哪里是如何复制的想法。
git rebase
的本质上是运行git cherry-pick
。 git cherry-pick
所做的是-从逻辑上讲;潜在的机制是使用Git的合并机制-将提交转化为更改,然后将该更改应用于其他地方。因此,Git将比较(差异)提交C
与提交A
,然后比较差异提交B
与A
,并尝试将两个变更集组合以产生提交C'
上方B
。当然,这两个变更集重叠,这就是为什么您会发生合并冲突的原因。
您没有使用git rebase
来实现这一目标。由于C
已经是您想要的最终结果,因此您可以简单地将C的快照以及可能的提交消息复制到新的提交C'
中。没有面向用户的Git命令可以执行此操作,但是有一个底层命令可以执行此操作。这些低级命令就是Git所说的 plumbing 命令。
尤其是,git commit-tree
写入一个提交对象。为此,它需要一个现有的 tree 对象(快照)以及一些父哈希集。通常,我们可以使用管道命令git write-tree
来创建一棵树,该命令会写出当前索引,但是在现有的提交C
中我们已经有一个非常合适的最终结果。我们只需要获取它的树哈希ID。 gitrevisions syntax是temp2^{tree}
,因为名称temp2
指向提交C
。
我们希望提交提交C
的副本的父级当然是提交B
。名称temp
就足够了,因为它现在指向提交B
。我们还必须在标准输入或作为参数或文件的情况下向git commit-tree
提供提交日志消息。 git log --no-walk --pretty=format:%B
将从给定的提交中获取日志消息。
一旦我们创建了新的提交对象,就必须在宽限期内(对于git gc
/ git prune
)抓紧时间,并为其命名一个 name ,默认为14天。为此,我们可以使用git merge --ff-only
扩展当前分支名称(即temp
)。
(请注意,只要您确定git log ... | git commit-tree
都可以使用,就可以将整个操作作为一个单行表达式来完成。)