Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git squash and merge

Tags:

git

merge

squash

Could anyone please tell me what the difference between these two commands:

git merge --squash

and

git merge --no-ff 
like image 977
Green Ho Avatar asked May 12 '14 18:05

Green Ho


People also ask

Is it better to squash and merge?

You should consider using squash if your team prefers a linear project history. This means that the history held by your main branch should not contain merges. A squash merge makes it possible to keep changes condensed to a single commit, supporting this strategy nicely.

How do you squash commit and merge?

To enable commit squashing as the default option in your repository: Navigate to your chosen repository and open the Settings sub-tab. Open the General Settings page. Check the box for Squash commits on merge default enabled.

What is a Git squash?

What does it mean to squash commits in Git? Squashing is a way to rewrite your commit history; this action helps to clean up and simplify your commit history before sharing your work with team members. Squashing a commit in Git means that you are taking the changes from one commit and adding them to the Parent Commit.


1 Answers

I think your question indicates a bit of a misunderstanding. --no-ff and --squash are not opposites, rather they are subtly different operations. Keep that in mind while reading.

git merge --squash

The help page for merge says the following about --squash:

--squash and --no-squash

Produce the working tree and index state as if a real merge happened (except for the merge information), but do not actually make a commit or move the HEAD, nor record $GIT_DIR/MERGE_HEAD to cause the next git commit command to create a merge commit. This allows you to create a single commit on top of the current branch whose effect is the same as merging another branch (or more in case of an octopus).

With --no-squash perform the merge and commit the result. This option can be used to override --squash.

That's a bit confusing, and requires some knowledge about the internals of git. To start, we need to understand the difference between a regular commit and a merge commit. A regular commit has one parent, and is simply a changeset to apply to the commit before it:

A --> B --> C

A merge commit has multiple parents, and it a place in the tree where you've brought two or more lineages together:

A --> B --> F
           /
C --> D - /

See how A, B, C, and D are regular commits, but F is a merge commit, since it has multiple parents (B and D)? This is what git merge --no-ff would produce. It forces Git to create a merge commit to bring two histories together.

git merge --squash would do something a little different. It prevents Git from creating a merge commit, but still pulls in the changes C and D made, so your tree looks like this:

A --> B --> F'

C --> D 

F' contains changes C and D made, but there's no sign of the fact you merged two trees in the repository.

git merge --no-ff

--no-ff is a slightly different operation. It forces Git into creating a merge commit even if it's not really necessary. For reference, here's what the manual has to say about --no-ff and it's opposite --ff-only:

--no-ff

Create a merge commit even when the merge resolves as a fast-forward.

--ff-only

Refuse to merge and exit with a non-zero status unless the current HEAD is already up-to-date or the merge can be resolved as a fast-forward.

To understand, it's best to look at an example:

A --> B --> C --> D --> E
      |                 |
    'master'          'topic'

If you had this tree, were on the master branch, and ran git merge, Git would perform what's called a "fast-forward" merge. Since there's no divergence between the two histories, Git can just move the master branch up to where topic sits without doing anything interesting. It would look like this:

A --> B --> C --> D --> E
                        |
                     'topic'
                     'master'

With both topic and master pointing to the same branch. Now, some workflow policies require that you create a merge commit every time you merge back into master. This keeps the history of branches around. You'll get arguments either way on how it should be done, but I won't get into those here.

If you used git merge --no-ff on that same tree instead, it would force git to create a merge commit, giving you a tree like this:

                   'master'
                       |
A --> B -------------> F
       \              /
        C --> D --> E
                    |
                 'topic'

Where F is the new merge commit --no-ff force Git to create.

like image 149
Collin Avatar answered Oct 28 '22 08:10

Collin