Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between git merge --squash and git cherry-pick?

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
like image 397
Botel Vyvten Avatar asked Jan 28 '15 10:01

Botel Vyvten


People also ask

What is the difference between cherry-pick and merge?

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.

What is the difference between squash and merge?

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.

What is the difference between merge -- squash and rebase?

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.

What is Git cherry-pick used for?

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.


2 Answers

There are two important differences between merge --squash and cherry-pick:

1. Cherry-picking only moves one commit

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.

2. Cherry-picking creates a commit

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).

like image 171
Andreas Wolf Avatar answered Oct 16 '22 03:10

Andreas Wolf


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).

like image 44
timgeb Avatar answered Oct 16 '22 03:10

timgeb