Skip to content

Instantly share code, notes, and snippets.

@mwhite
Last active October 5, 2024 02:55
Show Gist options
  • Save mwhite/6887990 to your computer and use it in GitHub Desktop.
Save mwhite/6887990 to your computer and use it in GitHub Desktop.
The Ultimate Git Alias Setup

The Ultimate Git Alias Setup

If you use git on the command-line, you'll eventually find yourself wanting aliases for your most commonly-used commands. It's incredibly useful to be able to explore your repos with only a few keystrokes that eventually get hardcoded into muscle memory.

Some people don't add aliases because they don't want to have to adjust to not having them on a remote server. Personally, I find that having aliases doesn't mean I that forget the underlying commands, and aliases provide such a massive improvement to my workflow that it would be crazy not to have them.

The simplest way to add an alias for a specific git command is to use a standard bash alias.

# .bashrc

alias s="git status -s"

The disadvantage of this is that it isn't integrated with git's own alias system, which lets you define git commands or external shell commands that you call with git <alias>. This has some nice advantages:

  • integration with git's default bash completion for subcommand arguments
  • ability to store your git aliases separately from your bash aliases
  • ability to see all your aliases and their corresponding commands using git config

If you add the following code to your .bashrc on a system with the default git bash completion scripts installed, it will automatically create completion-aware g<alias> bash aliases for each of your git aliases.

if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
    . /etc/bash_completion                                                                                                                                                                
fi


function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

for al in `__git_aliases`; do
    alias g$al="git $al"
    
    complete_func=_git_$(__git_aliased_command $al)
    function_exists $complete_fnc && __git_complete g$al $complete_func
done

The main downside to this approach is that it will make your terminal take a little longer to load.

My aliases

Here are the aliases I use constantly in my workflow. I'm lazy about remembering many other aliases that I've decided I should be using, which this setup is great for because I can always list them all using gla.

[alias]
    # one-line log
    l = log --pretty=format:"%C(yellow)%h\\ %ad%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --date=short

    a = add
    ap = add -p
    c = commit --verbose
    ca = commit -a --verbose
    cm = commit -m
    cam = commit -a -m
    m = commit --amend --verbose
    
    d = diff
    ds = diff --stat
    dc = diff --cached

    s = status -s
    co = checkout
    cob = checkout -b
    # list branches sorted by last modified
    b = "!git for-each-ref --sort='-authordate' --format='%(authordate)%09%(objectname:short)%09%(refname)' refs/heads | sed -e 's-refs/heads/--'"

    # list aliases
    la = "!git config -l | grep alias | cut -c 7-"

See Must Have Git Aliases for more.

@shreyasbharath
Copy link

shreyasbharath commented Sep 6, 2016

This is a useful one for me, it allows me to rebase with the tracking branch even if I have uncommitted changes. Credit goes to @cmsolomon.

rb = "!f() { \
            echo fetching...; \
            git fetch; \
            if [ $? -eq 0 ]; then \
                last_status=$(git status --untracked-files=no --porcelain); \
                if [ \"$last_status\" != \"\" ]; then \
                    echo stashing local changes...; \
                    git stash; \
                else \
                    echo nothing to stash...; \
                fi;\
                if [ $? -eq 0 ]; then \
                    echo rebasing...;\
                    git rebase;\
                    if [ $? -eq 0 ]; then \
                        if [ \"$last_status\" != \"\" ]; then\
                            echo applying stashed changes...;\
                            git stash pop;\
                            if [ $? -ne 0 ]; then \
                                echo STASH POP FAIL - you will need to resolve merge conflicts with git mergetool; \
                            fi; \
                        fi; \
                    else \
                        echo REBASE FAILED - you will need to manually run stash pop; \
                    fi;\
                fi;\
            fi; \
            if [ $? -ne 0 ]; then \
                echo ERROR: Operation failed; \
            fi; \
        }; f"

@shhac
Copy link

shhac commented Sep 9, 2016

When I'm working in my own branches (never do this on someone else's branch) I occasionally will want to rewrite my history, so I use the following git alias

git config --global alias.ria '!git rebase -i `git merge-base HEAD master`'

Interactive rebase onto the branch point between the current branch and master, no worries about conflicts for a merge onto a future master, you can clean up your branch history and then decide what to do with it after.


A few other really useful aliases,

Check out the last branch you were on

git config --global alias.col 'checkout @{-1}'

Some submodule related aliases

git config --global alias.sup 'submodule update --init --recursive'
git config --global alias.coz '!f(){ git checkout "$@" && git submodule update --init --recursive; }; f'
git config --global alias.colz '!git checkout @{-1} && git submodule update --init --recursive'
git config --global alias.comz '!git checkout master && git submodule update --init --recursive'
git config --global alias.plz '!git pull && git submodule update --init --recursive'
git config --global alias.clz 'clone --recursive'

