-
Git merge —squash
There is a flag to the
git mergecommand:--squashwhich at first looks as if it does the same thing as a rebase, but this isn’t exactly so. This post attempts to explain the difference.Consider the following repository structure:

There is a master branch and two feature branches feat1 and feat2. The feat2 branch will introduce a merge conflict, which is why it is highlighted in red.
Say you want to bring the two features into the master branch as a single change. There are, as you would expect with Git, multiple ways of doing this.
Merging
One way to do that is by merging the two branches into the master. From the master branch you can just
git merge feat1 feat2(yes, you can merge multiple branches in one step) You will get an error because of the merge conflict, which you should know how to fix by now, and this will give you this structure:
Rebasing
That’s fine if you want to be able to show that the merges have happened, but sometimes, that isn’t the case and you want to be able to show a clean history and a single change. To do that, you can use the rebase command. First by rebasing feat1 onto feat2
git checkout feat1; git rebase feat2
And then you can rebase this branch onto the master with
git rebase --interactive, where you can squash the three individual commits to a single commit, and resolve the merge conflict introduced by feat2:
And now, switch to the master branch and rebase it onto the feat1 branch (or merge it, the result will be the same as it will resolve as a fast-forward merge). This will give you a single commit with a clean history and all the merged changes from feat1 and feat2:

git merge —squash
Now to the topic of this post; using the
--squashflag withgit merge. What this does is similar to an interactive rebase where you squash all of the commits, except it doesn’t check in the change, it just updates the index (and by extension, the working directory). So, starting from the original repository structure we can get the same result by firstgit merge --squash feat1 feat2and fixing the merge conflict. This won’t affect the the checked in state of the master branch, but your index will have all the changes:
What’s the advantage of this? Well, you can get a clean history by just commiting the changes:

But, even more interestingly, you can now back out the changes from the index (which leaves them in the working directory) and add back selective changes using
git add --patchand create an entirely different set of commits.Which should you use?
For most cases, merging would be the choice of most people. It’s simple enough, and if there aren’t many micro commits the history will be coherent. In this example it only takes three steps (1. merge 2. fix conflicts 3. commit).
If you are the sort that makes many micro commits (like me) and you then rebase them into larger chunks before merging the changes to another branch, then the rebasing method is the one you are already using, even if you squash all the changes into a single commit. I prefer this method, because you have greater flexibility with what changes you make. I also find it easier to handle merge conflicts this way. But it does take longer (1. rebase feat1 onto feat2 2. Interactive rebase 3. Amend the commit messages 4. fix conflicts 5. commit 6. rebase master onto feat1).
But, if you’re aware of what conflicts might occur and how to resolve them, or if you want to use interactive patching, then using
git merge --squashis a good choice; and it might be faster (1. merge 2. fix conflicts 3a. commit or 3b/3c … patch add the changes)Posted on April 5, 2011 with 14 notes ()
-
Fast forward merge
Say we have a repository with a master branch and a feature branch. Since the feature branch has come off master, there have been no other changes added. Also, the master branch is the currently checked out branch and we can see that because HEAD points to master.

So, we want to bring in the changes from the feature branch to the master branch so we issue a simple
git merge feature. Even if you use the-mflag to add a message to this merge a terse message appear saying that there has been a fast-forward. What has happened is that since there are no other changes, there is no merge and the master branch is just moved to point to the tip of the feature branch. Since HEAD points at master, it follows along. So the position is this.
Nice and simple, and makes sense. But it can annoy some users who like to work on a feature in a branch but want that loop to exist after merging it back in the main line. It can make it easier to see the changeset in one glance and more convenient to back out later if needed. Git has a way of fixing that. If instead of
git merge featurewe change the command togit merge --no-ff feature(note the--no-ffflag) and the branch is merged instead.
Personally, I’m comfortable with a rebase based workflow, so I don’t use this flag, but it’s just another example of the annoying flexibility of Git.
Posted on April 7, 2010 with 2 notes ()
-
Fixing merge conflicts
Branching is great. Being able to work on code that you don’t have to check in, that doesn’t risk polluting the main branch until it’s finished is great; but you have to be able to bring the changes back in at some stage. One way is to use
git merge. Here’s a nice contrived example of a simple repository containing two files. Greetings.txt and Partings.txt. There are three branches; master, es and fr. The currently checked out branch is es so the HEAD reference points to that. Can you spot the deliberate mistakes in the contents of the files?
I want to merge the changes from the fr branch into the es branch and since I am on the es branch already, I use the command
git merge fr, and that’s when the problems start. It looks like the merge didn’t work and has left me with conflicts.
Conflicts are an unavoidable fact of working with merges in any version control system. If there has been a change to the same line in both branches then git does not know which one to apply. Although it’s very good at interleaving changes to different lines in files, there is nothing magical about git: it isn’t going to risk corrupting my files in cases where there is a conflict. So, that means that they need to be fixed by hand. Before we look at ways of fixing these commits, let’s take a look at the state of our repository now.
Examining the conflicts
Run
git statusto see the state of your working directory.
Here you can see that the two files are in the working directory, but not in the index. These files have been modified by git with familiar conflict markers.

