Git fixup explained
Quite often we must modify a file that has been already committed to the local repository. In this case, we often end up with commit messages like: “Fixed typo.”, “Added missing parameter.”, “Removed redundant import.”
Those comments are not that bad. They may be worse comments like: “Redo.”, “Fixed.”, “Code dump.”.
The problem occurs frequently, but we don’t want to have such commits in our git history. Fortunately, there are at least two options to get rid of them.
git rebase -i
The simplest solution is to create that artificial commit with any message we want and then squash it with another commit. The message does not matter, because nobody is going to see it anyway.
In the first example, I created 4 commits. I want to squash the last one with the second one.
1 git log — oneline
Now, I am going to run the interactive rebase of commits between the current HEAD and the three last commits.
1 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 pick to fixup.
Don’t forget to save the file opened by git rebase!
Done, I have commit history without artificial commits.
git commit — fixup
When I think that I may forget to run git rebase, I use git commit with the fixup parameter to automatically move the commit in the proper place and mark as fixup of another one.
In this example, I have three commits. Now I have modified the file again, and I want to commit my changes as a part of the “Second change.” commit.
1 git log --oneline
I need the identifier of the commit I want to modify. In this case
1 git commit --fixup 1e30877
Git created a commit marked as a fixup, but it is in the wrong place! I need to move the 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: 7dbe2a9
Now I must run an interactive rebase with the autosquash parameter.
1 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.
1 git add -p file
This command opens a file 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 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:
1 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.
1 2 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:
1 git rebase -i 7dbe2a9
Remember to share on social media! If you like this text, please share it on Facebook/Twitter/LinkedIn/Reddit or other social media.