I was recently asked about the “prompt git status trick”, so I thought this might be a good opportunity to dump some git tips and tricks I’ve found useful.
First, the prompt git status trick. This uses a bash function defined in git-prompt.sh. It may be automatically sourced on your system. If not, you can source it yourself, or I’ve got a basic (i.e., not full-featured) alternative implementation you can just put in your ~/.bashrc. But the essence is that you just stick $(__git_ps1) in your definition of PS1 (the shell variable, not the telescope). I like to stick it in with red coloring after the working directory, which is what’s implemented below. It tells you the current branch, and the status of the work on that branch through a series of symbols:
-
<means you’re behind and cangit mergeadditional commits. -
>means you’re ahead and cangit pushyour commits. -
=means you have all the commits. -
<>means you’ve diverged, so you may want togit push --forceif you’re on your ticket branch orgit tossif you’re on master. -
$means you have work stashed (so you cangit stash pop). -
+means you have changes added but not yet committed. -
*means you have changes that have not yet been added or committed.
# The following two functions provide a basic alternative for git.git/contrib/completion/git-prompt.sh
# in case it's not available
function prompt_git_dirty {
local gitstat=`git status 2> /dev/null`
local charstat=""
[[ -z $(echo $gitstat | grep "nothing to commit") ]] && charstat="\%"
[[ -n $(echo $gitstat | grep "Your branch and '.*' have diverged") ]] && echo "${charstat}\<\>" && return
[[ -n $(echo $gitstat | grep 'Your branch is ahead of') ]] && echo "${charstat}\>" && return
[[ -n $(echo $gitstat | grep 'Your branch is behind') ]] && echo "${charstat}\<" && return
echo $charstat
}
function prompt_git_branch {
git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/\[\1$(prompt_git_dirty)\]/"
}
# Setup for git.git/contrib/completion/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export GIT_PS1_SHOWSTASHSTATE=1
export GIT_PS1_SHOWUNTRACKEDFILES=1
export GIT_PS1_SHOWUPSTREAM="auto"
type __git_ps1 1>/dev/null 2>&1 || alias __git_ps1=prompt_git_branch
if [[ ${EUID} == 0 ]] ; then
PS1='\[\033[01;31m\]\u@\h\[\033[01;34m\]:\W \#\[\033[00m\] '
else
PS1='\[\e[1;32m\]\u@\h\[\e[0;39m\]:\[\e[1;34m\]\w\[\e[1;31m\]$(__git_ps1)\[\e[0;1m\] \$ \[\e[0;39m\]'
fi
Next, a simple but helpful bash alias for updating lots of repos. Before I go offline, I like to run lsst-fetch, which gets all the work that’s on the remote servers. It doesn’t actually update any of the branches I’m working on (it’s a fetch, not a pull), so it’s the best of all worlds: I’ve got the information but it hasn’t corrupted my working state. And I immediately know the state when I cd into the repo because it’s right there on my shell prompt.
function git-recursive-fetch {
for g in `find "$1" -name .git`; do
echo Working on $g
git --git-dir $g fetch --all
git --git-dir $g fetch --all --tags
done
}
alias lsst-fetch="git-recursive-fetch $HOME/LSST"
Now, some git aliases from my ~/.gitconfig. I have more that I’ve stolen from various places, but these are the ones I actually use.
- The first three (
stat,co,cp) are basic aliases that save typing. -
git descdescribes the branch state in the same way that scons does when it chooses a version name on install, so this allows me to compare my working state with eups versions. -
git recentprovides a commit history, with one line per commit, and graphing the merges. This is what I typically use to trace what’s been going on in a repo. -
git histprovides a fuller commit history, with the full commit messages. If I’m trying to find particular changes, I often usegit hist -pto also include the actual changes that have been made. -
git fixupcreates a fixup to the most recent commit. You’ll need togit rebase -ilater to squash those commits, so more often I usegit amend -
git amendapplies work to the last commit without modifying the commit message. So if I’ve discovered something left out of the most recent commit, I fix it and dogit amend -a(to throw all changes into the commit) orgit addfollowed bygit amend(to be more selective). -
git inshows commits that will come in from the remote (if I did agit pull). -
git outshows commits that will go out to the remote (if I did agit push). -
git tossthrows away any new commits. If I have made and pushed changes on my laptop and now I need to update my state on the same repo on the cluster I will dogit fetch && git toss. -
git nukecleans up files that don’t belong and throws away any new commits. -
git newshows new commits created by the last command (e.g.,git pull). -
git aliaslists the aliases. To find a specific alias you have togrepthe output. -
git oldest-ancestorfinds the oldest ancestor of two commits or branches (based on this). This is useful for finding the base of a branch that we need to rebase against. -
git reviewdisplays commits (the message and patch) for careful review. You can specify the branches involved, or it will default to the current branch againstorigin/master. This is very helpful for checking your work before sending it off for review. -
git sublists the commit messages for work you want to submit for review. I like to run this and paste it into the Jira ticket so the reviewer knows exactly what I’m submitting. -
git sub-patchis the same, except the patch is included instead of just a stat of files changed. -
git followlets you follow the evolution of particular lines of code. The syntax isgit follow <file> <firstLine> [<lastLine>]. If the<lastLine>is not specified, just the single line will be followed.
[alias]
stat = status
co = checkout
cp = cherry-pick
desc = describe --tags --always
recent = "log --graph --date-order --pretty=format:'%C(yellow)%h%d%Creset %Cred%an%Creset %Cblue%cr%Creset %s'"
hist = "log --decorate --graph --date-order --stat"
fixup = commit --fixup=HEAD
amend = "!git log -n 1 --pretty=tformat:%s%n%n%b | git commit -F - --amend"
in = "!git remote update -p; git log ..@{u}"
out = "log @{u}.."
toss = "reset --hard @{u}"
nuke = "!git clean -df && git reset --hard @{u}"
new = !sh -c 'git log $1@{1}..$1@{0} "$@"'
alias = "config --get-regexp '^alias.*'"
oldest-ancestor = !bash -c 'diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1' -
review = !sh -c 'git log --patch --reverse "${2:-origin/master}".."${1:-HEAD}"' -
sub = !sh -c 'git --no-pager log --stat --reverse "${2:-origin/master}".."${1:-HEAD}"' -
sub-patch = !sh -c 'git --no-pager log --patch --reverse ${2:-origin/master}..${1:-HEAD}' -
follow = "!sh -c 'git log --topo-order -u -L $2,${3:-$2}:"$1"'" -
Some other things from my ~/.gitconfig that I find useful:
[color]
ui = true
[help]
autocorrect = 1
[push]
default = tracking
[rebase]
autosquash = true
autostash = true