Quite often, we must modify a file that has already been committed to the local repository. In this case, we often end up with commit messages like: “Fixed typo.”, “Added missing parameter.”, “Removed redundant import.”
At some point, we realize that such commit clutter the code history, and it is better to avoid them. Fortunately, there are at least two options to get rid of them.
git rebase -i
The simplest solution is to create the “fixup” commit with any message we want and then squash it with another commit.
In the first example, I created four commits. I want to squash the last one with the second one:
git log — oneline
Now, I am going to run the interactive rebase of commits between the current HEAD and the three last commits:
git rebase -i HEAD~3
I must move the commits around. The “Fixed a typo in second change.” commit must be below the second commit. Additionally, I have to change the command from
Don’t forget to save the file opened by
Done, I have commit history without artificial commits.
git commit — fixup
When I think that I may forget to run
git rebase later, I use
git commit with the
fixup parameter to automatically move the commit in the proper place and mark it as fixup of change.
In this example, I have three commits. Imagine that, I have modified the file again, and I want to commit my changes as a part of the “Second change.” commit:
git log --oneline
I need the identifier of the commit I want to modify. In this case
git commit --fixup 1e30877
Git created a commit marked as a fixup, but it is in the wrong place! I need to move things around again.
First, I must check which commit was not affected by my recent change. I need its identifier. In my example, it is:
Now, I must run an interactive rebase with the autosquash parameter:
git rebase -i --autosquash 7dbe2a9
It is going to open the rebase text editor we saw in the first example, but this time all changes have been automatically applied.
All I have to do is saving the file.
Change only a part of a file
Sometimes we want to fix a commit, but we have made many changes in the file, and only some of them should be squashed with a past commit.
In such situations, we can use the
patch parameter of the
git add command to add only a part of the file to the commit.
Imagine that I have made two changes in the file, but only one of them should be used as a fixup:
git add -p file
This command opens a view with all the changes I have made since the last commit. I can add a chunk of the file to the next commit (stage the change) or leave it unstaged.
In my example, the chunk suggested by git is too large. I want only the first modification. Fortunately, it is possible to split a chunk into smaller parts:
Now, I have to decide whether I want the chunk or not:
I won’t explain how to add chunks or split them because there is an excellent explanation on StackOverflow.
Now I can use
git commit with the
fixup parameter to mark my changes as a fixup and then run the rebase to modify the commits.
There is one little issue. I have not added the second chunk to the commit, so when I run the
git rebase I get information about unstaged changes:
I can stash them, do the rebase, and unstash them back, but there is a shortcut. I can run rebase with the autostash parameter:
git rebase -i --autosquash 7dbe2a9 --autostash
One more shortcut
We don’t want to type autosquash and autostash every time we do a rebase, so we are going to enable them in the git configuration permanently.
git config --global rebase.autosquash true git config --global rebase.autostash true
Now, all I must type is the identifier of the last unaffected commit:
git rebase -i 7dbe2a9