Reputation: 5501
How can I tell cherry-pick to pick range of commits and squash it?
Or in other words, apply the diff between two commits to the current state of the repository?
The following does not work (cherry-pick has no --squash option):
git cherry-pick --squash e064480..eab48b59c
Note: My use case is within a subtree scenario - before anyone starts arguing that I should not squash.
The following works, but then I have a range of separate commits. I can squash them manually with interactive rebase afterwards.
git cherry-pick -X subtree=vendor/package e064480..eab48b59c
Is there any way to do the squashing as part of the cherry-pick?
Upvotes: 144
Views: 80611
Reputation: 1328682
From this 2022 discussion, there are other approaches:
git diff A...B | git apply --3wa
But,
git cherry-pick
" and there are conflicts, you can see the change in the context of the entire file.Johannes Sixt suggests an interactive rebase
To transplant the range
A..B
of the history on top of HEAD, for example, I'd start with (notice^0
afterB
, as I do not trust myself so I'd leave the true branchB
untouched as I may make mistakes while running rebase):$ git checkout --detach HEAD ;# this is only to use @{-1} later $ git rebase -i --onto HEAD A B^0
Then if my goal is to squash everything down into a single commit, then replace all 'pick', except for the first one, to 'squash'.
That will give me one single chance to edit a single commit message, but the editor buffer starts with the log message from all of the original, so I can pick good bits from them while writing new stuff. I'll have the result on detached HEAD.
If I like the result, I may update the branch I was originally on with it.$ git checkout -B @{-1}
Or, if I don't, perhaps because I made mistakes, then I can just discard it and go back to the original branch.
$ git checkout @{-1}
However, Noam Yorav-Raphael objects:
My main problem with using "
rebase -i
" is that it would require me to fix merge conflicts one by one, on each commit in which they appear, instead of fixing all conflicts at once, treating the change fromA
toB
as one.
It also requires manual editing for every commit betweenA
andB
.
Noam proposes:
I think that the best way to do what I want using the existing commands is:
git checkout A git merge --squash B git commit --no-edit git checkout @{2} # Go back to where we were at the beginning. # This is not exact, as you're in detached HEAD state. git cherry-pick --edit @{1} # cherry-pick the squashed commit A..B
This allows you to fix the merge conflicts in one go, shows the entire files causing the conflicts, and allows you to edit the commit message, starting with the descriptions of all the squashed commits.
I think this also gives a pretty good explanation of what "
cherry-pick --squash
" will do: it really is the analog of the "merge --squash
", but for cherry-pick.
Note that Rafał Cieślak suggests in the comments to skip the checkout and cherry-pick steps by performing a squash merge directly, which automatically stops before updating HEAD
.
So instead of:
A
).git merge --squash B
).Rafal, on the other hand, simplifies the process by directly using git merge --squash B
from the current branch.
That will:
B
into the current branch, but does not create a merge commit or update HEAD
.Squash commit -- not updating HEAD. Automatic merge went well; stopped before committing as requested.
The extra checkout and cherry-pick are unnecessary unless you need the resulting changes on a different branch.
If you stay on the target branch, there’s no need to detach HEAD
or cherry-pick. The squash merge alone prepares the changes for committing.
The staged changes from git merge --squash
are already on the branch where you ran the command. Committing them directly simplifies the workflow.
git merge --squash B
git commit -m "Squashed changes from branch B"
If you want the squashed changes on a branch other than the one you're currently on, then the cherry-pick step becomes necessary.
Upvotes: 10
Reputation: 642
git cherry-pick --no-commit <commit>…
.git cherry-pick --continue
.git status
shows Cherry-pick currently in progress.
loop from step 4.Upvotes: 4
Reputation: 495
Follow from master → cherry pick two commit from another branch
git checkout master
git cherry-pick :1
git cherry-pick :2
git reset --soft HEAD~2 (number of cherry pick commits, i.e 2 )
git add .
git commit
Upvotes: 16
Reputation: 2827
Useful alternative vs git cherry-pick -n
for some use cases might be git merge --squash
- for example, when you want to test a feature branch's changes on top of your integration branch, without rebasing.
Source: What's the difference between git merge --squash and git cherry-pick?
Upvotes: 10
Reputation: 19035
Pass -n
to git cherry-pick
. This will apply all the commits, but not commit them. Then simply do git commit
to commit all the changes in a single commit.
Upvotes: 196