A Faster Git fixup Flow in Vim

Sun Jun 26, 2022
~600 Words

As I work on a branch I often issue fixup commits for changes that I would have wanted to belong to an earlier commit in that branch. For example if I add some tests and then later tweak something small such as the description for the test then I’d like to just fix up that original commit rather than add a new commit just to record the improvement to the test description.

One reason for this is that the branch is that if I instead have a commit for each change I have made then before sending the branch for review I will want to manually squash all those commits.

Sometimes this is what I want (e.g. I may later decide to drop this change), but for minor changes this overhead is unnecessary.

For a long time now my workflow has been the following:

  1. Grab the SHA of the commit that I want to fix up. This is generally the SHA of the line that needs to be changed, or a neighbouring line which I know is part of the same commit.
    1. :G blame to open the vim-fugitive blame view
    2. yw to yank the SHA under the cursor
  2. Make the changes and stage them (bound to <leader>hs in my setup)
  3. :G commit --fixup=<c-r>" to issue a fixup, pasting the SHA from the default register.

This is relatively quick but does involve lots of repeated work over the course of a working day, just to issue fixup commits.

Here are some vim mappings and supporting function to reduce the repetitive elements of this workflow:

func! YankLastShaForLine()
  :Git blame
  :normal! yiw

  " Need to make blame buffer modifiable to be able to delete it
  :set ma

  :wincmd p

" Workflow for quick fixups:
" Use ys to yank the last SHA for the current line
" Use yf to form a fixup command
nmap <leader>ys :call YankLastShaForLine()<CR>
nmap <leader>yf :Git commit --fixup=<c-r>"

Now to fixup an earlier commit the flow is:

  1. While on the line to change, hit <leader>ys to yank the latest SHA for this line
  2. Make the changes and stage them
  3. Hit <leader>yf1 to prepare the fixup command
  4. After a visual check of the command, hit enter to commit

This reduces the bulk of the work, making it fairly effortless to fixup the commit of a given line. Note that any amount of changes could be staged, this is not limited to changes made to a single line.

The other part of this workflow is to enable autosquash in your ~/.gitconfig:

  autosquash = true

When running git rebase -i main all the fixup commits will be presented as fixup rather than pick, and will therefore be listed under the commit that they alter. After reviewing this briefly, saving and exiting will cause all the fixups to be applied.

This workflow was tested in Neovim 0.7, but I expect it will work without modification in vim.

There is an open PR for vim-fugitive which adds mappings for fixing up a commit identified in the :G blame pane. It hasn’t been merged in 3 years at this point in time, so I decided to add a function/mapping in my own configuration instead.

  1. yf is a poor choice of mapping in terms of the vim editing language (we are not yanking anything here) but I find this to pair nicely with ys, and will give some thought to a better pair of mappings that will be as memorable and intuitive for me. ↩︎