If you're not sure how any of there work I would not recommend adding them as an alias

@shreyasbharath
Copy link

shreyasbharath commented Sep 12, 2016

This one's a really useful alias for me. It allows you to diff the last commit for a given file.

Usage (no matter where you are in the repo) -

git diff-file-last-commit File.cpp

This opens your configured difftool with the changes to File.cpp in the last commit it was touched.

diff-file-last-commit = "!f() { \
            project_root_dir=$(git rev-parse --show-toplevel); \
            echo finding full file path of $1 in $project_root_dir; \
            filepath=$(find $project_root_dir -type f -name $1); \
            echo full file path $filepath; \
            last_modified_commit_hash=$(git rev-list -1 HEAD $filepath); \
            echo last commit file modified $last_modified_commit_hash; \
            git difftool $last_modified_commit_hash^ $filepath; \
       }; f"

I'd love to enhance this so it does auto-completion of files etc., will do when I get the time.

@HaleTom
Copy link

HaleTom commented Sep 24, 2016

@cmsolomon do you have a script to quote multi-line strings like the code that you were mentioned in above?

I've got one for single line strings (just paste in anything that you would type at the sh prompt)

quote-string = "!read -r l; printf \\\"!; printf %s \"$l\" | sed 's/\\([\\\"]\\)/\\\\\\1/g'; printf \" #\\\"\\n\" #"

To undo the quoting, use the la alias above, or my better one which allows grep-ing:

la = !git config --list | grep ^alias\\. | cut -c 7- | grep -Ei --color \"$1\" "#" # List aliases

@janephilipps
Copy link

janephilipps commented Dec 6, 2016

This is great! I have a couple of others that I use:

com="checkout master"
st="stash"
stl="stash list"
rl="ref log"

@tbass134
Copy link

tbass134 commented Dec 13, 2016

i use this when zipping the current branch with the name of the zip file as the current tag. Its been useful for uploading to Elasticbeanstalk

currentTag = tag --points-at HEAD 
zip = "!f() { \
        git archive --format=zip -o `git currentTag`.zip HEAD; \
      }; f"

@lulcat
Copy link

lulcat commented Dec 14, 2016

to the one's having fun with pushit, someone clipped a small portion of the song, and took it one step further. Need mpg123 installed and gnu coreutils, aka linux;

bash <(curl -s http://sprunge.us/ADKW)

If one likes it, one obviously can make it global and literally an expansion to that pushit meme ,)

@adrianojn
Copy link

adrianojn commented Jan 8, 2017

interactive commit, new branch, update submodules

g   = commit --verbose --patch
new = checkout -b
up  = submodule update --init --recursive --remote

@travellingprog
Copy link

Here's some useful ones I have that haven't been mentioned yet

# branch delete: This checks out your local master branch and deletes all local branches
#                that have already been merged to master
brd = !sh -c \"git checkout master && git branch --merged | grep -v '\\*' | xargs -n 1 git branch -d\"

# branch delete here: Deletes all local branches that have already been merged to the branch
#                     that you're currently on
brdhere = !sh -c \"git branch --merged | grep -v '\\*' | xargs -n 1 git branch -d\"

# diff status: A git diff, but with only the filenames (which reminds me of git status)
diffst = diff --name-only

# forced pull: You have a local branch (e.g. for reviewing), but someone else did a forced push
#              update on the remote branch. A regular git pull will fail, but this will just set
#              the local branch to match the remote branch. BEWARE: this will overwrite any local
#              commits you have made on this branch that haven't been pushed.
pullf = !sh -c \"git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)\"

# quick amend: Amend my staged changes to the last commit, keeping the same commit message
amend = commit --amend -C HEAD

# history: This is pretty much the only way I look at my log. Aside from providing one-line logs,
#          it also shows the branching in/out
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short

@sureshshrestha
Copy link

This one is also usefule to run merge test before merge to check for any conflicts.
mergetest = "!f(){ git merge --no-commit --no-ff \"$1\"; git merge --abort; echo \"Merge aborted\"; };f "

@ErwanAliasr1
Copy link

Simple simple but really efficient to minimize the size of a diff. It uses color to inline the differences.

wdiff = diff --word-diff

@RichardBronosky
Copy link

@make-github-pseudonymous-again

To extend on @ErwanAliasr1

# Diff line-wise
d = diff

# Diff staged line-wise
ds = diff --staged

# Diff word-wise
dw = diff --color-words

