Merging Commits in Git
The adoption of cloud technologies has introduced a new way of managing resources, whether they are related to infrastructure, security, applications, etc. In the past, code repositories such as Git were just used manage application code, but now we use them to manage pretty much everything. However, not everyone is familiar with repositories and their principals and that sometimes leads to issues.
A very common practice I've come across many times lately is having too many commits related to a single feature or request. I'm not suggesting commiting only big chunks of code that contain many changes at once, but instead getting rid of multiple commits that each one may overlap with the previous one or just contains minor changes. At least when pushing to a remote source that other people my be watching.
The three most common ways to clean up commits are reset, merge and rebase. For demonstration purposes we'll be working with a repository that contains Azure ARM template files for various workloads and we'll be focusing on the Data Lake part of it.
Here we've been developing the Data Lake templates in a seperate branch called "DataLake" and as you can see there are four commits already in this branch. Our goal here is to merge those commits into one, keeping all the work they contain. Let's go through our options in detail.Reset
The reset operation offers an option called "soft" that will reset the branch to a previous commit but will keep all the changes made to files as they are up to the current commit.
The command to reset is:
git reset --soft HEAD~4
This will reset the current branch to four commits back, right at the start of the branch resulting in something like:
The work contained in those commits is now uncommited, so we have to save it using:git commit -m "Added Updated Data Lake templates"
We will then have a single commit in our branch that contains all the work previously commited in it:
The key point here is that when you create the new commit, the authoring information changes. Furthermore, any changes that had not been commited at the start of the processes will also be commited, so make sure your working copy is clean before starting.Merge
The first thing to do when attempting to squash commits using the merge function, is to commit your work. We are going to hard-reset the branch and any uncommited changes will be thrown away.
Starting from the point where the branch had four commits like below:
we will reset it to four commits back using:
git reset --hard HEAD~4
to something like:
The next step is to merge the commits up to where the branch was before the reset operation using:
git merge --squash HEAD@{1}
ending up with uncommited changes:
The only thing left to do is to commit your changes:
Rebase
The rebase method is the most complicated out of the three, however it allows for more control on how to treat each commit.
Starting again with a branch that has four commits on it, we will issue the command:
git rebase -i HEAD~4
This command will open the interactive rebase editor showing information for all the commits:
The commits we are about to merge are listed on the top, starting from the latest one. What I usually do here is replace the "pick" command with "fixup" that merges the changes of a commit to the one above it. The first commit of the list (the most recent one from a time perspective) is left with "pick" since there is no commit to merge the changes to.If merging is something you do on a regular basis, you may want to consider adding an alias to your .gitconfig file to perform this operation, like:
[alias] squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
You can now use the git squash 4 command to merge the last 4 commits and update the commit message.
No matter what method you choose to follow, keep in mind that you should be starting the process having selected the latest commit in order to avoid merging the wrong commits. Get yourselves familiar with Git and always try out advanced operations you're about to perform on a local repository.