Did you know that Git can be customized to your needs in a multitude of ways?

Creating aliases in Git is a powerful feature that allows you to define "shortcuts" for longer Git commands (and more). For some, it’s just a tool to make command-line Git usage survivable, but I will show you that Git aliases are so powerful they can contribute to making command-line the preferred and most efficient way to use Git.

The main motivation for creating Git aliases could be one or more of the following:

  • Optimize: Create shortcuts for frequent Git commands.
  • Customize: Make Git behave how you want or support team-agreed habits.
  • Memorize: Easy-to-remember shortcuts for complex operations.

In part 1 of my blog series, I will take you on a journey from the very humble basics, introducing Git aliases that just save typing longer commands, up to more advanced features that even many seasoned Git veterans have never used.

Part 2 will continue on from there, introducing even more advanced concepts and venturing into truly “out there” uses of Git aliases, including some downright crazy examples, before discussing alternatives for solving the same needs.

Learning and understanding these techniques, whether used for aliases you steal from others or venturing into pushing the boundaries yourself, will make you a more efficient and powerful Git user and hopefully make your day-to-day life with Git more enjoyable. Along the way, you will probably also learn a few tips and tricks and explore corners of Git that you weren’t even aware of.

What are Git aliases, and how do we set them up?

Git aliases are substitutions for Git subcommands in the same way that, e.g., Bash aliases are substitutions for other Bash commands or scripts. Git simply allows you to define your own Git commands that do what you want and can be used seamlessly along with the built-in commands.

Aliases are defined in the Git config hierarchy, but as we usually want them to work everywhere on our machine, the global configuration is their natural home.

You can add aliases either by directly editing your global config file or using the git config command.

To create your first simple alias, just try:

$ git config --global alias.st status

This will add a new line to the [alias] section of your global config file and create the section if it isn't already there. Let's have a look:

$ cat ~/.gitconfig
[user]
   name = Jan Krag
   email = jan.krag@eficode.com
[alias]
   st = status
Congratulations, you have your first alias and can now type:
git st
Instead of:
git status

For more complex aliases, later on your journey, I suggest that you simply open ~/.gitconfig in your preferred editor and add or modify your aliases directly there.

Level 1: The lazy typer

Ok, let's get started with the first level. I am lazy and simply want to optimize my typing of commands I use hundreds of times a day or to handle common typos I frequently make.

We have already seen the st suggestion above, but here are a few more common examples of simple shortcuts:

[alias]
    s  = status
    st = status
    c = commit
    sw = switch
    br = branch

For "common typo" style aliases, these are a personal preference. Create aliases for whichever commands your fingers can't type. My personal nemesis is switch, but I have just resolved to always use the short "sw" suggested above instead of creating aliases for all the possible typos I can make in that one word. But here are a few examples for inspiration:

[alias]
    comit = commit
    swicht = switch
    statut = status

Now, at this time you might already be thinking about which Git commands are your best candidates for aliasing. I came up with an interesting answer years ago. Just like Git, my shell (Bash, Zsh) also supports aliases. So I created the following shell alias in my .bashrc and .zshrc config files:

alias frequentgit='history | cut -c 8- | grep git | sort | uniq -c  | sort -n -r | head -n 10'(Note: This is a shell alias, not a Git alias).
 
If I run this alias at any point in time, it will look through my shell history and list the most frequently used Git commands.

Git command

Some of them might already be existing aliases (here slog, st, and glog), but the others might very well be good candidates for you to "shorten" or to remember using aliases you have already made. The only reason I use "git status" so frequently, despite my "git st" alias, is that I deliver a lot of Git training classes, where I commit myself to using the actual commands.

Level 2: Simple options to avoid typing daily option flags

So, time to move up a notch on the Git alias volume control. Aliases also allow us to add "options" to the Git commands we are aliasing, so let's see how we can utilize that.

The most common use case is creating easy aliases for common variants of Git log output.

E.g.:

[alias]
    last  = log -1
    lo = log --oneline
    l5 = log --oneline -5

