Git is a DVCS or a distributed version control system. Its very similar to SVN in essence. As many others have said better, keeping your software version control is important for collaboration, maintaining a sense of history over time, and protection from data loss. Git in particular has a novel approach to this in that it doesn’t rely on a central repository from which all syncs happen.
Why I like it#
Minimal setup on the server#
Git requires minimal setup achieve maximum benefit. When trying to setup a working git server, there’s no complicated daemons to run, copious amounts of server configs or anything of the sort. Due to the fact that every git clone is a full mirror of the git repository, you just initialize a git repository (a bare version, as you probably don’t want/need a working copy) and you’re done. If you want to access this new repository remotely, you can do so via SSH. If you’re looking to collaborate with others, you can communicate via built-in support for mailing patches back and forth, or you can run your own git server which I did before a mass simplification of my hosting setup.
Community#
The community that has sprung up around git is an interesting one. While it started as a neckbeard-y, linux kernel developer group, it has gained in popularity with the ruby community which has given it an interesting focus towards usability and nifty tools. Probably the best artifact of the ruby influence is github. This has made git a pretty clear winner in the DVCS realm, though git’s lack of historic windows support has given an edge to Mercurial for that crowd.
Very reasonable command layout and extension points#
While it seems to be a source of frustration for newcomers, I’ve now
become accustomed to the command structure and conventions for git.
One of the best points of this is running git foo is the same as
having a git-foo executable somewhere on your path. This is
dead simple from an extension point.
Founded on interesting concepts#
The internals of git intrigue me. I’m told that its modeled after file systems, which seems plausible given the original authors. The concept of traversing graphs to a tree of objects which can construct the state of a repository is of geeky interest to me. I’ve long been interested in implementing something on top of git as a datastore, but haven’t yet gotten around to it.
How I configure it#
Git’s config files are structured like INI files. In addition to the
configuration in this file, I tend to alias g to git which allows
for some nice savings for typing on commands when combined with some
of the aliases I have below.
Setup a global git ignore file which contains various things I never want in my repositories such as the OS X .DS_STORE files, compilation artifacts, etc. Also, highlight a large class of errors related to whitespace so hopefully I don’t check in code with trailing whitespace and such (though Emacs makes this more or less an impossibility).
[core]
excludesfile = ~/.gitignore
whitespace=fix,-indent-with-non-tab,trailing-space,cr-at-eol
pager = delta
General information about me for recording commits.
[user]
name = Justin Abrahms
email = justin@abrah.ms
signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGIolzScZ+EVWFp7s+HoLoZc6HBQ9tQHG46WBZk4Iokn
Setup a bunch of nice coloring for the various colored outputs that exist. This particular theme allocates sane colors for things, such as marking bad things red (like merge conflicts, trailing white space) and more benign things in less overloaded colors like magenta or yellow.
[color]
ui = auto
diff = auto
status = auto
branch = auto
interactive = auto
pager = true
[color "branch"]
current = yellow reverse
local = yellow
remote = green
[color "diff"]
meta = yellow bold
frag = magenta bold
old = red bold
new = green bold
whitespace = red reverse
[color "status"]
added = yellow
changed = green
untracked = cyan
Git can autocorrect misspelled commands with a confirmation prompt, reducing the need for typo aliases.
[help]
autocorrect = prompt
This is another handy extension point for git. When building a simple
alias from one command to another, you don’t have to create a git-foo
script, but can instead include those below. The most commonly used
aliases I have below are a, ci, and st, which make sense given
their purpose in general git workflow. The lg command will show one
line commit messages, but lays them out in a nice ascii graph of
commits.
[alias]
a = add
b = branch
cp = cherry-pick
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative
ci = commit --verbose
pick = cherry-pick --sign-off
sb = status --short --branch
st = status --short --branch
log1 = log --pretty=oneline --abbrev-commit
top-commits = shortlog --summary --numbered --no-merges
prune-all = !git remote | xargs -n 1 git remote prune
prune-local-merged = !git branch --merged | grep -v -E "master|main" | xargs git branch -d
When pushing, push the current branch by default.
[push]
default = current
When making a new repo, make the default branch main.
[init]
defaultBranch = main
When committing, gpg sign the commit. Use 1password to do it.
[commit]
gpgsign = true
[gpg]
format = ssh
[gpg "ssh"]
program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"
Enable rerere, which is a nifty tool which will remember the merge chunks that fail and if they happen again, it’ll auto resolve them for you.
[rerere]
enabled = true
Automatically prune remote-tracking branches that no longer exist on the remote during fetch. Rebase by default when pulling to keep history linear.
[fetch]
prune = true
[pull]
rebase = true
Use zdiff3 conflict style which shows the common ancestor in merge conflicts, making them easier to resolve. Use the histogram diff algorithm for better diffs.
[merge]
conflictstyle = zdiff3
[diff]
algorithm = histogram
Automatically squash fixup commits during interactive rebase. Validate object integrity on transfer.
[rebase]
autoSquash = true
[transfer]
fsckObjects = true
My github username, used by tools like the GitHub CLI (gh).
[github]
user = justinabrahms
Git has support for replacements such that you can type one thing and something different executes. This can make underlying commands much simpler.
[url "git@github.com:"]
insteadOf = https://github.com/
My .gitignore#
.venv is a file I use to contain the name of my Python
virtualenv. If that file is detected, it sources that virtualenv.
*~
node_modules/
.venv
TAGS
tags
Delta#
Delta is a pager which makes for really nice diff output. I currently manually install it (gross, I know).
[interactive]
diffFilter = delta --color-only
[delta]
features = side-by-side line-numbers decorations
whitespace-error-style = 22 reverse
[delta "decorations"]
commit-decoration-style = bold yellow box ul
file-style = bold yellow ul
file-decoration-style = none
Other nifty scripts#
I tend to leave random comments scattered across my source code in the format of “@@@ Null Check?” or “FIXME: fill in documentation” which is intended to be filled in prior to getting a code review. This script piggybacks on git grep to find them.
#!/bin/bash
git grep -C2 -E "(@@@|FIXME)"
I also use git-wtf, which is a bit large to paste here. It will show you how your current branch relates to various other branches, including remote branches. I also keep a copy of the author’s git-rank-contributor script which, as it’s name suggests, will stack rank contributors to a repository by commit.
This command, which I keep around as git-status-home will look for any git repositories under my home directory, and run a git status in their directory. This is helpful to find any repositories who have uncommited changes laying around the working copy. It will also give a general guideline of if there are changes in my repository that aren’t referenced in the default tracked origin repository, meaning I should push.
#!/bin/bash
# TODO:
# - Figure out how to make -prune work so I don't have to use grep -v
# - add the sed command to -exec param
# - do better filtering of git status. Might require moving this out
# of bash and using something like dulwich
if [ -n $1 ]; then
DIR=$1
else
DIR=$HOME
fi
cd $DIR
echo "Searching for git repositories.."
for dir in `find . -name ".git" -type d 2>/dev/null | grep -v "env" | grep -v ".virtualenv" | sed 's/\/.git//g'`
do
echo "=== $dir ==="
pushd $dir
git status -sb
popd
done