How to inject a commit between some two arbitrary commits in the past?

GitGit Rebase

Git Problem Overview


Suppose I have the following commit history on my local-only branch:

A -- B -- C

How do I insert a new commit between A and B?

Git Solutions


Solution 1 - Git

It's even easier than in OP's answer.

  1. git rebase -i <any earlier commit>. This displays a list of commits in your configured text editor.
  2. Find the commit you want to insert after (let's assume it's a1b2c3d). In your editor, for that line, change pick to edit.
  3. Begin the rebase by closing your text editor (save your changes). This leaves you at a command prompt with the commit you chose earlier (a1b2c3d) as if it has just been committed.
  4. Make your changes and git commit (NOT amending, unlike most edits). This creates new a commit after the one you chose.
  5. git rebase --continue. This replays the successive commits, leaving your new commit inserted in the correct place.

Beware that this will rewrite history, and break anyone else who tries to pull.

Solution 2 - Git

Turns out to be quite simple, the answer found here. Suppose you're on a branch branch. Perform these steps:

  • create a temporary branch from the commit after you want to insert the new commit (in this case commit A):

     git checkout -b temp A
    
  • perform the changes and commit them, creating a the commit, let's call it N:

     git commit -a -m "Message"
    

(or git add followed by git commit)

  • rebase the commits you want to have after the new commit (in this case commits B and C) onto the new commit:

     git rebase temp branch
    

(possibly you need to use -p to preserve merges, if there were any - thanks to a no longer existing comment by ciekawy)

  • delete the temporary branch:

     git branch -d temp
    

After this, the history looks as follows:

A -- N -- B -- C

It is of course possible that some conflicts will appear while rebasing.

In case your branch is not local-only this will introduce rewriting history, so might cause serious problems.

Solution 3 - Git

Even easier solution:

  1. Create your new commit at the end, D. Now you have:

     A -- B -- C -- D
    
  2. Then run:

     $ git rebase -i hash-of-A
    
  3. Git will open your editor and it will look like this:

     pick 8668d21 B
     pick 650f1fc C
     pick 74096b9 D
    
  4. Just move D to the top like this, then save and quit

     pick 74096b9 D
     pick 8668d21 B
     pick 650f1fc C
    
  5. Now you will have:

     A -- D -- B -- C
    

Solution 4 - Git

Here's a strategy that avoids doing an "edit hack" during the rebase seen in the other answers I've read.

By using git rebase -i you obtain a list of commits since that commit. Just add a "break" at the top of the file, this will cause the rebase to break at that point.

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

Once launched, git rebase will now stop at the point of the "break". You can now edit your files and create your commit normally. You can then continue the rebase with git rebase --continue. This may cause conflicts you'll have to fix. If you get lost, don't forget you can always abort using git rebase --abort.

This strategy can be generalised to insert a commit anywhere, just put the "break" at the spot where you want to insert a commit.

After rewriting history, don't forget to git push -f. The usual warnings about other people fetching your branch apply.

Solution 5 - Git

Assuming that the commit history is preA -- A -- B -- C, if you want to insert a commit between A and B, the steps are as follows:

  1. git rebase -i hash-of-preA

  2. Git will open your editor. The content may like this:

     pick 8668d21 A
     pick 650f1fc B
     pick 74096b9 C
    

Change the first pick to edit:

    edit 8668d21 A
    pick 650f1fc B
    pick 74096b9 C

Save and Exit.

  1. Modify your code and then git add . && git commit -m "I"

  2. git rebase --continue

Now your Git commit history is preA -- A -- I -- B -- C


If you encounter a conflict, Git will stop at this commit. You can use git diff to locate conflict markers and resolve them. After resolving all conflicts, you need to use git add <filename> to tell Git that the conflict has been resolved and then rerun git rebase --continue.

If you want to undo the rebase, use git rebase --abort.

Solution 6 - Git

Many good answers here already. I just wanted to add a "no rebase" solution, in 4 easy steps.


Summary

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

Explanation

(Note : one advantage of this solution is that you don't touch your branch until the final step, when you're 100% sure you're OK with the end result, so you have a very handy "pre-confirmation" step allowing for AB testing.)


Initial state (I've assumed master for your branch name)

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

1) Start by pointing HEAD at the right place

git checkout A

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

(Optionnally here, rather than detaching HEAD, we could have created a temporary branch with git checkout -b temp A, which we would need to delete at the end of the process. Both variants work, do as you prefer since everything else remains the same)


2) Create the new commit D to be inserted

# 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) Then bring copies of the last missing commits B and C (the command would be the same even if there were more commits in between, since this is picking a range of commits)

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)

( comfortable AB Testing here if needed )

Now is the moment to inspect your code, test anything that needs to be tested, and you can also diff / compare / inspect what you had and what you would get after the operations, simply by checking alternatively C or C'.


4) Depending on your tests between C and C', either it is OK or it is KO.

(EITHER) 4-OK) Finally, move the ref of master

git branch -f master HEAD

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

(OR) 4-KO) Just leave master unchanged

If you created a temporary branch, just delete it with git branch -d <name>, but if you went for the detached HEAD route, no action needed at all at this point, the new commits will be eligible for garbage collection just after you reattach HEAD with a git checkout master

In both those cases (OK or KO), at this point just checkout master again to reattach HEAD.

Solution 7 - Git

Given that the commit you want to insert is identified by 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBartoszKPView Question on Stackoverflow
Solution 1 - GitSLaksView Answer on Stackoverflow
Solution 2 - GitBartoszKPView Answer on Stackoverflow
Solution 3 - GitMatthewView Answer on Stackoverflow
Solution 4 - GitaxerologementyView Answer on Stackoverflow
Solution 5 - GithaoleeView Answer on Stackoverflow
Solution 6 - GitRomain ValeriView Answer on Stackoverflow
Solution 7 - GitMateja PetrovicView Answer on Stackoverflow