Another great suggestion is to use this feature to create those pesky "missing" Git commands that you think should have existed and where you might struggle to remember which exact command and option you have to use to achieve that very common task.

amend = commit --amend
untrack = rm --cached
unadd = restore --staged
Remember that aliases are a personal preference. You have to make the aliases that make sense to you for commands that you might actually use (and understand). More advanced examples in this category could be things like:
pleasepush = push --force-with-lease
gg = grep -E --line-number
softadd = add --intent-to-add
catp = cat-file -p

Admittedly, many Git users may have never heard about these Git commands and options, let alone used them. This makes these aliases either useless or a great learning opportunity.

Level 3: Complex arguments help us remember rarely-used commands

The next level on our journey is to investigate using aliases for commands that also take complex arguments.

[alias]
foo = <subcommand> <option> <arg...> <option> <arg...>    

So far, we have mainly focused on aliasing frequently or commonly used commands, but since we can now shorten much more complex commands, we might also find it useful to go in the other direction and use aliases for:

  • Infrequently used hard-to-remember Git tasks.
  • Making hard-to-type commands handier.

Together, this allows you to use nice Git features that you might otherwise not bother with.


# Diff of last commit
dlc = diff --cached HEAD^ 

# list all defined aliases
aliases = config --get-regexp alias

# Find very first commit
first = rev-list --max-parents=0 HEAD

# what would be merged
incoming = log HEAD..@{upstream}

# what would be pushed
outgoing = log @{upstream}..HEAD
outgoing = log @{u}..

# List all commits (on this branch) made by me
mycommits = log --author=\".*[Jj]an\\s*[Kk]rag.*\"

As you can see, I have described the aliases "inline" in the examples above by using the ability to add # comments in your config file. This is not only to make the example easier, but to strongly suggest that you do this as well in your actual config. It took me way too many years to learn the hard way to do this, and now I have numerous weird aliases and other settings collected that I can't quite remember the purpose of.

Let's look at a few more specific and often-used examples from my collection. This time I will actually show you the results. I will show you a useful diff alias, but first, the "before" view running git diff on a markdown file.

Git diff

Now let's add an alias:

wdiff = diff -w --word-diff=color --ignore-space-at-eol

And use that instead:

Git wdiff

This is obviously much cleaner and easier to grasp. It is not the alias itself that is doing the magic; these are built-in features in Git. But the alias makes it useful in my daily life because I can't be bothered to remember to type: git diff -w --word-diff=color --ignore-space-at-eol every time.

Let us define:

[alias]
structure = log --oneline --simplify-by-decoration --graph --all
And then run the command on a huge repo:
$ git structure

Git

The shown snippet covers a range of over 3,000 commits in the Tensorflow repo by just showing the commits that are "labeled" (tags or branch heads).

To close this level off, and lead nicely into the next one, let’s look at what’s probably the most beneficial use of aliases with complex arguments: The ability to create even more customized git log commands that suit your preference by utilizing custom formats. I don't want this to become a tutorial on the actual formatting options, so let's just dive right in, and you will catch the drift. This is all thoroughly documented in the Pretty formats section of git help log.

I present to you, my daily driver:

slog = log --pretty=format:'%C(auto)%h %C(red)%as %C(blue)%aN%C(auto)%d%C(green) %s'

Git slog

Level 4: Pretty formats - clean up your aliases with reusability

For this level, let us venture outside the immediate realm of Git aliases, and I will teach you how to use the little-known feature of custom pretty formats to clean up your aliases and vastly improve reusability.

We saw above how I utilize custom formatting fields to craft exactly the log output I prefer, and this is all nice and good until you realize that I used to have all of the following:
slog = log --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....
#(For those lazy days).
l = log --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....
l1 = log -1 --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....
l5 = log -5 --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....
l10 = log -10 --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....
l20 = log -20 --pretty=format:'%C(auto)%h %C(red)%as %C(blue).....

(Actual aliases were much longer but all the same format string).

And many similar ones. This made it really annoying every time I wanted to tweak the format, or coloring, etc.

