1

I have a branch with several commits:

A --- B --- C --- D
                  ^
                 HEAD

I have finished to work on this branch and preparing the pull request. But I am not satisfied with the current history:

  • C is modifying a lot files that would have been better to commit into A and B
  • D is fixing some independent issues and should be kept as

I am looking for a clean way to split C into A and B without breaking D.

Puck
  • 2,080
  • 4
  • 19
  • 30
  • Does this answer your question? [How can I split up a Git commit buried in history?](https://stackoverflow.com/questions/4307095/how-can-i-split-up-a-git-commit-buried-in-history) – Enrico Campidoglio Aug 13 '20 at 12:54
  • @EnricoCampidoglio Partially but I don't want to split C into 2 new commits, I want to move the changes commited in C into commits A and B. – Puck Aug 13 '20 at 13:07

2 Answers2

1

You'll need to rebase twice:

  • First, run interactive rebase with git rebase -i HEAD^4
  • In the editor window, find the commit that you want to split and replace the first word pick with e (short for edit)
  • Save and exit editor, the rebase will get you after the commit you set for editing
  • Reset the commit with git reset HEAD~
  • Now your changes are unstaged and you can commit them as you wish
  • Using git add -p can be useful to add only some changes in a file
  • Create two commits, one to be merged in A and the other to be merged in B
  • git rebase --continue

Now, you've split your commit into two, the history should look like that:

A --- B --- Ca --- Cb --- D
                          ^
                        HEAD

You can now start the second rebase to merge Ca into A and Cb into B:

  • git rebase -i HEAD^4
  • In the editor, move the line of the commit Ca under the line of the commit A and change pick to s (short for squash).
  • Move the line of Cb under the line of the commit B and also set it to squash into B
  • git rebase --continue
Puck
  • 2,080
  • 4
  • 19
  • 30
1

I would create a temp branch on C that I will play with, let's suppose that main is on D (not specified, but):

git branch -b temp C
# let's go back to A, keeping _all_ of C's content so you can decide what to keep:
git reset --soft A
# now get the files the way you would like to get it for A (you can compare with A and B, just so that you _really_ get what you want.
# when you are done
git commit -m "new A"
# temp is on the new A

# let's go back to C
git checkout C
# let's put it on top of temp
git reset --soft temp
# Get the files you want the to be on B (compare with HEAD~1 and B.. and C)
# when you are done
git commit -m "new B"
# let's put temp where we are
git branch -f temp

# finally, let's move whatever is left over from C that is not part of B
git checkout C
git reset --soft temp
git commit -m "New C"
git branch -f temp
# at this moment C and temp are exactly the same

git checkout temp
git cherry-pick D
# temp is like the branch you want

... and that's it.

If actually C is what you would like B to be like, with some changes on A, then follow the recipe about "new A", then

git checkout C
git reset --soft temp
git commit -m "New B"
git branch -f temp
git checkout temp
git cherry-pick D
eftshift0
  • 26,375
  • 3
  • 36
  • 60