How to squash commits which have merge-commit in between?
Solution 1
You can rebase -i
starting with commit 2
's parent (that is, the commit on master
that you branched from. You'll likely have to re-resolve conflicts when you get to the merge commit.
So if your history looks like
* D commit 3 (HEAD)
* M merge
/|
| * C commit 2
* | B commit on master
|/
* A (master)
Start with git rebase -i A
. You'll see a list of commits including both master
and your_branch
, but not the merge commit. pick
the first one (B
or C
, depending on timing) and squash
the rest.
Solution 2
Assuming the feature branch is called feature
and the main branch main
:
Create a temporary branch from main
:
git checkout -b temp main
Squash the feature
branch in:
git merge --squash feature
Commit the changes (the commit message contains all squashed commit messages):
git commit
Go back to the feature
branch and point it to the temp
branch:
git checkout feature
git reset --hard temp
Delete the temporary branch:
git branch -d temp
Solution 3
You can use the tool I've created specifically for this task:
https://github.com/sheerun/git-squash
It's only necessary to merge master branch, and then run squashing command:
git merge master
git squash master
Solution 4
The only way I have found to not have to re-resolve conflicts is this:
Given branch main and branch work, perform the following steps:
git checkout -b work-squashed `git merge-base main work`
This creates a new branch from the last main commit you merged into the work branch.
git diff work-squashed...work | patch -p1
This grabs and applies to the working directory all the changes between the last commit on main that was merged into work and the tip of the work branch. In other words, all the work, including the resolved conflicts.
At this point you need to take care of files added/removed on the work branch, because patch is not git. It doesn't know what files are being tracked by git. So, you need to git add/git rm until all the files are accounted for. Then, you simply commit the changes as a single commit.
Solution 5
In my case, I started working with a branch that had several commits, then a merge with the main/source branch, then more commits and I wanted to squash all commits, but kept running into an error because of the merge commit:
error: commit is a merge but no -m option was given.
->C1->C2->M(merge with source branch)->C3->C4
There's probably a better way (and I look forward to learning), but what I ended up doing after much reading and trial and error was creating a copy branch for reference, then reverting the current branch to C1,
reset --hard (C1 hash)
then cherry-picking C2, C3, C4, then squashing, then rebasing ... resulting in:
M->C
(just one commit that has been rebased with source!)
I hope this helps someone else with the same problem.
![Tyr1on](https://i.stack.imgur.com/9xTun.jpg?s=256&g=1)
Tyr1on
Updated on November 04, 2021Comments
-
Tyr1on over 2 years
I am working on a feature branch.
- Made several commits. Squashed commits.
- Pushed changes to remote branch. Got conflicts.
- Merged changes from master, resolved conflicts on feature branch.
git fetch origin master
git merge FETCH_HEAD
- Resolved conflicts manually.
git commit
git push
- I made one more commit.
So, current commit history looks like this. From current to old:
- commit 3
- commit M yyy (Merged)
- commit 2
How do I squash above 3 commits into 1 before I merge my feature branch to master?
-
Tyr1on about 9 yearsIt worked. But Can you point me to the "theory" why it worked?
-
Kristján about 9 yearsAbsolutely, check out this article and comment again if you've got some specific questions.
-
Kristján about 9 yearsLooking again (and playing with a toy repo), it looks like
git rebase -i master
should also have worked just fine, and it wouldn't have included themaster
commit in yoursquash
. Is that what you were trying before? What was going wrong? -
Kristján about 9 yearsOne more thing - mixing merges and rebases is generally a good way to get confused in your Git tree. I'd recommend sticking to just one or the other, and since you want to squash, that means avoiding merge in favor of rebase. In this case, you would have
git rebase master
d instead ofgit merge master
, which would hoist each branch commit up as if you'd started fromcommit 2
. Then when you finally squash, there's no merge to tangle you up. However, if you've pushed the branch to other people, rebase requires a force push and breaks their history, so tradeoffs. -
Kristján about 9 yearsHappened upon yet another way that might interest you:
git rebase --preserve-merges origin/master
from stackoverflow.com/questions/4783599/rebasing-a-git-merge-commit -
fgblomqvist over 3 yearsThis is a winner in my books. However, it would be neat if you could also include the commands/procedure you do here and explain what it does. It's really quite simple when you look at the code, but would be nice to have it right here :)
-
OwnageIsMagic over 2 yearsyou can use
git apply
instead ofpatch
-
PinguinoSod over 2 yearsthis is genius, thanks a lot!
-
pmishev almost 2 years...and
git push --force
in the end