These days, in more recent versions of Git, aliases may refer to others, so the above can be vastly improved, e.g.:

l1 = slog -1
l5 = slog -5

But it turns out that there is a better option.

You may know that Git has some built-in "pretty" formats that you can use in your log, like:
git log --pretty=oneline
git log --pretty=full
git log --pretty=raw

(See the above-linked documentation for more coverage).

Way too late in my journey, I discovered that Git allowed me to define my own custom "pretty" formats in Git config, but when I found this feature, it was awesome!

These formats can be defined in the [pretty] section of your config (or git config –global pretty.myformat …..) like this:

[pretty]
    slog = format:%C(yellow)%h %Cred%as %Cblue%an%Cgreen%d %Creset%s
    bw = format:%h | %as | %>(20,trunc)%d%x09%s

Once I have these defined, I can use them at any time when running a Git log command:

$ git log --pretty=slog

This also means that I can rewrite all my weird log aliases to use my own pretty format, and then I have a single place to edit whenever my tastes change.

[alias]
    l1 = log -1 --pretty=slog
    l5 = log -5 --pretty=slog 
    slog = log --pretty=slog
    slogbw = log --pretty=bw
    glog = log --graph --pretty=slog 
    outgoing = log --pretty=slog @{u}..

Another bonus of having these pretty formats defined is that I can also use them "on demand" even when running "in-the-moment" log commands.

$ git log --pretty=slog --no-merges --author-date-order  foo..bar

Level 5: Prefixes - override Git behavior for specific Git subcommands

For level 5, to round off this "part 1" blog post, we’re going to have a look at a very unknown feature in Git aliases utilizing an also rather unknown feature.

It turns out that Git aliases can send options not only to the Git subcommand but to the git command itself. And if you are thinking, "I didn't know that the Git command had options," you are probably not alone.

One useful and easy-to-explain example is the option to control pagination. By default, Git will pass any output of more than a screenful to less (or other configured pager), and output less than a screenful is printed outright. But Git allows us to override this behavior if desired, e.g.:

git –paginate 
git --no-pager 

Let us see how that could be utilized in an alias.
E.g.:

pst = --paginate status # so that output doesn't stick around on your screen and scroll buffer when you quit less
listconfig = --no-pager config --list # so that output stays on your screen and scroll buffer to allow copy/paste

Another good use of this feature is to use it in conjunction with Git's ability to temporarily override config settings.

In Git, you can use the -c option to override a config value for this single command only, i.e., git -c <config override> <subcommand>.

This can also be used as an alias:

annoncommit = -c user.name="Anonymous" -c user.email="notme@localhost" commit

Note: This is different from using git commit --author= as it sets both the author and the committer identity, as seen in the following example:

$ git add .
$ git annoncommit --message 'foo'
$ git log --pretty=full
commit 0ae65ffc6192b6a2561db906bfed5c45bac702db (HEAD -> master)
Author: Anonymous 
Commit: Anonymous 

    foo
Let's see a few more useful examples:
# Verbose commit (add diff to comments in commit text)
vcommit = -c commit.verbose=true commit
# Use VSCode for editing, just this once
vscommit = -c core.editor="code --wait" commit
# Use VSCode for interactive rebase
riv = -c sequence.editor="code --wait" rebase -i

What’s next in the Git blog post series?

I have taken you through a bit more than the basics of Git aliases. We have seen how they can be incredibly helpful for daily work, commands that we use frequently or commands that we use so rarely that we can't remember them. We have also gone a bit outside the box of normal Git aliases by playing around with options for Git itself (already a feature that most users don't know about).

This is a good place to leave you hanging in anticipation of part 2, where we will continue with:

  1. !non-git commands: More “bang” for the buck.
  2. Reusing aliases.
  3. Pipelining the action: Chaining unix tools for more action or craziness.
  4. Bash functions for the win.
  5. Going overboard because limits can only be found by crossing them.
  6. Bonus round exploring alternatives to Git aliases.

See you in part 2!

Published: Apr 8, 2024

DevOpsCI/CD