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 one 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 two continues 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
git st
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).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.
Now let's add an alias:
wdiff = diff -w --word-diff=color --ignore-space-at-eol
And use that instead:
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
$ git structure
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'
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.
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.
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 one 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
# 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 two, where we continue with:
- !non-git commands: More “bang” for the buck.
- Reusing aliases.
- Pipelining the action: Chaining unix tools for more action or craziness.
- Bash functions for the win.
- Going overboard because limits can only be found by crossing them.
- Bonus round exploring alternatives to Git aliases.
Go to part two!
Published: Apr 8, 2024