Blog Engineering How (and why!) to keep your Git commit history clean
2018-06-07
5 min read

How (and why!) to keep your Git commit history clean

Git commit history is very easy to mess up, here's how you can fix it!

keep-git-commit-history-clean.jpg

Git commits are one of the key parts of a Git repository, and more so, the commit message is a life log for the repository. As the project/repository evolves over time (new features getting added, bugs being fixed, architecture being refactored), commit messages are the place where one can see what was changed and how. So it's important that these messages reflect the underlying change in a short, precise manner.

Why a meaningful Git commit history is important

What does Git commit do? Git commit messages are the fingerprints that you leave on the code you touch. Any code that you commit today, a year from now when you look at the same change; you would be thankful for a clear, meaningful commit message that you wrote, and it will also make the lives of your fellow developers easier. When Git commits are isolated based on context, a bug which was introduced by a single commit becomes quicker to find, and the easier it is to revert the commit which caused the bug in the first place.

While working on a large project, we often deal with a lot of moving parts that are updated, added or removed. Ensuring that commit messages are maintained in such cases could be tricky, especially when development spans across days, weeks, or even months. So to simplify the effort of maintaining concise commit history, this article will use some of the common situations that a developer might face while working on a Git repository.

But before we dive in, let's quickly go through what a typical development workflow looks like in our hypothetical Ruby application.

Note: This article assumes that you are aware about basics of Git, how branches work, how to add uncommitted changes of a branch to stage and how to commit the changes. If you're unsure of these flows, our documentation is a great starting point.

A day in the life

Here, we are working on a small Ruby on Rails project where we need to add a navigation view on the homepage and that involves updating and adding several files. Following is a step by step breakdown of the entire flow:

  • You start working on a feature with updating a single file; let's call it application_controller.rb
  • This feature requires you to also update a view: index.html.haml
  • You added a partial which is used in index page: _navigation.html.haml
  • Styles for the page also need to be updated to reflect the partial we added: styles.css.scss
  • Feature is now ready with the desired changes, time to also update tests; files to be updated are as follows:
    • application_controller_spec.rb
    • navigation_spec.rb
  • Tests are updated and passing as expected, now time to commit the changes!

Since all the files belong to different territories of the architecture, we commit the changes isolated of each other to ensure that each commit represents a certain context and is made in a certain order. I usually prefer backend -> frontend order where most backend-centric change is committed first, followed by the middle layer and then by frontend-centric changes in the Git list commits.

  1. application_controller.rb & application_controller_spec.rb; Add routes for navigation.
  2. _navigation.html.haml & navigation_spec.rb; Page Navigation View.
  3. index.html.haml; Render navigation partial.
  4. styles.css.scss; Add styles for navigation.

Now that we have our changes committed, we create a merge request with the branch. Once you have merge request open, it typically gets reviewed by your peer before the changes are merged into repo's master branch. Now let's learn what different situations we may end up with during code review.

Situation 1: How to change the most recent Git commit

Imagine a case where the reviewer looked at styles.css.scss and suggested a change. In such a case, it is very simple to do the change as the stylesheet changes are part of last commit on your branch. Here's how we can handle this;

  • You directly do the necessary changes to styles.css.scss in your current branch.
  • Once you're done with the changes, add these changes to stage; run git add styles.css.scss.
  • Once changes are staged, we need to add these changes to our last commit; run git commit --amend.
    • Command breakdown: Here, we're asking the git commit command to amend whatever changes are present in stage to the most recent commit.
  • This will open your last commit in your Git-defined text editor which has the commit message Add styles for navigation.
  • Since we only updated the CSS declaration, we don't need to alter the commit message. At this point, you can just save and exit the text editor that Git opened for you and your changes will be reflected in the commit.

Since you modified an existing Git commit, these changes are required to be force pushed to your remote repo using git push --force-with-lease <remote_name> <branch_name>. This command will override the commit Add styles for navigation on remote repo with updated commit that we just made in our local repo.

One thing to keep in mind while force pushing branches is that if you are working on the same branch with multiple people, force pushing may cause trouble for other users when they try to normally push their changes on a remote branch that has new commits force pushed. Hence, use this feature wisely. You can learn more about Git force push options [here](https://git-scm.com/docs/git-push#git-push

We want to hear from you

Enjoyed reading this blog post or have questions or feedback? Share your thoughts by creating a new topic in the GitLab community forum. Share your feedback

Ready to get started?

See what your team could do with a unified DevSecOps Platform.

Get free trial

New to GitLab and not sure where to start?

Get started guide

Learn about what GitLab can do for your team

Talk to an expert