Skip to content

Instantly share code, notes, and snippets.

@jprivet-dev
Last active June 22, 2024 06:33
Show Gist options
  • Save jprivet-dev/09912ca4188a4ba3c610d7f61c200c38 to your computer and use it in GitHub Desktop.
Save jprivet-dev/09912ca4188a4ba3c610d7f61c200c38 to your computer and use it in GitHub Desktop.
Git and Bash aliases defined and documented in a single `.bash_aliases` file, with Git auto-completion
# #########################################################################################
# GIT AND BASH ALIASES DEFINITION IN A SINGLE FILE `.BASH_ALIASES` WITH GIT AUTO-COMPLETION
# #########################################################################################
# All these git commands and bash aliases are tested on git version 2.17.1 on Linux Mint 19
# This compilation of aliases is an experiment.
# On a daily basis, I use only a few of these aliases ¯\_(ツ)_/¯
# **********
# GIT CONFIG
# **********
git config --global core.excludesFile '~/.gitignore_global' # set up a global .gitignore file (to avoid committing files like .DS_Store, Vim swp files, PHPStorm idea files, etc)
git config --global tag.sort v:refname # version sort ordering
git config --global push.default current # avoid fatal error on push: if the current branch has no upstream branch then it is created on push
git config --global push.followTags 1 # push the commits and tags in one go
git config --global rerere.enabled 1 # record and reuse previous conflicts resolutions
git config --global help.autocorrect 1 # auto correct typos
git config --global rebase.autosquash 1 # enable Git's autosquash feature by default
git config --global pull.rebase 1 # rebase branches by default on pull (it's not a requirement :P)
#git config --global core.autocrlf 1 # on Windows
git config --global core.autocrlf input # on Linux and macOS
git config --global color.status.untracked 'white red' # specific colors (white on red) for untracked files
git config --global init.defaultBranch main
# PHPSTORM
# @see https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html
# On linux :
# 1. $ ln -s ~/path/to/PhpStorm-192.xxxx.xx/bin/phpstorm.sh /usr/local/bin/phpstorm
# 2. $ phpstorm ==> launch phpstorm!
git config --global diff.tool phpstorm # set the diff tool to show changes
git config --global difftool.prompt 0 # disable prompt before each invocation of the diff tool
git config --global difftool.phpstorm.cmd 'phpstorm diff "$LOCAL" "$REMOTE"' # PHPSTORM command to invoke to show changes
git config --global difftool.phpstorm.trustExitCode 1 # exit difftool if PHPSTORM returns a non-zero exit status
git config --global merge.tool phpstorm # set the merge tool to resolve merge conflicts
git config --global mergetool.phpstorm.cmd 'phpstorm merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"' # PHPSTORM command to invoke to resolve merge conflicts
git config --global mergetool.phpstorm.trustExitCode 1 # exit mergetool if PHPSTORM returns a non-zero exit status
git config --global mergetool.keepBackup 0 # do not preserve *.orig files after merge conflict resolution @see https://stackoverflow.com/a/1251696/13480534
# ***********
# GIT ALIASES
# ***********
#git config --global alias.<alias> '<action>' # <documentation> | <arguments>...
# ------
# CONFIG
# ------
git config --global alias.alias "!git config -l | grep 'alias\.' | sort | cut -d '.' -f 2" # list all git aliases
git config --global alias.conf '!git config --list | sort' # list all sortered variables set in config file, along with their values
# ------------------
# DIFF & MERGE TOOLS
# ------------------
# @see previously the configuration with PHPSTORM
git config --global alias.comparefile '!f() { git difftool $1 $2 -- $3; }; f' # compare a file across two branches | <branch_1> <branch_2> <file>
git config --global alias.resolve 'mergetool' # run merge tool to resolve merge conflicts
# --------------
# SIMPLE ALIASES
# --------------
git config --global alias.a 'add' # simple 'add' alias
git config --global alias.b 'branch' # simple 'branch' alias
git config --global alias.ck 'checkout' # simple 'checkout' alias
git config --global alias.cp 'cherry-pick' # simple 'cherry-pick' alias
git config --global alias.d 'diff' # simple 'diff' alias
git config --global alias.dt 'difftool' # simple 'difftool' alias
git config --global alias.mt 'mergetool' # simple 'mergetool' alias
git config --global alias.pl 'pull' # simple 'pull' alias
git config --global alias.ps 'push' # simple 'push' alias
git config --global alias.r 'reset' # simple 'reset' alias
git config --global alias.s 'status -s' # simple 'status -s' alias
git config --global alias.sub 'submodule' # simple 'submodule' alias
# ----
# LOGS
# ----
# show / list
git config --global alias.last 'log -1 HEAD' # show last commit
git config --global alias.count 'shortlog -sn' # number of commits per author
git_log_graph="log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s'"
git config --global alias.l "$git_log_graph -12" # show the last 12 commit logs (graphical representation)
git config --global alias.ll "$git_log_graph" # show all commit logs (graphical representation)
git config --global alias.lmerges "$git_log_graph --merges" # show all merge logs (graphical representation)
git config --global alias.ltags "!git $git_log_graph | grep 'tag:'" # show all tagged logs (graphical representation)
git config --global alias.lfile "$git_log_graph --follow" # find commit logs for a specific file (graphical representation) | <file>
git config --global alias.lfind "$git_log_graph --grep" # find commit messages that contains a specific text | <text>
git config --global alias.lauthor "$git_log_graph --author" # find commit logs by author | <author>
# ------
# SEARCH
# ------
git config --global alias.find 'grep --break --heading --line-number' # displays all lines (per file) containing the searched text | <text>
# --------
# BRANCHES
# --------
# show / list
git config --global alias.br '!git fetch --all && git branch -r' # get only remote branches
git config --global alias.current 'rev-parse --abbrev-ref HEAD' # get the name of current branch
git config --global alias.recent "!git branch --sort=-committerdate --format='%(HEAD) %(refname:short);%(committerdate:relative);%(authorname);%(subject)' | column -t -s ';'"
# show all local branches ordered by recent commits
# switch
git config --global alias.previous 'checkout -' # quickly switch to the previous branch
# create
git config --global alias.copy 'checkout --track' # create local branch from remote branch | <remote_branch>
git config --global alias.new 'checkout -b' # create and switch on new branch WITHOUT push new branch on origin | <new_branch>
# rename
git config --global alias.rename '!f() { \
[ -z "$2" ] && o=$(git current) || o=$1 && n=${2:-$1} \
&& git branch -m $@ && echo "Rename branch $o to $n" \
; }; f' # rename one local branch | [<old_branch>] <new_branch>
git config --global alias.renameall '!f() { \
[ -z "$2" ] && o=$(git current) || o=$1 && n=${2:-$1} \
&& git branch -m $@ && git push origin -u $n && git push origin -d $o \
&& echo "Rename branch $o to $n (local & origin)" \
; }; f' # rename one local & remote branch (origin) | [<old_branch>] <new_branch>
# delete
git config --global alias.delete 'branch -d' # delete local branch | <branch>...
git config --global alias.deletef 'branch -D' # forcefully delete local branch | <branch>...
git config --global alias.deleter 'push --delete' # delete remote branch | <remote> <branch>
git config --global alias.deleteall '!git branch -d $@ && git push --delete origin' # delete local and remote branches (origin) | <branch>...
# cleaning
grep_without_branches="grep -v '\*\|main\|dev\|staging'"
git config --global alias.merged "!f() { git branch --merged \$1 | $grep_without_branches; }; f" # show local merged branches except HEAD, main, dev* & staging | [[<remote>/]<branch>]
git config --global alias.no-merged "!f() { git branch --no-merged \$1 | $grep_without_branches; }; f" # show local not merged branches except HEAD, main, dev* & staging | [[<remote>/]<branch>]
git config --global alias.delete-merged '!f() { \
echo -n "Do you want to delete all merged branches [yn]? " && exec </dev/tty && read r \
&& [ "$r" != "y" ] && echo "Cancelled!" || (echo "OK! Remove all" \
&& git merged $1 | xargs git branch -d) \
; }; f' # delete local merged branches except HEAD, main, dev* & staging | [[<remote>/]<branch>]
git config --global alias.prunedr "fetch --prune --dry-run" # show references to remote branches that have been deleted in the remote
git config --global alias.prune "fetch --prune" # prune references to remote branches that have been deleted in the remote
# compare
git config --global alias.compare '!f() { git diff $1..$2; }; f' # display the differences between two branches (with current by default) | <branch_1> [<branch_2>]
# extract
git config --global alias.grab '!f() { git checkout $1 -- $2; }; f' # grab just one file from another branch | <branch> <file>
# -------
# CHANGES
# -------
# add / index
git config --global alias.all 'add --all' # add all changes (new, modified and deleted files - same as 'add .')
git config --global alias.addn '!git add $(git ls-files --others --exclude-standard)' # add NEW files, without modified and deleted
git config --global alias.addm '!git add $(git diff --name-only --diff-filter=M)' # add MODIFIED files, without new and deleted
git config --global alias.addd '!git add $(git ls-files --deleted)' # add DELETED files, without new and modified
git config --global alias.addnm 'add . --ignore-removal' # add NEW and MODIFIED files, without deleted
git config --global alias.addmd 'add --update' # add MODIFIED and DELETED files, without new
# show / list
git config --global alias.dw 'diff -w --word-diff --color-words' # show changes (ignore whitespace / word diff / without [-...-]{+...+})
git config --global alias.ds 'diff --staged' # show changes staged for commit
git config --global alias.dsw 'diff --staged -w --word-diff --color-words' # show changes staged for commit, like 'dw'
# reset / clean / discard
git config --global alias.discard 'checkout --' # discard changes made on a file | <file>
git config --global alias.untrackeddr 'clean -f -d --dry-run' # show the list of all untracked changes to be removed
git config --global alias.untracked 'clean -f -d' # remove all untracked changes
git config --global alias.hard 'reset --hard' # discard any changes to tracked files, since last commit | [<hash>]
git config --global alias.cleanup 'stash push -u -m "cleanup (safe)"' # remove all deleted, modified and untracked (new) files (more safe than 'clean -f -d')
git config --global alias.cleanup-restore 'stash pop' # restore last 'cleanup' action (see 'git stash list' to find specific stash)
# -------
# COMMITS
# -------
# show / list
git config --global alias.changelog '!f() { n=${1:-20}; git shortlog HEAD~$n..; }; f' # show the last 20 or 'n' commits grouped by author | [<n>]
# create
git config --global alias.c 'commit -m' # create commit with message | <msg>
# amend / undo
git config --global alias.amend 'commit --amend -m' # reword the previous commit message | <msg>
git config --global alias.noedit 'commit --amend --no-edit' # modify previous commit without modifying the commit message
git config --global alias.undo '!f() { git reset --soft HEAD~$1; }; f' # undo the last commit or the last 'n' commits, while keeping files changes | [<n>]
# ----
# PULL
# ----
#git config --global alias.plo 'pull origin' # ! Not effective - Auto-completion does not detect that the 'origin' remote is already indicated
# and does not propose after the listing of the branches names.
# ----
# PUSH
# ----
# (trick: start with '!git push ...' for good branch completion)
git config --global alias.psu '!git push -u' # push a new local branch to remote repository and track | <remote> <branch>
git config --global alias.pushu '!git push -u' # push a new local branch to remote repository and track (same as 'git psu') | <remote> <branch>
git config --global alias.psf '!git push --force-with-lease' # push force in safety mode | <remote> <branch>
git config --global alias.pushf '!git push --force-with-lease' # push force in safety mode (same as 'git psf') | <remote> <branch>
#git config --global alias.pso 'push origin' # ! Not effective - Auto-completion does not detect that the 'origin' remote is already indicated
# and does not propose after the listing of the branches names.
# ---------
# SUBMODULE
# ---------
# (WIP & experimental!!!) - @see https://git-scm.com/book/en/v2/Git-Tools-Submodules
# clone
git config --global alias.sclone 'clone --recurse-submodules' # clone a repository, initialize and update each submodule | <clone_opts>...
# diff
git config --global alias.sdiff "!git diff && git submodule foreach 'git diff'" # nice unified diff of what is changed in your main project and all your subprojects as well
#git config --global alias.sdiff 'diff --cached --submodule' # see that the submodule was updated and get a list of commits that were added to it
# push / update
git config --global alias.spush 'push --recurse-submodules=on-demand' # Git went into the submodules and pushed their before pushing the main project
# If only one submodule push fails for some reason, the main project push will also fail
git config --global alias.supdate 'submodule update --remote --merge' # update the checkout to the tracked branch (main by default) of the submodule repository | [<path>...]
#git config --global alias.supdate 'submodule foreach git pull' # update all the submodules
# config
git config --global alias.strack '!f() { git config -f .gitmodules submodule.$1.branch $2; }; f' # set the default tracked branch of the submodule (for 'supdate') | <repository> <branch>
git config --global alias.sconfig 'config -f .gitmodules' # read configuration from the file .gitmodules
# ----
# TAGS
# ----
# @see https://git-scm.com/book/en/v2/Git-Basics-Tagging
# show / list
git config --global alias.tlast 'describe --tags --abbrev=0' # show the most recent tag on the current branch
# delete
git config --global alias.tdelete 'tag -d' # delete local tag | <tag>
git config --global alias.tdeleter '!f() { git push origin :refs/tags/$1; }; f' # delete remote tag | <tag>
# -------
# ARCHIVE
# -------
git config --global alias.zip '!f() { git archive $1 --format=zip --output=$1.zip; }; f' # archive branch | <branch>
# ----
# GITK
# ----
git config --global alias.visual '!gitk' # open git repository browser
# -----
# STATS
# -----
git config --global alias.contributors '!git log --format='%aN' | sort -u' # get the list of contributors for repository
# --------------------------------------------
# GIT ALIASES - FORCE SPECIFIC AUTO-COMPLETION
# --------------------------------------------
# Sometimes a complex Git alias can lose auto-completion of the correct Git command.
# In this case, you need to specify the auto-completion you want.
# @see https://coderwall.com/p/d2w7oa/auto-completion-within-complex-git-alias
#
# for 'x' alias, use 'function _git_x { _git_<command>; }'
# for 'x-yz' alias, use 'function _git_x_yz { _git_<command>; }'
# for 'x.yz' alias, no function available
function _git_deleteall { _git_branch; }
function _git_new { _git_branch; }
function _git_rename { _git_branch; }
function _git_renameall { _git_branch; }
# ************
# BASH ALIASES
# ************
alias reload='. ~/.bashrc'
# BASH ALIASES WITH GIT AUTO-COMPLETION
#
# By default, Bash aliases don't benefit from Git auto-completion.
# You need to specify the Git auto-completion you want.
# @see https://stackoverflow.com/a/24665529/13480534
#
# use '__git_complete <bash_alias> _git_<git_command>'
# or '__git_complete <bash_alias> _git_<git_alias>' (_git_<git_alias> must be defined: see 'GIT ALIASES - FORCE SPECIFIC AUTO-COMPLETION')
# avoid error "__git_complete: command not found"
# @see https://stackoverflow.com/a/47496210/13480534
[ -f /usr/share/bash-completion/completions/git ] && . /usr/share/bash-completion/completions/git
# @see https://stackoverflow.com/a/24665529
alias g='git'; __git_complete g __git_main
alias gti='git'; __git_complete gti __git_main # because it could happen to anyone :P
alias a='git a'; __git_complete a _git_add
alias all='git all'
alias addn='git addn'
alias addd='git addd'
alias addm='git addm'
alias addnm='git addnm'
alias addmd='git addmd'
alias b='git b'; __git_complete b _git_branch
alias br='git br'; __git_complete br _git_branch
alias c='git c'; __git_complete c _git_commit
alias ck='git ck'; __git_complete ck _git_checkout
alias d='git d'; __git_complete d _git_diff
alias dw='git dw'
alias ds='git ds'
alias dsw='git dsw'
alias h='git hard'
alias l='git l' # attention because the 'l' shortcut may already be in use (ex "alias l='ls -CF'")
alias pull='git pull'; __git_complete pull _git_pull # ! not effective 'pull' auto-completion - 'git pull' is more efficient
alias push='git push'; __git_complete push _git_push # ! not effective 'push' auto-completion - 'git push' is more efficient
alias pushf='git pushf'; __git_complete pushf _git_push # ! not effective 'push' auto-completion - 'git push' is more efficient
alias pushu='git pushu'; __git_complete pushu _git_push # ! not effective 'push' auto-completion - 'git push' is more efficient
alias r='git r'; __git_complete r _git_reset
alias recent='git recent'
alias reflog='git reflog'
alias s='git s'; __git_complete s _git_status
alias current='git current'
alias previous='git previous'
alias develop='git ck develop'
alias staging='git ck staging'
alias main='git ck main'
alias continue='git rebase --continue'
alias abort='git rebase --abort'
alias skip='git rebase --skip'
; The following configuration lines are generated from the `.bash_aliases` file.
[core]
excludesFile = ~/.gitignore_global
autocrlf = input
[tag]
sort = v:refname
[push]
default = current
followTags = 1
[rerere]
enabled = 1
[help]
autocorrect = 1
[rebase]
autosquash = 1
[pull]
rebase = 1
[color "status"]
untracked = white red
[diff]
tool = phpstorm
[difftool]
prompt = 0
[difftool "phpstorm"]
cmd = phpstorm diff \"$LOCAL\" \"$REMOTE\"
trustExitCode = 1
[merge]
tool = phpstorm
[mergetool "phpstorm"]
cmd = phpstorm merge \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"
trustExitCode = 1
[mergetool]
keepBackup = 0
[alias]
alias = !git config -l | grep 'alias\\.' | sort | cut -d '.' -f 2
conf = !git config --list | sort
comparefile = "!f() { git difftool $1 $2 -- $3; }; f"
resolve = mergetool
a = add
b = branch
ck = checkout
cp = cherry-pick
d = diff
dt = difftool
mt = mergetool
pl = pull
ps = push
r = reset
s = status -s
sub = submodule
last = log -1 HEAD
count = shortlog -sn
l = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' -12
ll = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s'
lmerges = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' --merges
ltags = !git log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' | grep 'tag:'
lfile = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' --follow
lfind = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' --grep
lauthor = log --graph --oneline --decorate --date=short --pretty=format:'%C(yellow)%h %C(cyan)[%ar] %C(green)<%<(9,trunc)%aN>%C(auto)%d %C(reset)%s' --author
find = grep --break --heading --line-number
br = !git fetch --all && git branch -r
current = rev-parse --abbrev-ref HEAD
recent = "!git branch --sort=-committerdate --format='%(HEAD) %(refname:short);%(committerdate:relative);%(authorname);%(subject)' | column -t -s ';'"
previous = checkout -
copy = checkout --track
n = checkout -b
new = "!f() { git checkout -b $@ && git push -u origin $1;}; f"
rename = "!f() { \\\n [ -z \"$2\" ] && o=$(git current) || o=$1 && n=${2:-$1} \\\n && git branch -m $@ && echo \"Rename branch $o to $n\" \\\n; }; f"
renameall = "!f() { \\\n [ -z \"$2\" ] && o=$(git current) || o=$1 && n=${2:-$1} \\\n && git branch -m $@ && git push origin -u $n && git push origin -d $o \\\n && echo \"Rename branch $o to $n (local & origin)\" \\\n; }; f"
delete = branch -d
deletef = branch -D
deleter = push --delete
deleteall = !git branch -d $@ && git push --delete origin
merged = "!f() { git branch --merged $1 | grep -v '\\*\\|main\\|dev\\|staging'; }; f"
no-merged = "!f() { git branch --no-merged $1 | grep -v '\\*\\|main\\|dev\\|staging'; }; f"
delete-merged = "!f() { \\\n echo -n \"Do you want to delete all merged branches [yn]? \" && exec </dev/tty && read r \\\n && [ \"$r\" != \"y\" ] && echo \"Cancelled!\" || (echo \"OK! Remove all\" \\\n && git merged $1 | xargs git branch -d) \\\n; }; f"
prunedr = fetch --prune --dry-run
prune = fetch --prune
compare = "!f() { git diff $1..$2; }; f"
grab = "!f() { git checkout $1 -- $2; }; f"
all = add --all
addn = !git add $(git ls-files --others --exclude-standard)
addm = !git add $(git diff --name-only --diff-filter=M)
addd = !git add $(git ls-files --deleted)
addnm = add . --ignore-removal
addmd = add --update
dw = diff -w --word-diff --color-words
ds = diff --staged
dsw = diff --staged -w --word-diff --color-words
discard = checkout --
untrackeddr = clean -f -d --dry-run
untracked = clean -f -d
hard = reset --hard
cleanup = stash push -u -m \"cleanup (safe)\"
cleanup-restore = stash pop
changelog = "!f() { n=${1:-20}; git shortlog HEAD~$n..; }; f"
c = commit -m
amend = commit --amend -m
noedit = commit --amend --no-edit
undo = "!f() { git reset --soft HEAD~$1; }; f"
psu = !git push -u
pushu = !git push -u
psf = !git push --force-with-lease
pushf = !git push --force-with-lease
sclone = clone --recurse-submodules
sdiff = !git diff && git submodule foreach 'git diff'
spush = push --recurse-submodules=on-demand
supdate = submodule update --remote --merge
strack = "!f() { git config -f .gitmodules submodule.$1.branch $2; }; f"
sconfig = config -f .gitmodules
tlast = describe --tags --abbrev=0
tdelete = tag -d
tdeleter = "!f() { git push origin :refs/tags/$1; }; f"
zip = "!f() { git archive $1 --format=zip --output=$1.zip; }; f"
visual = !gitk
contributors = !git log --format=%aN | sort -u

