👉 Latest post:
"Why .every() on an empty list is true"
By Vincent Driessen
on Thursday, November 08, 2018

Every developer has their own favorite Git tricks they use daily. Here are some of my favorite ones I have been using for as long as I can remember.

First of all, I should mention that most of these commands are bundled in my git-toolbelt project. If you like to use them, all you need to do is install it like so:

$ brew install nvie/tap/git-toolbelt

Quickly opening modified files

While working on a branch, I often find the need to re-open the files I was working on. The Git toolbelt project contains a command to show you all locally modified files. It will only report files that still exist locally, so this overview won't include deleted files.

$ git modified
controllers/foo.py
README.md

This is super useful to quickly open all locally modified files in your editor. Definitely one of my most-used commands throughout the day:

$ vim $(git modified)

After quitting your editor, you can easily re-open the files you're working on this way.

To also include any files modified in the index (files that are git add'ed already), use the -i flag:

$ git modified -i

You can also pass it a commit SHA, which will open all files modified in that commit:

$ git modified HEAD~1

I have the following aliases set up in my shell, for quickly opening a specific set of files:

  • vc: vim locally modified files (not indexed)
  • vca: vim all locally modified files (including the ones indexed)
  • vch: vim files modified in the last commit (HEAD)
  • vc HEAD~1: vim all files modified in the second-last commit

Fixing up the last commit

You're probably familiar with git commit --amend to incorporate the currently-staged changes into the lastcommit, effectively rewriting the last commit. The toolbelt offers a similar command called git fixup, which will do the same, but without prompting for the commit message. So it's like a quicker version of commit --amend.

$ git fixup

This is a great way to build up a commit incrementally. A very typical flow for me looks like:

$ git add -p    # Pick bits to commit
$ git commit
$ git add -p    # Pick more bits
$ git fixup     # Add those to the last commit

"Emptying" the last commit

Sometimes I make a mistake and I accidentally commit too much, or something I didn't intend to commit. For example, an extra file I accidentally added, or a patch within a file that I didn't want to include. Here's how I fix that:

$ git delouse

This "empties" the last commit. Think of "emptying" as keeping the commit message and the author/date info, but "moving" all of its changes back into the work tree.

Technically:

  • Soft-resets the last commit, which means it will remove the last commit from the branch, and put back the contents of that commit into the work tree (basically reverting to the state right before committing). File contents aren't changed by this, only the Git commit disappears.
  • Add an empty commit with the same commit message and author details as the commit that was just removed.

The net result of these actions are that it appears as if the last commit on the branch is "emptied" back into your work tree. This command is non-destructive, since all of your files remain untouched. They are now just local changes again.

This allows re-adding all changes again. Just use git add -p to select the bits to commit, and then git fixup (see previous section) to keep changing the last commit, effectively rebuilding it up from scratch.

Because git delouse kept the commit message and author information around in that empty commit, the original commit info is never lost, and you don't have to re-enter the commit message whenever you run git fixup, which makes this whole process super cheap.

Typical flow:

$ git commit -m 'Add login screen'

# Oops! Checked in a secret key with that... let's fix this mistake!
$ git delouse

# Retry adding stuff
$ git add -p   # This time, don't add the secret key
$ git fixup    # Rewrites the previous commit

And if you make a mistake, you can just run git delouse again and start over, as often as you want. Since none of these commands destroy your local changes, this allows you to carefully craft your commit contents without the risk of losing any data.

Splitting up a commit into pieces

It's also a great way to split up a commit! For example, suppose you are adding a bugfix but you also renamed a variable to have clearer meaning. When submitting the code for review, you realize that the variable rename adds a lot of noise to the actual change. You may then decide it's a good idea to split up this commit into two pieces: one that atomically just changes the variable name everywhere, and one that fixes the bug. You can then point to the bugfix commit when asking for a code review.

How would that work?

$ git commit -m 'Bugfix for login screen'

# Oops, I should've split this one up. Let's start over!
$ git delouse
$ git add -p     # Just pick the bugfix bits
$ git fixup
$ git add -p     # Now pick the var rename bits
$ git commit -m 'Rename variable name to be clearer'

These three commands have become indispensable in my day-to-day Git routine. If you like it, let me know!

Other posts on this blog

If you want to get in touch, I'm @nvie on Twitter.