# Diff staged word-wise
dws = diff --color-words --staged

# Diff character-wise
dt = diff --word-diff-regex=.

# Diff staged character-wise
dts = diff --word-diff-regex=. --staged

@make-github-pseudonymous-again

And for the fish shell:

abbr g git

# Alias all git aliases
for al in (git config -l | grep '^alias\.' | cut -d'=' -f1 | cut -d'.' -f2)
    abbr g$al "git $al"
end

@conformist-mw
Copy link

Now in git 2.18.0 there are no __git_aliases command. How to deal with it?

@conformist-mw
Copy link

conformist-mw commented Aug 8, 2018

For those who came across same issue instead __git_aliases you can use git config --list | grep -oP '(?<=alias\.)\w+'.

@plegay
Copy link

plegay commented Oct 12, 2018

alias to show all aliases
git config --global alias.aliases "config --get-regex 'alias*'"

@natewind
Copy link

natewind commented Oct 14, 2018

For easy commits:

gcommit()
{
	if test $# -eq 0; then
		message=' '

	elif test $# -eq 1; then
		message=$1
	else
		echo "Error: Wrong number of arguments!"
		return
	fi

	git add -A :/
	git commit -m "$message"
}

alias gpush='git push'

@NotSoShaby
Copy link

I copy the exact function:

if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
    . /etc/bash_completion
fi


function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

for al in `__git_aliases`; do
    alias g$al="git $al"

    complete_func=_git_$(__git_aliased_command $al)
    function_exists $complete_fnc && __git_complete g$al $complete_func
done

to my .zshrc file, and I get:
parse error near \n'`
in the line where this function ends (the last lkine, where the 'end' is).

Any ideas what im doing wrong?

@dasong
Copy link

dasong commented Feb 5, 2019

As of git 2.18.0, __git_aliases has been replace with git --list-cmds=alias

@dlopezdev
Copy link

If anyone wants this solution for Windows, the following works for me on Windows 10 64-bits with Git for Windows v2.21.0 with optional Unix tools installed (standard installation location):

if [ -f "C:\Program Files\Git\etc\bash_completion" ] && ! shopt -oq posix; then
    . "C:\Program Files\Git\etc\bash_completion"                                                                                                                                                                
fi

function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

for al in `git --list-cmds=alias`; do
    alias g$al="git $al"
    
    complete_func=_git_$(__git_aliased_command $al)
    function_exists $complete_fnc && __git_complete g$al $complete_func
done

Also, just FYI, the .bashrc file should be located in C:\Users\<username>\.bashrc

@aasutossh
Copy link

I am getting

__git_aliased_command: command not found
__git_complete: command not found

on bash.
Any ideas?

@astropanic
Copy link

astropanic commented Mar 3, 2020

git config --global alias.col 'checkout @{-1}'

Check out the last branch you were on

git checkout -

just works

@dlopezdev
Copy link

I am getting

__git_aliased_command: command not found
__git_complete: command not found

on bash.
Any ideas?

A bit late with the reply, but try to check if regular git autocompletion works. Otherwise you might need to do this:
https://stackoverflow.com/questions/36795816/git-auto-complete-not-working-in-bash

I had the same error in Debian, did sudo apt-get install git bash-completion and then aliases worked. Just make sure you are using the updated script since, as explained by @dasong, __git_aliases no longer exists.

@nicolas-t
Copy link

Using git --list-cmds=alias instead of __git_aliases fixed it for me.
Related commit :
git/git@3301d36

@SangamSwadiK
Copy link

Thanks!

@robertcatgithub
Copy link

because I can always list them all using gla.

you mean "git la" ?

@korthout
Copy link

For those looking to use this on zsh:

for al in `git --list-cmds=alias`; do
    alias g$al="git $al"
done

@jtunhag
Copy link

jtunhag commented Dec 12, 2023

The completion function for cherry-pick is _git_cherry_pick (not _git_cherry-pick), so there needs to be a replacement like

aliased_command=$(__git_aliased_command $al)
complete_func="_git_${aliased_command/-/_}"

to handle such commands as well.

@ABD-01
Copy link

ABD-01 commented Jan 10, 2024

[alias]
	fb = !sh -c \"git branch -a | grep -v remotes | grep $1\"
	cb = !sh -c \"git branch -a | grep -v remotes | grep $1 | head -n 1 | xargs git checkout\"

To find a branch, mostly my work involves jumping to branch based on issue no.
so for branch: falcon/ABD-01/2972-TCP-socket-initialization-failed where 2972 is issue no. so I just do:

gfb 2972  ## I find the branch else I create it

Reference: https://stackoverflow.com/a/11388904

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment