假设我在本地分支上有以下提交历史:

A -- B -- C

如何在a和B之间插入新的提交?


当前回答

假设提交历史是preA——A——B——C,如果你想在A和B之间插入一个提交,步骤如下:

git rebase -i hash- prea Git会打开你的编辑器。内容可能是这样的: 选A 选B 选C 更改第一个选择编辑: 编辑8668d21 选B 选C 保存并退出。 修改你的代码,然后git添加。&& git commit -m "I" Git rebase—继续

现在你的Git提交历史是preA—A—I—B—C


如果遇到冲突,Git将在此提交时停止。您可以使用git diff来定位冲突标记并解决它们。在解决所有冲突后,您需要使用git add <filename>来告诉git冲突已经解决,然后重新运行git rebase——continue。

如果你想撤销rebase,使用git rebase——abort。

其他回答

这里已经有很多好答案了。我只是想在4个简单的步骤中添加一个“无rebase”解决方案。


总结

git checkout A
# <<< modify your files at this point
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD

解释

(注意:这种解决方案的一个优点是,直到最后一步,当你100%确定你对最终结果满意时,你才会接触你的分支,所以你有一个非常方便的“预确认”步骤,允许AB测试。)


初始状态(我假设分支名为master)

A -- B -- C <<< master <<< HEAD

1)首先将HEAD指向正确的位置

git checkout A

     B -- C <<< master
    /
   A  <<< detached HEAD

(在这里,我们可以选择使用git checkout -b temp a创建一个临时分支,而不是分离HEAD,我们需要在进程结束时删除它。这两种变体都可以工作,做你喜欢的,因为其他一切都保持不变)


2)创建要插入的新提交D

# at this point, make the changes you wanted to insert between A and B, then

git commit -am "Message for commit D"

     B -- C <<< master
    /
   A -- D <<< detached HEAD (or <<< temp <<< HEAD)

3)然后带来最后一个丢失的提交B和C的副本(即使中间有更多的提交,命令也会是相同的,因为这是在选择一个提交范围)

git cherry-pick A..C

# (if any, resolve potential conflicts between D and these last commits)

     B -- C <<< master
    /
   A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)

(如果需要,可以在这里进行AB测试)

现在是检查代码的时候了,测试任何需要测试的东西,你还可以通过检查C或C'来区分/比较/检查你所拥有的和你在操作后将得到的。


4)根据你在C和C'之间的测试,它是OK还是KO。

(EITHER) 4-OK)最后,移动master的ref

git branch -f master HEAD

     B -- C <<< (B and C are candidates for garbage collection)
    /
   A -- D -- B' -- C' <<< master

(OR) 4-KO)保持master不变

如果你创建了一个临时分支,只需使用git branch -d <name>删除它,但如果你选择分离HEAD路由,此时根本不需要任何操作,新的提交将在你用git checkout master重新连接HEAD后符合垃圾收集的条件

在这两种情况下(OK或KO),此时只需再次签出master以重新连接HEAD。

假设你想要插入的提交是由D标识的:

# Temporarily append the commit you want to insert to the end 
git cherry-pick D
# Results in D -- A -- B -- C

# Start interactive rebase
git rebase -i B^
# Let's imagine that this is what the rebase prompt looks like:
# pick B Third commit
# pick A Second commit
# pick D First commit
# Then reorder the commits:
# pick B Third commit
# pick D First commit
# pick A Second commit
# Save and exit
# After completing the rebase you will find
# A -- D -- B -- C

假设提交历史是preA——A——B——C,如果你想在A和B之间插入一个提交,步骤如下:

git rebase -i hash- prea Git会打开你的编辑器。内容可能是这样的: 选A 选B 选C 更改第一个选择编辑: 编辑8668d21 选B 选C 保存并退出。 修改你的代码,然后git添加。&& git commit -m "I" Git rebase—继续

现在你的Git提交历史是preA—A—I—B—C


如果遇到冲突,Git将在此提交时停止。您可以使用git diff来定位冲突标记并解决它们。在解决所有冲突后,您需要使用git add <filename>来告诉git冲突已经解决,然后重新运行git rebase——continue。

如果你想撤销rebase,使用git rebase——abort。

更简单的解决方案:

在最后创建你的新commit, d。现在你有: A——b——c——d 然后运行: $ git rebase -i hash- a Git会打开你的编辑器,它看起来是这样的: 选B 选C 选D 只需要像这样将D移到顶部,然后保存并退出 选D 选B 选C 现在你将拥有: A d b c

这里有一个策略,可以避免在我读过的其他答案中看到的重基过程中进行“编辑黑客”。

通过使用git rebase -i,您可以获得自该提交以来的提交列表。 只需在文件顶部添加一个“break”,这将导致rebase在这一点上中断。

break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>

一旦启动,git rebase现在将在“中断”点停止。 现在可以正常编辑文件并创建提交了。 然后,你可以使用git rebase——continue继续进行重基。这可能会导致你必须解决的冲突。如果你迷路了,不要忘记你可以使用git rebase中止。

这个策略可以推广到任何地方插入一个提交,只要把“break”放在你想要插入一个提交的地方。

重写历史之后,别忘了git push -f。关于其他人获取你的分支的通常警告适用。