One weird trick for powerful Git aliases

(Yes the title of this post is a pun, apologies!)

I have written about aliases before! See for example a collection of my favorite git aliases or peruse my personal list on Bitbucket.


Recently at our Summit I showed a simple technique that can really unleash the power of your Git command line. Several people commented that it was very useful and I am happy to elaborate on it some more here. Let me know your genius creations in the comments! (If you’re an old time git wrangler you know this already but it already but if not, read on!)

Simple aliases are simple: you just add a line in the [alias] section of .gitconfig with your shortcut. For example:

ls = log --oneline

Will allow you to type git ls to get a bare list of short commit ids and
commit messages.

Or one of my favorites:

caa = commit -a --amend -C HEAD

Which will take all uncommitted and un-staged changes currently in the working directory and add them to the previous commit, amending it before pushing the change up. I use this all the time.

Limitations of simple aliases

There are many simple and useful aliases that you can craft like the above, but once you start to streamline your day to day routine you quickly realize there are some limitations to what you can do with simple aliases:

  • Normal aliases can’t have parameters.
  • You can’t execute multiple git commands in a single alias.
  • You can’t use | (pipes) or grep.

Or can you?!


Actually there is a solution!

Advanced alias template

git allows you to shell out in aliases – that is to escape to a shell (like bash or zsh) using the ! (bang). This opens a new world of possibilities for your aliases. By forking a shell process we are able to access a whole lot of things:

  • Shell expansions and parameters
  • Use multiple git commands
  • Pipes, greps and all Unix command line tools as needed

Here is the template for an advanced alias that requires parameters:

my_alias = "!f() { 〈your complex command〉 }; f"

The trick is to wrap our git command in an “anonymous” bash function – or better said a function named “f“. Doing it like this we have access to command line variables and shell expansions as the following:

  • $1 to mean the first parameter passed to the command.
  • $2 to mean the second parameter passed to the command. (and so on for $3,
    $4, etc.)
  • $@ to mean all command line parameters passed.

We can also now chain git commands with && and use the entire Unix toolkit.

Example advanced aliases

Now that I whet your appetite, let’s see a few examples of what you can do with all this power. Here a few ideas:

Quickly add a remote pointing to your colleagues forks

ra = "!f() { \
        git remote add $1$2.git; \
      }; f"

Define the above in the [alias] section and you can now add a fork with:

git ra jsmith jsmith/project-name

Add a subtree to your git project

sba = "!f() { \
         git subtree add --prefix $2 $1 master --squash; \
       }; f"

With this you can plug the contents of an external project into a sub-folder squashing the project’s history. Here’s how it looks:

git sba

This will show up like this in your history:

ff361e5 [7 days ago] Merge commit '6f214408ac7b8874bc7ca9a3e5041d6866f5a013' as '.vim/bundle/less' [Nicola Paolucci]

Cleanup merged branches

bclean = "!f() { git branch --merged ${1-master} | grep -v " ${1-master}$" | xargs -r git branch -d; }; f"

The above will remove local branches that have already been merged to master by default, but you can pass a different one if you need to (Credit to haacked).

Commits came in since last command

To see what new commits have been created by the last command – typically after a git pull (Source):

new = !sh -c 'git log $1@{1}..$1@{0} "$@"'

You invoke it for example with:

git new HEAD

Which will list to screen all new commits have been created with the previous pull.

Simple diff ignoring line number changes

When working with textual/tabular data files sometimes you move lines around and want a diff of what else happened apart from position changes. For those situations you can use the following alias:

sortdiff = !sh -c 'git diff "$@" | grep "^[+-]" | sort --key=1.2 | uniq -u -s1'

Video goodie

Fresh from the presses here is my Becoming a Git Master talk from Summit, where I cover the above ideas and much more:


More reading

While looking for some additional examples for this piece I stumbled on a very good set of articles, check them out for more insights:


Other Git articles you might find interesting:

P.S. Our engineering teams are hiring. A lot. Just sayin’.