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 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
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. 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
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:**/thrivenetwork/"] path = ~/.gitconfig.thrivenetwork
This is a specific work config file so my email address is set to my internal email address.
# e.g. lives in ~/.gitconfig.subconscious [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