Better living through git aliases
Tags: git, productivity
Published: 2020-06-04 08:35:05

There are many varied and wonderful git clients available, but by far the fastest, most flexible and most extensible is the `git` commandline itself. The problem is that it's ... a bit of a mess. There have been some improvements over the years, like [changing `push.default`'s default](https://stackoverflow.com/q/21839651) and [splitting `git checkout` in two](https://www.infoq.com/news/2019/08/git-2-23-switch-restore/). But there are still fundamental problems, like the same concept being referred to as the ["index"](https://git-scm.com/docs/git-add), ["staging area"](https://git-scm.com/docs/git-restore#Documentation/git-restore.txt---staged) or ["cache"](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-emgitdiffemltoptionsgt--cachedltcommitgt--ltpathgt82308203) depending on the command.

Fortunately, we can mitigate a lot of the problems with `git` using [aliases](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases). A personalized set of aliases can help you be much more productive. I’m going to take you through some of the aliases I’ve created so far, but I recommend starting with the default git commands for a while and gradually introducing aliases as you see fit. One of the great things about aliases is they can become completely personal to the way you want to work.

There are a couple of ways to create aliases. You can create them on the commandline using something like `git config --global alias.co checkout`, or you can edit aliases in a text editor by running `git config --global --edit`. This will open an editor for your user-wide git config, where aliases will be in the `[alias]` section.

If you're ever unsure about what an alias does, then `git help` will quickly expand the alias for you:
```
> git help di
'di' is aliased to 'diff --color-words'
```

The simplest kind of aliases reduce the typing needed for some other command:

```
st = status
ci = commit --verbose
fa = fetch --all
co = checkout
di = diff --word-diff
amend = commit --amend
ama = commit --amend -a --no-edit
aa = add -A
mt = mergetool
rhard = reset --hard
```

This next one is pretty incomprehensible! It produces a useful git log that shows branches with one line per commit, much like you'd get in a UI client:

```
lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%h%C(reset) - %C(bold green)(%ar)%C(reset) %s%C(reset) %C(bold green)- %an%C(reset)%C(bold yellow)%d%C(reset)'
```
By default the log will expand to fill the terminal and let you page through the history. If you're looking for a recent commit you can limit the output with an extra option: `git lg -20`.

The next few aliases I use get a bit more complicated. You can combine aliases with other options, or make variants of the aliases like so:

```
cia = commit --verbose -a
cip = commit --verbose -p
dc = diff --cached --word-diff
fap = fetch --all --prune
cob = checkout -b
```
The next alias is pretty important. [Git pull is broken](https://felipec.wordpress.com/2014/05/27/is-git-pull-broken/), so I've aliased `git p` to a safe version. Since I'm lazy and it's fewer characters to type, I'll generally do the right thing :)

```
p = pull --ff-only
```

What happens if the above command fails to do a fast-forward? Well you might need to rebase, or merge, or you might just decide to discard your current branch and update to the version of the branch from the remote:

```
rhu = reset --hard @{upstream}
```
Here, `@{upstream}` is a useful alias for whatever the remote tracking branch is (usually `origin/foo` for some branch `foo`). It's a bit fiddly to type, though, so having the alias here is especially handy.

This next one lets you do a fast-forward on master while a different branch is currently checked out. Useful when other people have pushed changes to master that you want in your branch, since you don't need to switch branches before rebasing or merging.

```
fomm = fetch origin master:master
```

I used to have an alias like `dlast = diff HEAD^ HEAD --word-diff` for checking the contents of a commit that just happened, but later I discovered `git show` without any arguments will do the same thing :)

`hg outgoing` and `hg incoming` are a couple of commands I used to really miss from mercurial, but haven't used much in the last few years. These days it's rare that we'll have multiple people committing to the same branch, but they might still be useful to other people.

These aliases are a little more complicated than the previous ones - the `!` at the beginning means "pass this to the shell" rather than just being a direct alias for another command. In this case we immediately run `git` again, but we could potentially use other commands as well.

```
outgoing = !git lg FETCH_HEAD..
incoming = !git fetch && git lg ..FETCH_HEAD
outin = !git fa && git lg --left-right ...FETCH_HEAD
```

Here's another one I haven't used in a few years, but has been especially useful in the past:
```
addw = !git diff -b | git apply --ignore-whitespace --cached
```
Short for “add-whitespace-insensitive”: stage changes while ignoring whitespace. Really useful when certain editors don't like whatever line endings you're using and decide to flip them over. Note: this breaks in powershell, since powershell implicitly changes strings in its pipeline to all have CRLF line endings! :facepalm:

Has this ever happened to you?
```
$ git push
fatal: The current branch foo has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin foo
```
It's super annoying, and while [there are more amusing solutions](https://github.com/nvbn/thefuck) I like the following alias, which figures out the current branch name and automatically inserts it into the command:

```
puom = !git push --set-upstream origin `git rev-parse --abbrev-ref HEAD`
```
Now you just have to remember to use `git puom` instead of `git push` for the first push of a new branch.

And one final one:
```
fix-master = update-ref refs/heads/master origin/master HEAD
```
`fix-master` is useful when you start making commits, but realize you forgot to make a branch! You can checkout a new branch to point at the commit you're currently on, then use `fix-master` to repoint the `master` branch back where it should be.

A side note: If you’re working on windows, there’s a couple of things you’ll want in order to make the commandline actually pleasant to use. The first is a decent shell. This can be powershell, git bash or something similar - you'll probably want to avoid the default cmd unless you're already used to it. The second is a decent console to run the shell in. I've used ConEmu in the last few years and grown to love it, but [Microsoft are finally investing in a new terminal](https://github.com/microsoft/terminal) which looks very promising.

Other useful links:

- http://haacked.com/archive/2014/07/28/github-flow-aliases/
- https://git.wiki.kernel.org/index.php/Aliases

What other useful aliases can you come up with? Post your favorites in the comments :)