Git Pro Tips
Here are some of useful git configs and aliases.
Most of these settings are in ~/.gitconfig
, the global git configuration
file.
Note that this duplicates configuration names like [alias]
in different
sections. Your ~/.gitconfig
file should only include one [alias]
line, and
all aliases should appear under.
Default Branch
Recent versions of git use main
as the branch name when setting up a new
repo. If you prefer to stay with master
, you can configure it:
[init]
defaultBranch = master
Colors
If you like color everywhere:
[color]
ui = auto
You can also customize the colors used for various actions (see man git-config
for more info):
; Colors for `git status`
[color "status"]
header = yellow bold
added = green bold
updated = green reverse
changed = magenta bold
untracked = red
; Colors for `git diff`
[color "diff"]
meta = yellow bold
frag = magenta bold
old = red bold
new = green bold
whitespace = red reverse
Default --pretty
format
For things like git log
, git show
, etc, you can keep things simple and
concise by setting the following:
[format]
pretty = format:%C(red)%h%Creset%C(yellow)%d%Creset %C(magenta)%an%Creset: %s %C(green)(%ar)%Creset
For git log
, git shows commits on a single line with only the most useful
information present.
You can still use git show --format=fuller [sha]
to see the full details
of a commit when more context is needed (see the git showf
alias below).
Pulling Branches
Ever run git fetch
and see dozens of remote branches pulled in? If you run
git branch --remotes
you’ll likely see references to old branches that have
already been merged in to master. You can tell git to clean these
automatically, any time you run git fetch
or git pull
, with the following
setting:
[fetch]
prune = true
Like to handle things manually? You can create a git prune-branches
alias:
[alias]
prune-branches = !git branch --merged | grep -v '^master$' | grep -v "\\\\*" | xargs -n 1 git branch -d
If you want to always git pull --rebase
, you can configure it with:
[pull]
rebase = true
Pushing Branches
In older versions of git, git push
without any branch specified would push
all branches to origin. Since this is hardly ever what you’d want, it is now
disabled by default. But if you want to keep safe in case you come cross any
old git versions, you can add the following:
[push]
default = current
This makes git push
operate on the currently checked out branch only.
Viewing Branches
You may have a few different branches being worked on at once and it can be
useful to see them by the last commit date. The following enables this
behavior by default when running git branch
commands to list branches:
[branch]
sort = -authordate
You can also use the following git recent-branches
alias to show the 10 most
recent branches, sorted by date:
[alias]
recent-branches = for-each-ref --count=10 --sort=-committerdate --format=\"%(refname:short)\" refs/heads/
Need the current branch name? Try a git branch-name
alias (also try git branch-name | pbcopy
on MacOS to copy it to your clipboard):
[alias]
branch-name = rev-parse --abbrev-ref HEAD
Disable advice
Git is nice enough to provide advice on certain commands. For example, git status
will show advice on how to git add
files. You may find some of these
annoying so they can be disabled with the following:
[advice]
statusHints = false
pushNonFastForward = false
See the advice.*
section under man git-config
for more places advice is
printed.
Nicer git status
For a more concise status output, git status --short --branch
(or git status -sb
) can be used. It can be setup by default using:
[status]
short = true
branch = true
Want a verbose status once in a while? Try git status --no-short
.
Viewing commits
If you set a git log
format to be simple as outlined above, you might want a
quicker way to use git show --format=fuller
to view full details on a single
commit:
[alias]
showf = show --format=fuller
Note: git showf
can optionally have a branch/commit specified. Without
arguments it will use HEAD
(i.e. that current branch/latest commit). You
could also specify git showf deadbeef
to see a specific commit.
For a graph view of different branches and their merge points you can setup
git lg
:
[alias]
lg = log --graph --abbrev-commit --date=relative
For when you want full log details there’s git full-log
:
[alias]
full-log = log --format=fuller
If you need to see the last commit SHA, perhaps for pasting into GitHub or a
Jira ticket, you can setup git last-sha
:
[alias]
last-sha = rev-parse --short HEAD
You can see the commits you’ve made to a local branch but haven’t yet pushed
upstream with git pending
:
Note: this requires you pushed with git push -u
to track your remote.
[alias]
pending = !git --no-pager log @{u}.. && echo
You can see the last committer with git last-committer
:
[alias]
last-committer = !git --no-pager log --pretty="%an" --no-merges -1
Or you can see the last commit subject with git last-subject
(great for
copy/pasting into pull requests/tickets):
[alias]
last-subject = !git --no-pager log --pretty="%s" -1
Sometimes you want to see just the filenames that were changed in a commit, and
git show-files
does this. It can also work on a branch, like git show-files master..HEAD
to show everything changed on the current branch vs master. This
is great when you want to run tests or linters on those files (eg
make-me-pretty $(git ls-files master..HEAD)
):
[alias]
show-files = !git --no-pager show --name-only --format=
On MacOS or a system with Ruby’s webrick gem installed, git web
will spawn a
web interface for browsing (close with git web --close
):
[alias]
web = instaweb --httpd=webrick -b open
Undoing things
For when you’ve run git add
on files and you want to back out, git unstage
does the job (git unstage -- .
to unstage everything, or git unstage -- file1
to specify files):
[alias]
unstage = reset HEAD -- ...
If you’ve already committed, you can setup git undo
to undo the commit
(which can then be unstaged with git unstage
):
[alias]
undo = reset --soft HEAD^
If you’ve made a typo and haven’t pushed yet, git amend
can be setup to
amend the last commit. You might git amend -a
or git add changed-file; git amend
. You might want to reset the date as well so that the commit shows
up as expected in views sorting by date:
[alias]
amend = !git commit --amend --date=\"$(date)\" -C HEAD
Per-System overrides
If you keep your personal git life and work git life separate, you can keep
default settings in ~/.gitconfig
and overrides in a separate file.
For example you might default to using your personal email in ~/.gitconfig
:
[user]
email = josh@josh.fail
In ~/.gitconfig
you can specify a local include, with system specific
configuration file:
[include]
path = ~/.gitconfig.local
Then in that ~/.gitconfig.local
file, specific customizations are made:
[user]
email = work@josh.fail
Customizing Your GIT_EDITOR
For git commands like git commit
or git tag
where you might want to use a
text editor to draft the associated message, you can set the $GIT_EDITOR
environment various or the core.editor
git config.
To use the environment variable, add to ~/.zprofile
for ZSH or
~/.bash_profile
for Bash:
export GIT_EDITOR=vim
Or to set via ~/.gitconfig
:
[core]
editor = vim
Want to use VS Code instead?
export GIT_EDITOR="code --wait"
Or in ~/.gitconfig
:
[core]
editor = code --wait
Directory-based Git Author
A directory-based alternative to override your git author is to use direnv to configure author/committer git environment variables while under a certain directory.
If you just need to change your email, you could setup a .envrc
file in the
root directory where you keep your A2 projects cloned — for example
~/work/a2/.envrc
.
export GIT_AUTHOR_EMAIL=work@josh.fail
export GIT_COMMITTER_EMAIL=work@josh.fail
If you want to change your name too for some reason:
export GIT_AUTHOR_NAME="Josh Danger Priddle"
export GIT_COMMITTER_EMAIL="Josh Danger Priddle"
With that in place, any time you cd ~/work/a2
(or any subdirectory), git commit
will automatically use the values you’ve set.
”Local” gitignore
If you need to ignore files or directories in your own copy of a cloned repo,
you can add ignore rules to .git/info/exclude
. For example, if you kept some
personal scripts in _my_scripts/
you could add _my_scripts/*
to
.git/info/exclude
to tell git to ignore it without it impacting other
developers.
Note: Use this feature sparingly and with caution. If multiple developers
need to ignore the same files, you should use a regular .gitignore
file.
Utilities
When you first setup a new repo you need to make a first commit. You can
setup git msg
to allow committing with no content and just a message:
[alias]
msg = commit --allow-empty -m
Use git msg "First commit"
then checkout a new branch with your code.
If you need a way to determine the root directory of a project, you can setup
git root
:
[alias]
root = rev-parse --show-toplevel
Want to scan for TODO/FIXME comments? You can setup git todo
:
[alias]
todo = grep -wniI -e TODO -e FIXME
Need the last commit sha from the previous pull?
[alias]
last-pull-sha = !"git reflog | awk '/: pull / { if (l == 1) { print $1; exit }; l+=1 }'"
Rollback with git checkout "$(git last-pull-sha)"
.
Bonus: Bash/ZSH aliases
These shell aliases cover very common git operations. They can be added to
~/.bashrc
for Bash users or ~/.zshrc
for ZSH users.
The obvious ones:
alias gb='git branch'
alias gbd='git branch -d'
alias gco='git checkout'
alias gd='git diff'
alias gdc='git diff --cached'
alias gs='git status'
For when you’ve deleted a lot of files from a repo you can use grm
to remove
them from git as well:
alias grm="git status --porcelain | awk '\$1 == \"D\" {print \$2}' | xargs git --git-dir=\$(git rev-parse --git-dir) rm --ignore-unmatch"
Finally, gcomp
and gcomp-
(mnemonic: git checkout master
pull). gcomp
checks out master and pulls, gcomp-
checks out master and
pulls, then checks out the previous branch. Any time you’re about to push up
a new pull request you can run gcomp- && git rebase master
to make sure
you’re up to date.
alias gcomp='git checkout master; git pull'
alias gcomp-='git checkout master; git pull; git checkout -'