Tools I use: Git

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 .DSSTORE 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

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

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. The serve command will export the current git repository via http for ad-hoc sharing. prune-all will find all the branches that you're tracking that are stale (aren't around anymore) and removes them.

[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
  chekcout = checkout
  pick = cherry-pick --sign-off
  sb = status --short --branch
  st = status --short --branch
  log1 = log --pretty=oneline --abbrev-commit
  serve = daemon --reuseaddr --base-path=. --export-all --verbose
  top-commits = shortlog --summary --numbered --no-merges
  gdiff = difftool --gui --no-prompt
  prune-all = !git remote | xargs -n 1 git remote prune
  prune-local-merged = !git branch --merged | grep -v "master" | 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.

[commit]
gpgsign = true

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

My github username which helps to power a few nice tools the github guys have put out. The best of them is hub, which allows you to do nice things like hub clone user/repo and forget about memorizing the path to the git repository.

[github]
	user = justinabrahms

When doing merge conflicts or diffs that are particularly hairy, I look to kdiff3. While not pretty, its a fairly functional merging and diffing utility. I'd like to eventually move to meld for diffing which supports loading all of your diffs at once, rather than viewing them one at a time in kdiff3, but I use a GUI so infrequently that it tends not to matter.

[mergetool]
	tool = kdiff3
[diff]
    guitool = kdiff3

We include an employer specific config if I'm in a directory that matches my workplace.

[includeIf "gitdir:**/subconsciousnetwork/"]
path = ~/.gitconfig.subconscious

This is a specific work config file so my email address is set to my internal email address.

[user]
email = justin@subconscious.network
signingkey = F1F44F323C6610D1

Git templates

For setting up hooks in all future repositories, we can use git init templates, which are very cool. I have one setup to prevent pushing to main by accident that I got from from Hammad Khalid.

[init]
templatedir=/home/abrahms/.git_template/
# Warn before pushing to protected branches
# Make script executable with chmod +x pre-push
# Bypass with git push --no-verify

BRANCH=`git rev-parse --abbrev-ref HEAD`
PROTECTED_BRANCHES="^(main|master|release-*)"

if [[ "$BRANCH" =~ $PROTECTED_BRANCHES ]]; then
  read -p "Are you sure you want to push to \"$BRANCH\" ? (y/n): " -n 1 -r < /dev/tty
  echo
  if [[ $REPLY =~ ^[Yy]$ ]]; then
    exit 0
  fi
  echo "Push aborted."
  exit 1
fi
exit 0

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

References

© 2012 - 2023 · Home — Theme Simpleness