Notice that it has marked the changes to the HEAD and fr branches in each case. What this doesn’t show you are the hidden references to the state of the files at each branch. This is what your repository looks like now:

Quick and dirty resolution
For small files like this the fixes are quite simple:
- Search for the conflict markers in each file.
- Edit the file to the state that you want, and remember to remove the conflict markers.
- Add the changed files to the index with
git add. - Once all the conflicted files are fixed, commit them with
git commit. - Edit the commit message to show that I’ve merged the files and fixed the conflicts.
Not so dirty resolution
In this case, I can see that files in one or the other branch should take priority. That is, changes in those files are the ones to be applied in conflicts. So here; I am on the es branch and I can see that I want my version of the Greetings.txt file to be in the merge, and the fr branch version of Partings.txt is the correct one. What I can do is to use the references to check out the version of the file that I want. So what I do here is:
- Get the es version of Greetings.txt with
git checkout --ours Greetings.txt. Note: I am using--oursbecause it is the one from the branch I am currently on. - Get the fr version of Partings.txt with
git checkout --theirs Partings.txt. Note: I am using--theirsbecause it is the one from the branch I am merging with. - Do a quick sanity check of the files; and I can see that the correct versions exist, and without the conflict markers.

- Add the files to the index with
git add . - Commit the changes with
git commit - Edit the commit message to show that I’ve merged the files and fixed the conflicts.
Resolved Merge
After resolving the conflicts and checking in the changes this is what the repository looks like.

You can see that there is a new commit with the correct versions of the files. Also, since this was a merge commit, this new commit has two parents which are the commits that it merged.
Summary
This is a contrived example but merge conflicts are a fact of life. Git doesn’t replace communication between team members and cannot read minds when deciding which changes to apply if there are conflicts. It’s also generally safe to merge branches because you will be asked to resolve conflicts rather than have changes applied under you that might mess up the project.
There are other ways of joining branches and dealing with the conflicts. If you can’t remember them all, the quick and dirty method is the one that you can always use.
Of course, if things become too much of mess, and there are too many conflicts to resolve all at once, you can always about a merge (before resolving and committing, of course) with
git reset --mergewhich will restore the repository to the state it was before you started the merge. You can then use other methods to get your project into shape before trying to merge changes.Posted on April 1, 2010 with 32 notes ()
-
Branching out
Branching in git is fast and easy. Coupled with fast merging, and rebasing, it is very convenient to always work on a separate branch. Typically, you create a branch, make changes to your project on that branch, merge this working branch with one of the permanent branches and delete the temporary branch.
Let’s take an example. Assume you have a repository with a single branch; master with a few commits.

The single branch master is represented by a file in the repository. It only contains a 40 character string which is the ID that uniquely identifies the commit. (You’re probably getting sick of me saying this; but I will get around to explaining that another day). There is also another reference called HEAD. This is a special reference that points to the current commit. All changes in your project will be added on top of this commit. As we only have one branch, and it is checked out, HEAD points to master.
Let’s create a new branch called feature with the command
git branch feature
Another file is created in your repository called feature that contains the 40 character ID of the commit from which it was branched. Since this branch was just created, it points to the same commit as master. Since all we have done is created the branch, not checked it out, the HEAD reference still points at master. Check out the feature branch with
git checkout feature
Since all commits will now be on the feature branch, the HEAD reference now points at the feature reference. Now, imagine some commits have been made to the feature branch.

What has happened here is that every check-in has created a new commit (which references the previous commit; that’s why the arrows are pointing backwards) and the reference file for feature has the ID updated to that of the latest commit. The HEAD commit just points to the feature branch.
Imagine another jump forward in time. The master branch has been checked out and some changes applied to that. The repository situation now looks like this.

Because we are back on the master branch, that is where the HEAD reference is pointing. If the two branches are merged with
git merge feature, then the changes from feature are merged with the changes to master to form a new commit.
Again, feature and master both point to the same commit, and as we are still on the master branch, that is where the HEAD reference points to. Since we’re done with adding the feature, the feature branch can be deleted.
It might seem like a palaver to go through this branching and merging. You don’t have to create a branch, you can make the changes to the _master) branch directly, but there are advantages:
You can consider your master branch as the working, non-experimental state of your project. Since all the messing about is done in branches and then merged back in, you always have a correct state to your project.
Changes are chunked together. If you name your branches by what you are trying to do, it’s easy to see what work is being done in what branch.
If work is being shared with other developers, working in your own branch keeps things separate.
If you give up on the changes you are making, you can just delete the branch and not worry about trying to back out all the changes from the project.
Virtually no overhead to branching; It’s just a matter of updating a 40 character string in a file.
You can do nifty things with
git rebaseon a branch.Branches in git are not permanent. You can delete them without them showing up in the history.
If you’ve worked in other version control systems I’m sure that you can think of many more reasons.
Posted on March 30, 2010 with 3 notes ()