Speed up everyday work with handy Git aliases

Published on September 13, 2020

Git allows us to define aliases, which are basically our own commands we can use. They may be just a calls for other commands with parameters, or even shell scripts. Possibilities are unlimited.

Do you ever google for this Git command you forgot every time? Often execute several commands one by one, every time in the same combination for a final effect? Or saw a really nice Git command on the internet, but with way too more flags to use it in a real-life? Git aliases are the solution.

Here I will show Git aliases that I use in everyday work. With explanation.

Defining first Git alias

Aliases are part of the Git configuration that is saved to the ~/.gitconfig file. They can be added or modified directly by editing this file, or by executing a command that will do this for us.

Let’s create an alias to the git status command that will be just a git s command – simple abbreviation.

git config --global alias.s status

Alternatively, we can open the ~/.gitconfig file (creating it if it does not already exist) and add this config lines by hand:

[alias]
	s = status

In both cases, we add new alias named s for status command. Since then those two calls are equal.

git status
git s

In ~/.gitconfig file more aliases may be added to the [alias] block, we don’t need to repeat it. We just add next lines under it.

Here I will show all examples in a form of file lines. You don’t repeat [alias] block name, but I will put them in every snippet just to be clear on to which group we should add it.

Useful Git aliases for everybody

Handy shortcuts for Git commands

[alias]
	s   = status
	c   = commit
	go  = checkout
	gob = checkout -b
	d   = diff
	dc  = diff --cached

It may look silly at first. Oh, we can save 5 letters calling git status.

But when you start to use those short command versions, you will find it frustrating to go back. Those versions are two times shorter (including git command call at the beginning) than normal ones. It means you execute them two times faster. And for a git status and git commit commands, that we usually execute multiple times a day, it’s a significant change. You stop spending time typing commands, but rather analyzing the results instead. It’s also one second of distraction from work less.

git s
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

git gob feature-1
Switched to a new branch 'feature-1'

echo "some changes" >> README.md

git s
On branch feature-1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

git a
On branch feature-1
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	 modified:   README.md

git c -m 'Update README'
[feature-1 2b9345f] Update README
 1 file changed, 1 insertion(+)

Alias I use to move between the branches, git go, has a new alternative available from Git 2.23. It’s git switch, which is dedicated to changing branches. But still, I prefer my shorter version.

Beautiful and meaningful Git history log

[pretty]
	better-oneline = "format:%C(auto)%h%d %s %Cblue[%cn]"

[alias]
	tree    = log --pretty=better-oneline --all --graph
	ls      = log --pretty=better-oneline
	ll      = log --pretty=better-oneline --numstat

	details = "!f() { git ll "$1"^.."$1"; }; f"

For those we need to declare a new git log format first, so we can use it in all three aliases.

