If I work in a standard master-feature workflow, what'd be the difference between squashing a feature branch into master and cherry-picking it into master?
Example branches:
m1 -- m2 master
\-- f1 -- f2 feature
I think both have the same output ie
m1 -- m2 -- -- -- m3 master
\-- f1 -- f2 feature
With the cherry-pick command, Git lets you incorporate selected individual commits from any branch into your current Git HEAD branch. When performing a git merge or git rebase , all the commits from a branch are combined.
Squash merging is a merge option that allows you to condense the Git history of topic branches when you complete a pull request. Instead of each commit on the topic branch being added to the history of the default branch, a squash merge adds all the file changes to a single new commit on the default branch.
Merge squash merges a tree (a sequence of commits) into a single commit. That is, it squashes all changes made in n commits into a single commit. Rebasing is re-basing, that is, choosing a new base (parent commit) for a tree.
git cherry-pick is a powerful command that enables arbitrary Git commits to be picked by reference and appended to the current working HEAD. Cherry picking is the act of picking a commit from a branch and applying it to another. git cherry-pick can be useful for undoing changes.
There are two important differences between merge --squash
and cherry-pick
:
That is, if you have a the situation you described above and you (on master
) do a git cherry-pick feature
, the resulting branch will look like this:
m1 -- m2 -- f2’ master
\-- f1 -- f2 feature
This means the changes from f1
are not present on master (and cherry-picking possibly fails if f2
depends on them.
merge --squash
does not immediately commit, instead it creates a summary of all the changes and makes them ready to commit. This is essentially a patch of your complete branch changes, the same that git diff m1..feature
would show.
On my machine, a quick test gave this output:
$ test git:(master) git merge --squash testbranch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
The important bit here is the "not updating HEAD", which is git-speak for "I didn’t commit the stuff I did". The second sentence in fact is far more user-friendly…
This feature is handy if you like to develop step-by-step (by committing all the little steps that led to the solution, e.g. with a commit after every successful test run. In this case, your history would likely get cluttered with hundreds of one-liner commits. So it is probably better to do a merge-squash-commit every now and then (e.g. after you have developed one bit of the functionality).
Cherry-picking lets you apply the change another commit introduced to your current branch.
Merging with squashing (followed by committing) does the same as merging (with respect to the resulting working directory and index, not the history).
Here's an example (some output by git is not shown).
Setup
$ git init
$ echo sahred file > shared-file.txt
$ git add .
$ git commit -m 'added shared-file.txt in master branch'
$ git checkout -b dev
$ touch dev.txt
$ git add .
$ git commit -m 'added dev.txt in dev branch'
$ echo shared file > shared-file.txt
$ git add .
$ git commit -m 'fixed typo in shared-file.txt in dev branch'
$ git checkout master
We now have a branch dev
with an additional file (dev.txt
) and where the typo in shared-file.txt
is fixed. We switched back to the master
branch to compare cherry-picking and merging.
Cherry-picking
$ git cherry-pick dev
$ git log --oneline --graph
* 8019b05 (HEAD -> master) fixed typo in shared-file.txt in dev branch
* 7dbd3aa added shared-file.txt in master branch
$ ls
shared-file.txt
$ cat shared-file.txt
shared file
As you can see, after cherry-picking the change the last commit in the dev
branch introduced was applied (fixing the typo in shared-file.txt
) but the additional file dev.txt
was not carried over, because it was not created in the last commit in the dev
branch.
We now undo the cherry-pick and compare with the result of merging.
Merging (with squashing and committing)
$ git reset HEAD~1 --hard # undo cherry-pick
$ git merge dev --squash
$ git commit -m 'merged dev'
$ git log --oneline --graph
* 01dd755 (HEAD -> master) merged dev
* 7dbd3aa added shared-file.txt in master branch
$ ls
dev.txt shared-file.txt
$ cat shared-file.txt
shared file
As you can see, both the typo were fixed and dev.txt
was carried over. This is because merging with squashing (followed by committing) is the same as the regular merge of two commits with respect to the resulting working directory and index (but not the history of course).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With