My favorite Git tricks to make life easier
Git sometimes is a confusing beast, but it can be awesome if you train it well
January 16, 2018
productivity
hacks
Pretty much everything I do is code. I build cloud platforms; everything is code. I build tools; more code. This blog; again, code. All this code lives in Git. Either on Github, on Bitbucket, or on private-hosted company VCS servers. And Git is nice. But from time to time, Git is also extremely confusing, or requires terrifyingly long commands.
So here are some of my favorite Git tricks that make my life easier. Maybe they’ll work for you, too.
Git on MacOS
Apple has a tendency to ship somewhat out of date versions of tools with MacOS. Their version of Git is no exception, although it’s not as bad as their Bash version. Nonetheless, if you want a more recent version, Homebrew is your friend:
$ brew install git
Git and Github
If most of your ‘Git business’ resides on Github, Hub may be your friend. It is a tools that wraps Git and adds a ton of Github specific extra goodies, like creating (and listing) pull-requests in your terminal, or quickly opening the relevant repository in your browser with a single command.
$ brew install hub
To really wrap git, add the following to your Bash config:
# If Github's 'hub' is installed, alias 'git' to it
if which hub &> /dev/null ; then
alias git=hub
fi
To check if it works:
$ git version
git version 2.15.1
hub version 2.2.9
For more info on what Hub can do for you and how to set it up, see the Hub webpage
Run git commands on multiple repositories
Chances are your work is spread across multiple repositories, in which case you might want to run some git command across all of them. Maybe you have a daily standup and you want to quickly check what you did yesterday. Your team works on 10 repositories? That would mean running this 10 times:
$ cd some_repo
$ git log --author="<your name here>" --since='yesterday 12am' --until='12am' --all --no-merges
$ cd ..
As an alternative you can setup a shell alias called cgit
:
cgit() {
c_repos=$(find . -maxdepth 2 -name .git -type d -print | cut -d'/' -f2)
c_basedir=${PWD}
for repo in ${c_repos}; do
echo "## [${repo}] "
git --git-dir="${c_basedir}/${repo}/.git" \
--work-tree="${c_basedir}/${repo}" \
"$@"
echo -e ""
done
}
Then, considering your repos are in ~/git/work
, you do this:
$ cd ~/git/work
$ cgit log --author="<your name here>" --since='yesterday 12am' --until='12am' --all --no-merges
Short everything
Long commands are bad. So let’s have some short aliases for things in our .gitconfig
, right?
[alias]
# short aliases
st = status
co = checkout
ci = commit
br = branch
df = diff
lg = log -p
rso = remote show origin
me = config user.name
smash = !git ir
# interactive rebase
ir = !sh -c 'git rebase -i origin/${1-master}' -
The magic pull
When I start working or something (or simply every morning) I make sure my local working copies are up to date with the remote. But what about local commits? What about uncommited work? What about submodules? There are many reasons why a simple git pull
might fail.
I use: git up
, which is configured in my .gitconfig
:
[alias]
up = !git pull --rebase --prune --autostash --recurse-submodules
Combined with cgit
I can simply enter my ~/git/work
dir and run cgit up
and I’ll be up to date. Profit!
Prettier logs
The basic git log
command works. It’s not perfect, but it works. But it also takes a ton of space, and I thought I could do a better job. Prettier logs, but also some aliases to quickly see what I did yesterday. Or what everyone did yesterday.
[alias]
llog = !git log --pretty=format:'%Cgreen(%ci)%Creset %Cred%h%Creset %<(20,trunc)%C(blue)%an%Creset %s %C(yellow)%d%Creset ' --abbrev-commit
# all commits that aren't merges on all branches
all = !git log --pretty=oneline --abbrev-commit --all --no-merges
# all commits today for only me
today = !git llog --since='12am' --all --no-merges --committer=\"`git me`\"
# and yesterday
yesterday = !git llog --since='yesterday 12am' --until='12am' --all --no-merges --committer=\"`git me`\"
# and the same, but for everyone
today-all = !git llog --since='12am' --all --no-merges
yesterday-all = !git llog --since='yesterday 12am' --until='12am' --all --no-merges
Magic pull with relevant logs
I like staying on top of things, so when I pull in the latest changes, I don’t just want to be up to date, I would also like to see what changed since I last pulled. Meet git up-log
:
[alias]
up-log = !sh -c 'logstart=$(git rev-parse HEAD) && git pull --rebase --recurse-submodules --prune --autostash && echo "" && git --no-pager llog ${logstart}.. --stat'
Finish up after a pull-request has been merged
Nobody wants to celebrate their work being merged into master by manually checking out master, updating it, removing branches, etcetera. Just a single command, and get on with the fun stuff already! Just a simple git prdone
and there you go:
[alias]
# clean up after PR has been merged
prdone = !sh -c 'git co ${1-master} && git up && git clean-merged'
# `git clean-merged`
# clean up merged branches
clean-merged = "!git branch --merged | grep -v '\\^\\*\\|master' | xargs -n 1 git branch -d"
Putting it together
With all these aliases, working with git looks like this:
## Check in in the morning
$ cd ~/git/work
$ cgit up-log
...
## Pre standup
$ cd ~/git/work
$ git yesterday
## Start working on something
$ cd ~/git/work/foobar
$ git co -b feature
$ git ci -a -m 'did something'
$ git push origin feature
## Create pull request (using hub)
$ git pr
...
## Finishing up
$ git prdone
$ cd ..
$ cgit up-log
That’s it! You can also read about how I use multiple Git identities and how I deal with situation-specific shell configuration.