3 simple tricks for managing git histories
It took me a while to learn these simple tricks for effectively managing Git histories with just a few commands. In the first article, I explained why clear, structured Git commit messages and cohesiv commits are important or why temporary commits make the history more difficult to read. This time, I will show you how to organise your commits before merging them. This will give you a clean, easy-to-understand commit history.
Changes should be reviewed and consolidated into a clear commit history. The aim is to include only changes that are relevant, just like book authors revise drafts before publishing. Consider cleaning up commit history for clarity and maintainability:
- Add changes directly to the previous commit
If you make minor adjustments to the last commit, you can add them directly into the previous commit by utilizinggit commit --amend - Consolidate multiple commits into one single commit
All changes can be grouped together into a single commit withgit merge --squash - Tidy up your commits
Remove temporary commits, combine multiple commits into one or change the content to create a simple, easy-to-read history withgit rebase
Add changes directly to the previous commit
You can make minor corrections or unnecessary changes directly in the last commit using git commit --amend. This command lets us add new files, remove unwanted files or change the commit message – all without creating a new commit (simplified). Here’s how to make changes:
- Open your files in the working directory, and then either add new files using
git addor remove files that you don’t need any more usinggit rm - Now you can adjust a commit by adding the changes to the last one using the
git commit --amendcommand. - You can also edit the commit message if you want.
- And when the commit is already been pushed, you’ll need to force push to the remote repository with
git push --force-with-lease.
Need more tips? Here we go:
Undo unnecessary commits
You can undo unnecessary commits with git reset, as long as they haven’t been pushed yet.
Undo changes that have been alread pushed
If you’ve already pushed some changes, you can undo them using git revert <commit-hash>. But keep in mind that this will create a new commit.
Consolidate multiple commits into one single commit
During development, you usually work in your feature branch and create several commits like partial steps, WIP commits or bug fixes. Often these intermediate states are not relevant for the main branch and inflate the history. In such a case, the changes can be reduced to one single, clean commit.
Before merging into the main branch, you can squash your commits so that only one summary commit appears in the main branch. The detailed intermediate steps in the feature branch are not retained – instead, a clear, complete commit is created that describes the entire change. Here is an example:
git checkout main
git merge --squash <feature-branch>
git commit -m "feat(auth): add JWT authentication"
git push origin main
This method makes sure you’ve got a tidy and clear commit history by presenting the whole change as one completed commit – perfect for feature developments with lots of intermediate stages. This makes code reviews a lot of easier. And the main branch is kept simple and free of any unnecessary steps.
Disadvantages like losing granularity because you lose contextual information, or more complex conflict resolution because squash bundles multiple changes into one large commit, are often not that important.
Tidy up your commits
You can use git rebase -i <commit-hash> to edit the commit history interactively and to tidy up the commits. This is really useful for getting rid of temporary or debug commits, or for merging multiple commits. Let’s say we want to edit the last five commits with git rebase -i HEAD~5. This will show you a list of the last five commits:
pick abcdef1 Commit A
pick abcdef2 Commit B
pick abcdef3 Commit C
pick abcdef4 Commit D
pich abcdef5 Commit E
Now you can edit the commits as you like:
- Change pick to squash for the commits you want to merge.
- Use fixup, if you want to stick changes or corrections made in later commits into an earlier one, but you don’t want to keep the message from the later one.
- You can use the drop command to get rid of individual commits from the history. This is great for temporary or debug commits.
- And use edit to make changes to a commit. Git stops at this point in the rebase process, so you can make changes in the working directory, like remove debug output or undo temporary changes.
Then just save those changes and you’re all set! Now that you’ve changed the commit history, I think it’s important to mention that you’ll need to perform a forced push to update the changes in the remote repository using git push --force-with-lease.
A rebase cleans up the Git history. It makes the history linear, as if all the changes from your feature branch were applied directly to the latest version of the main branch. And there’s no merge commit created that is causing any issues.
Just be careful:
- If you re-write history, you might lose sight of the original context, especially when and how changes upstream were integrated.
- If you rebase branches that have already been published, you might run into problems because you’ll be changing the history. Other team members working on the same branch will run into conflicts and have to readjust their local changes.
Conclusion
In this two-part series of articles, we’ve taken a close look at keeping Git histories easy to understand and to maintain. This is something that often gets overlooked, but it’s actually really important making sure the team works efficiently.
The second part is all about specific ways to tidy up the commit history before merging into the main branch. We need to go the changes one more time to get them into a clear, consolidated commit history. The idea is to include only changes that are relevant for production.
Links
Rewriting history https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
Photo by Tobias Reich on Unsplash