The first 3 aliases (lines #5-7) are used to show Git history in a nicer form. git tree is my favorite one and most used, as it shows a branching graph similar to the one shown on GitHub or GitLab. Irreplaceable in a branch-based workflow.

You can think about git ls and git ll as an equivalent of shell ls command for files listing. The first version will show commits history in a nice, one-line-per-commit format. The second one will additionally show modified files with a number of added and removed lines in each of them.

The custom format we define makes the output of all 3 commands nice and significant. We get short commit hash, message, author, and branch name pointing on that commit, if any.

Last one, git details (line #9), is used to show the same statistics as git ll, but for a single commit. We use it providing commit reference as an argument, for example, git details HEAD to show modified files in the last commit.

This last alias has a different syntax than all previous. It starts with an exclamation mark (!), which makes Git execute it as a shell command rather than git command. The commands will be always executed in a root repository directory. Additionally, we need to wrap it in a function to be safe using positional arguments – see explanation here.

git ls
1a45b2d (HEAD -> master) Merge branch 'feature-1' into master [Maciej Radzikowski]
2fe9377 (feature-1) Update README [Maciej Radzikowski]
b5cc426 change [Maciej Radzikowski]
22653f3 first commit [Maciej Radzikowski]

git tree
*   1a45b2d (HEAD -> master) Merge branch 'feature-1' into master [Maciej Radzikowski]
|\
| * 2fe9377 (feature-1) Update README [Maciej Radzikowski]
|/
* b5cc426 change [Maciej Radzikowski]
* 22653f3 first commit [Maciej Radzikowski]

Plurals for listing all elements

[alias]
	branches = branch -a
 	tags     = tag

Those two aliases are very helpful, especially for beginners. They make listing all branches and tags easy with just a plural form of the word. Additionally, it fixes inconsistency in commands, as we see that in a normal form showing all branches and all tags is done in a different way.

Fast files adding and committing

[alias]
	a   = !cd ${GIT_PREFIX:-.} && git add . && git s
	aa  = !git add -A && git s

 	ac  = !cd ${GIT_PREFIX:-.} && git add . && git c
	aac = !git add -A && git c

Those commands will speed up committing but needs to be used with care.

git a will add files from our current directory and subdirectories to the index/staging area, ready to be committed. It will also show git status right away, so you will see what is the state of your repository after the call. Remember to check if no random files were added automatically with this command by a mistake.

git aa will do a very similar thing but will add all modified files from the repository, no matter from what directory you will call it. You can memorize these two commands as “add” and “add all”.

The other two aliases are extended versions of previous ones. They will additionally commit changes. Using them is very helpful and time-saving, but we need to be sure only what we want is committed.

git s
On branch feature-2
nothing to commit, working tree clean

echo "new change" >> README.md

git ac -m 'Update README'
[feature-2 4e28b6b] Update README
 1 file changed, 1 insertion(+)

git s
On branch feature-2
nothing to commit, working tree clean

Clearing Git workspace and index

[alias]
	unstage  = reset HEAD
	cleanout = !git clean -df && git checkout -- .

The first alias, unstage, does the opposite of git aa, that we defined previously. It’s a shortcut for removing all files from the index/staging area. It doesn’t undo changes done in the files. They just go back to the workspace.

The next alias behaves differently. It acts on the files in the workspace (not added to the staging area with git add yet). git cleanout will undo all changes from the files in the workspace and remove all new, never committed files. Effectively we will get a clean repository state of the last commit.

Carefully, both discard and cleanout will cause you loos uncommitted changes!

git s
On branch feature-3
nothing to commit, working tree clean

echo "some change" >> README.md

touch new-file-1

git a
On branch feature-3
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   README.md
    new file:   new-file-1

git unstage
Unstaged changes after reset:
M	README.md

git s
On branch feature-3
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    new-file-1

no changes added to commit (use "git add" and/or "git commit -a")

git cleanout
Removing new-file-1

git s
On branch feature-3
nothing to commit, working tree clean

Undoing commits and merges

[alias]
	uncommit = reset --soft HEAD~1
	unmerge  = reset --hard ORIG_HEAD

How many times did you realize that you committed or merged branches too soon? Here are commands to revert it easily. They do exactly what the names suggest.

git uncommit will remove the last commit. All committed changes will go back to the index, nothing will be lost. You can change them and commit them again. This is helpful to use instead of git commit --amend when we need to make some bigger modifications or wait longer before committing.

The other alias does a similar thing for branch merges. When we do git merge and then git unmerge right away, we will go back to the repository state from before the merge. This time no files will be found in the index after the command, and if we did some merge conflicts resolution – it will be lost. It’s important to know that we can do unmerge safely only right after the merge, as it uses special pointer ORIG_HEAD created during the merge. It may be changed by other Git commands, so calling it in any other circumstances could lead us to lose other commits and their content.

Both these commands rewrite Git history by removing commits. A good practice is not to do such operations on commits already pushed to the remote repository. In fact, without special permissions to the remote repository, we won’t be even able to push such changes. Therefore it’s best to use uncommit and unmerge only when we spot a mistake right after we do it, so we can fix it before it leaves our computer.

Showing Git merge details

[alias]
	merge-span  = "!f() { echo $(git log -1 $2 --merges --pretty=format:%P | cut -d' ' -f1)$1$(git log -1 $2 --merges --pretty=format:%P | cut -d' ' -f2); }; f"
	merge-log   = "!git ls `git merge-span .. $1`"
	merge-diff  = "!git diff `git merge-span ... $1`"

Git branch merges may cause a lot of headache, especially for beginners. Here is a way to at least understand and review what was introduced by a given merge.

While merge-span is only a subsidiary function, merge-log and merge-diff show us a list of commits added by a merge and all introduced changes. We can put a merge commit hash as a parameter to review the chosen merge, otherwise, the last found merge on the current branch will be shown.

git gob feature-4
Switched to a new branch 'feature-4'

echo "first change" >> README.md

git ac -m 'Edited README'
[feature-4 c6fca5d] Edited README
 1 file changed, 2 insertions(+)

echo "second change" >> README.md

git ac -m 'Edited README again'
[feature-4 5a09b0d] Edited README again
 1 file changed, 1 insertion(+)

git go master
Switched to branch 'master'

git merge feature-4
Merge made by the 'recursive' strategy.
 README.md | 2 ++
 1 file changed, 2 insertions(+)

git merge-log
5a09b0d (feature-4) Edited README again [Maciej Radzikowski]
c6fca5d Edited README [Maciej Radzikowski]

Summary

Having aliases for most common actions and using them will speed up and simplify everyday work, allowing us to get most from the version control system.

There is nothing stopping you from adding more aliases. You can create them by yourself based on your most common actions. There are also plenty of other ready to use aliases created by various people. I’ve also taken some of my aliases from them and got inspired by others. Here they are:

If you have your own favorite aliases, share them in the comments. If I find something useful for most programmers, I will add it to the snippets in the article.

Category: Tools