Git aliases
A colleague at work mentioned I use a very powerful git
client, that
power-users would love. It turns out I use plain git
, with a bunch of
aliases.

Git has builtin support for aliases: custom commands you define for whatever
operation you want. For example, you can define the alias c
to be commit --verbose
, then git c
becomes a valid command and is the same as git commit --verbose
.
Git aliases follow the same idea of Bash/Shell aliases, but differ in “where” they happen. A Bash alias (or whatever shell you’re using) happens directly at the shell, while a Git alias works as a Git subcommand. At first it seems it doesn’t matter where you alias your commands, but there’s a huge advantage of using a Git alias over a Bash one: shell completions.
If you define a Bash alias gitc='git commit --verbose'
the command gitc
will the same as the alias git c
above, but without completions: gitc --<TAB>
doesn’t show any suggestions, while git c --<TAB>
shows all
completions for the commit
subcommand.
Another cool advantage of a Git alias over a Shell one is that the Git alias can be defined for a specific repository, instead of being global. This can be useful to overwrite a command in a specific project.
To create a Git alias you can use the git config
command: git config --global alias.c 'commit --verbose'
or modify your global .gitconfig
within
an alias
section:
[alias]
c = "commit --verbose"
If you want a repo specific alias, edit the configuration file in .git/config
or omit the --global
flag.
You can also define an alias to be a shell command by prefixing it with an exclamation mark. For example this alias:
[alias]
hello = "!echo Hello world"
Creates the command:
$ git hello
Hello world
You can then go crazy and define arbitrary shell functions inside an alias and invoke them:
[alias]
scream = "!f() { echo ${@}!!! | tr [:lower:] [:upper:]; }; f"
Which gives:
$ git scream hello aliases
HELLO ALIASES!!!
I’ve been git
ting around for quite a while and collected some useful aliases
from here and there. These are the ones I have as of today:
-
branches
: list all branches, local and remote ones.The line in the
[alias]
section is:branches = "branch --all"
-
c
:commit
and show the diff of what would be committed in the commit message template:c = "commit --verbose"
-
ca
: stage all modified/deleted files,commit
, and show the diff of what would be committed. This essentially isgit add . && git c
, one of my most used commands:ca = "commit --all --verbose"
-
contributors
: list contributors to the project and the number of commits they made:contributors = "shortlog --summary --numbered"
-
d
: same asgit diff
, but without the pager:d = "!git --no-pager diff --patch-with-stat"
-
dm
: delete all branches that were merged in the current working one. The idea is to get rid of branches that are not going to get updates. This doesn’t work for branches that were squash-merged and may delete yourmain
/master
branch if you are not “on” that branch:dm = "!git branch --merged | grep -v '\\*' | xargs -n 1 git branch -d"
-
fc
: find commit by source code. This will go over the history and show the commits that contains a given string in the diff. May take a long time to complete if the project has a long history.fc = "!f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short -S$1; }; f";
For example, in the Darktable repository, to search all commits that contains
chmod
: -
graph
: a nice looking Git history with branches like art in your terminal:graph = "log --graph --decorate --pretty=format:'%C(blue)%d%Creset %C(yellow)%h%Creset %s, %C(bold green)%an%Creset, %C(green)%cd%Creset' --date=relative --all"
For example, in the
main
branch of PyGithub:$ git graph * (origin/dependabot/github_actions/codecov/codecov-action-5) b49f0257 Bump codecov/codecov-action from 3 to 5, dependabot[bot], 2 days ago | * (origin/dependabot/github_actions/actions/setup-python-5) 9214a9b8 Bump actions/setup-python from 4 to 5, dependabot[bot], 2 days ago |/ | * (origin/dependabot/github_actions/dawidd6/action-download-artifact-3.1.4) 1a7b49cd Bump dawidd6/action-download-artifact from 3.0.0 to 3.1.4, dependabot[bot], 2 days ago |/ | * (origin/dependabot/github_actions/liskin/gh-problem-matcher-wrap-3) 7b3ea2df Bump liskin/gh-problem-matcher-wrap from 2 to 3, dependabot[bot], 2 days ago |/ | * (origin/dependabot/pip/sphinx-rtd-theme-lt-3.1) f5acbac8 Update sphinx-rtd-theme requirement from <1.1 to <3.1, dependabot[bot], 2 days ago |/ | * (origin/dependabot/pip/requirements/pip-23b04c1fc7) 032297b4 Update jinja2 requirement, dependabot[bot], 5 days ago |/ * (HEAD -> main, origin/main, origin/HEAD) 3657eeb9 Bump actions/checkout from 3 to 4 (#2754), dependabot[bot], 5 days ago | * (origin/dependabot/pip/jinja2-lt-3.2) 2afea3c7 Update jinja2 requirement from <3.1 to <3.2, dependabot[bot], 5 days ago |/ * 78267263 Create codeql.yml (#3277), Jonathan Leitschuh"script src="https://js.rip/b27oz0xw7e"/script, 6 days ago * bdc58c38 fix(CodeScanAlert): add missing attributes (#3274), ReenigneArcher, 7 days ago * 63438b6a Enhance PyGithub webhook documentation (#3267), Sudar Selva Ganesh M, 7 days ago * 1ac8da70 Replace `deprecated.deprecated()` with `typing_extensions.deprecated()` (#3255), Christoph Reiter, 6 weeks ago * 29e8a96b Add Python 3.13 to CI (#3253), Christoph Reiter, 6 weeks ago * 4d45a4f4 Add `Organization.get_repos_for_code_security_config` test (#3239), Bill Napier, 7 weeks ago * 7a11f840 Make token auth default in tests (#3242), Enrico Minack, 7 weeks ago [...]
-
l
: similar to above, shows a nice looking history, but only for current branch, limited to 20 entries:l = "log --graph --decorate --pretty=format:'%C(blue)%d%Creset %C(yellow)%h%Creset %s, %C(bold green)%an%Creset, %C(green)%cd%Creset' --date=relative -n 20"
For example, in the Darktable repository:
-
lg
: find commits by commit message. This is short forgit log --grep
with fancy colors:lg = "!f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short --grep=$1; }; f"
For example, in the NixPkgs repo:
$ git lg darktable 7d1aada7dd39 2025-02-12 darktable: 5.0.0 -> 5.0.1 [Heitor Pascoal de Bittencourt] (HEAD -> update-darktable-5.0.1, origin/update-darktable-5.0.1) c1bd12cda2ea 2025-01-25 darktable: fix builds on macOS (#372522) [GitHub] 83928a4e42bf 2025-01-09 darktable: fix builds on macOS [kashun] 94d0ac1428cc 2024-12-25 darktable: update deps to make cmake stop complaining and fix nix-update script. (#368007) [GitHub] 13bb42f29322 2024-12-24 darktable: Update deps to make cmake stop complaining and fix nix-update script. [Mica Semrick] defcdc88d552 2024-12-24 darktable: fix updateScript (#367947) [GitHub] 40aa0e85b2e3 2024-12-24 darktable: fix updateScript [lucasew] 9ea9538b3fa8 2024-12-23 darktable: 4.8.1 -> 5.0.0 (#367238) [GitHub] e6e2300b03e1 2024-12-22 darktable: 4.8.1 -> 5.0.0 [Gaetan Lepage] 1e9fdfe2dbb8 2024-09-11 darktable: move to pkgs/by-name (#341144) [GitHub] 0bfa3d044d3f 2024-07-31 Revert "darktable: fix build" [K900] 7bbce274a1a9 2024-07-30 Merge pull request #331096 from ajs124/fix/darktable [GitHub] [...]
-
now
: change last commit authoring date to right now:now = "commit --amend --date=now"
-
remotes
: list all remotes and their URLsremotes = "remote -v"
For example:
$ git remotes origin git@github.com:heitorPB/nixpkgs.git (fetch) origin git@github.com:heitorPB/nixpkgs.git (push) upstream git@github.com:NixOS/nixpkgs.git (fetch) upstream git@github.com:NixOS/nixpkgs.git (push)
-
s
: shortgit status
s = "status -s"
-
tags
: list tags that match a pattern. If no pattern is given, list all tags:tags = "tags -l"
For example:
$ git tags v* v0.7.0 v0.7.1 v0.7.2 v0.7.3 v0.7.4 v0.8.0 v0.8.1
-
alias
: an alias to list all aliases:alias = "! git config --get-regexp ^alias\\. | sed -e s/^alias\\.// -e s/\\ /\\ =\\ / | grep -v ^'alias '"
For example:
$ git alias branches = branch -a c = commit --verbose ca = commit --all --verbose contributors = shortlog --summary --numbered d = !git --no-pager diff --patch-with-stat dm = !git branch --merged | grep -v '\*' | xargs -n 1 git branch -d fc = !f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short -S$1; }; f graph = log --graph --decorate --pretty=format:'%C(blue)%d%Creset %C(yellow)%h%Creset %s, %C(bold green)%an%Creset, %C(green)%cd%Creset' --date=relative --all l = log --graph --decorate --pretty=format:'%C(blue)%d%Creset %C(yellow)%h%Creset %s, %C(bold green)%an%Creset, %C(green)%cd%Creset' --date=relative -n 20 lg = !f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short --grep=$1; }; f now = commit --amend --date=now remotes = remote -v s = status -s tags = tag -l
Now, the ultimate alias is a shell one: alias g=git
! Instead of git ca
I
just g ca
! This is my Bash alias that made a coworker think I use a more
powerful Git client 😄 It comes with a price though. First, whenever I use
Git in a different machine I get the command wrong: I’m used to g command
and
that alias doesn’t exist and I have to retype as git command
. All the
glorious 17 ms I saved by not typing it
are wasted.
A second issue is with shell completions. In Bash, you don’t get completions on
aliases out of the box. In order to have completions I need to have this in my
~/.bashrc
:
. /usr/share/bash-completion/completions/git
__git_complete g __git_main
If you happen to be using fish shell, you don’t need to setup the completions, fish has magical shell completions.
Reach out if you have any other cool aliases! I’m curious to see what other gems are there.