Git Polishing 💎

Custom git configuration often ends up being something we hone over the course of our development careers, slowly evolving into a perfectly tuned set of non-standard commands (this is much the same as how I feel about my Emacs and Vim configs).

As I read this tweet the other day I realized that we could all benefit from sharing our own workflows:

I'd like to share a few of the git commands I use every day for "getting the job done".

Adding custom git commands

Before we start digging into specific tips/tricks I think it would be useful to share the general idea of creating custom git commands. This is effectively like making shell aliases, but they work with the git command.

To define a custom alias, you would run:

git config --global checkout

After running that command, in any git repo you can run git co instead of git checkout. 🎉

I most often take advantage of custom git aliases globally (using git config --global or manually editing my global ~/.gitconfig file), but you can also add them to your projects own configuration (git config without --global or via editing ./.git/config) if they are not generally useful outside of a given project.

Listing Branches

Quite often I want to see a listing of branches in my current repo. Many of you might be thinking:

WAT, this is trivial 😏:

git branch

You're right that is trivial, and I used this for quite a while but I slowly started to get annoyed. What I really wanted was to see a list of the branches in most recently used order (this is likely due to my forgetfulness about what I named a given branch 😜).

So for this, I use a custom command:

git config --global alias.list-branches "branch --sort=committerdate"

This one is fairly obvious (it used to be a bit harder before newer features added in Git 2.7), but its still pretty nice to have a quick auto-complete for it.

I run git list-branches at least 10 times a day now...

Delete Merged Branches

I think almost everyone has a variation of this (or does it manually 😫). Deleting merged branches (and pruning the remote) is a very common task (otherwise the git list-branches alias we added above is not terribly useful).

git config --global alias.delete-merged-branches "!git branch --merged master | grep -v -e 'master' -e '\\*' | xargs -n 1 git branch -d && git remote prune origin"

😨⁉️ OK, hold on. Lets break it down a bit.

  1. Aliases that start with a ! are independent commands to be executed. If you look back to the list-branches alias, you will see that we do not have a leading ! because this command is for the git itself (without need for | or && etc).
  2. git branch --merged master is fairly straight forward, it just lists the branches that are merged with master.
  3. Then we pipe that into grep -v -e 'master' -e '\\*' which filters the list to exclude the master branch and the current branch (signified by a * in the git branch --merged master output).
  4. At this point we have our list of branches to delete, so we pipe that into xargs -n 1 git branch -d. This takes each branch returned from git branch --merged master | grep -v -e "master" -e "\\*" and invokes git branch -d <BRANCH NAME HERE> for it.
  5. Now that we have cleared local branches that have been deleted, we prune our remote branches via git remote prune origin.

git delete-merged-branches is probably one of my most used commands (right behind git commit, git push, and git pull). It looks complicated (and kinda is), but it's just so useful.

Checkout Pull Request

While reviewing a pull request I often want to pull it down locally and test things out. For that I use the following alias:

git config --global alias.setup-fetching-prs "!git config --add remote.origin.fetch '+refs/pull/*/head:refs/remotes/origin/pr/*' && git fetch origin"

This one isn't nearly so difficult as the last one, lets break it down:

  1. Github repos include the refs of all pull requests, we just need to instruct our local project to also fetch them via git config --ad remote.origin.fetch '+refs/pull/*/head:refs/remotes/origin/pr/*'.
  2. Then we kick off the fetch of the remote to get the new refs.

After you run this, you can run git checkout pr/1234 to checkout a given pull request.

There is a great alternative to this command (though it still uses the same basics) over on the Ghost Dev Blog which just runs the above for specific pull requests (and therefore avoiding fetching a remote for each PR). Both are great workflows, the "best" really just depends on how often you will need to checkout pull-requets for a given project.

Conclusion 🤖

These are the git customizations I use on a regular basis. What are you using?