Git and Bash aliases defined and documented in a single .bash_aliases file, with Git auto-completion

These files are the result of my last study of the Git and Bash aliases, with the forced activation of Git auto-completion.

This is, of course, only one interpretation among many :P

All these commands and aliases are tested on Git version 2.17.1 on Linux Mint 19

Method 1: Use the .bash_aliases file directly

Logically, your ~/.bashrc file checks if the ~/.bash_aliases file exists. If it does, ~/.bashrc imports ~/.bash_aliases.

  • If ~/.bash_aliases exists: copy the contents of the .bash_aliases file from this Gist to the end of your ~/.bash_aliases file.

  • If ~/.bash_aliases doesn’t exist: you can create it by downloading the .bash_aliases file from this Gist.

With this method, you initialize the Git and Bash aliases all at once, and you benefit from forced Git auto-completion.

Caution
The .bash_aliases file is reloaded each time a terminal is opened and resets the global aliases and configuration, but does not delete old configurations. You’ll sometimes need to manually clean up this .gitconfig file to remove these old configurations.

Aliases: difference between .bash_rc, .bash_aliases and /usr/local/bin - https://askubuntu.com/questions/2528/aliases-difference-between-bash-rc-bash-aliases-and-usr-local-bin

Method 2: Use the generated .gitconfig file directly

You can copy/paste the configuration from these .gitconfig file into your own global ~/.gitconfig file. Aliases are available as soon as you save your ~/.gitconfig file.

However, with this method, you can’t benefit from forced Git auto-completion.

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