The Short Answer
If you didn't have the merge commits, you could just simply use an interactive rebase, as pointed out in Combining multiple commits before pushing in Git.
You can get rid of the merge commits as well by simply rebasing or cherry-picking the commits unique to your branch on top of master, since the final state of your code will be equivalent to having done a merge.
To cleanup your branch, the following solution will combine rebasing and cherry-picking commits to remove the merge commits, then interactive rebasing to squash the others.
Rebase and Cherry-pick Commits On Top of Master
We'll create a temporary branch to keep the current state of dev
, then rebase the parts of dev
before the merges with master
on top of master
, which is equivalent to having merged master
in the first place:
# Create temporary branch to save current state of dev
git branch temp dev
# Temporarily reset dev to state before the merges
git reset --hard 2e05a2fc
# Rebase dev onto master
git rebase master
Next, cherry-pick the remaining commits from temp
. We'll use a commit range, where the start of the range is exclusive (i.e. is not actually included):
git cherry-pick 035a72fd..7f75hsyfr
Squash Commits with Interactive Rebase
Now you're ready to squash commits with interactive rebase. Find the rewritten commit that corresponds to 60c9daaf
in your example. Let's call that commit X
. Then
git rebase -i X^
X^
represents the base commit of the rebase, which won't actually be changed. Every descendant commit, however, will not appear in the interactive rebase editor, in order of oldest to newest:
pick X changes to file1
pick Y changes to file2
pick Z changes to file3
pick AA more changes to file2
pick AB changes to file1
Reorder the commits so that commits changing a file are adjacent
pick X changes to file1
pick AB changes to file1
pick Y changes to file2
pick AA more changes to file2
pick Z changes to file3
Then add squash
or s
to each following commit that you want to squash into the previous one:
pick X changes to file1
squash AB changes to file1
pick Y changes to file2
squash AA more changes to file2
pick Z changes to file3
When you're done, save and exit the editor, and the rebase will continue unless there are conflicts, in which case you'll need to resolve each conflict and continue the rebase.
Verify Results
Now you'll want to verify your results to make sure that the changes you've made are still equivalent to the state of your branch before you started to make modifications:
git diff temp
If there is no output, then the final state of your branch is the same as before you started modifying it, so now you may delete temp
with git branch -D temp
.
See Also