Git: Config

Git is very configurable. If you're ever bored, you could spend some time reading git-config(1). You never know what you might find.

I'm an opinionated person. So I've tried to break the Git configuration I use into varying levels of opinion.

Must-haves

<rant>

There's no excuse for not setting your user.name and user.email, especially in a professional environment. Git defaults to using your computer username, which probably isn't appropriate to use.

I don't want to see 4-5 different spellings of your name, your username, and your email in the Git log. I don't want to see a random ID number in the Git log that I can't map to a person.

</rant>
[user]
    name = First Last
    email = email@example.com

My workplace prefers a linear history. That means we rebase and merge without a merge commit. This results in a much cleaner commit history that's easier to interrogate. My personal opinion, is that feature branches should be rebased, but then merged with an empty merge commit. They should not be squashed, as that makes using git-log and git-blame to interrogate the commit history impossible to glean useful information from.

In both cases, pull.rebase=true should be set. This means any local commits will be rebased on top of the remote branch instead of creating a merge commit. This one change would eliminate much of the frustration coworkers have experienced with "tricky" rebases.

[pull]
    rebase = true

In my preferred workflow, there's a branch per feature, and then those branches get deleted after they're merged (so you don't end up with thousands of remote branches!) So you should set fetch.prune=true

[fetch]
    prune = true

TODO: I have not learned a nice way to automatically delete local branches when the remote tracking branch has been deleted. From https://stackoverflow.com/questions/13064613/how-to-prune-local-tracking-branches-that-do-not-exist-on-remote-anymore it looks like

git switch master
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d

Note you must checkout the master branch before running, or else use git branch -D.

Should-haves

I was really tempted to put this in the section above. Complaints about how much work it is to write good commit messages that meet the stylistic concerns of the esteemed How to Write a Git Commit Message blog post by Chris Beams. Vim does syntax highlighting of the subject line, blank line, and can reflow paragraphs to meet the 72 character line length limit.

[core]
    editor = vim

The importance of using Linux newlines depends on the team and technologies being developed. For what I work on, CRLF has no place.

[core]
    eol = lf

Seeing the diff you're committing right in your commit editor (and syntax highlighted if you have a sufficient core.editor) is invaluable for crafting informative commit messages, and providing a guard against committing changes you never intended.

[commit]
    verbose = true

It's also helpful to have a global .gitignore to avoid committing common temp files.

[core]
    excludesFile = ~/.gitignore

in which I have

# Vim swap files
*.sw?
# GDB history logs
.gdb_history

Nice-to-haves

I like using delta as my Git pager. It does better word-diffing and highlighting than the contrib diff-highlight, and it additionally provides syntax highlighting and line numbers. The package in the Ubuntu repository was a bit out of date, so I installed delta from the latest GitHub release with

USERNAME="dandavison"
REPO="delta"
curl --silent "https://api.github.com/repos/$USERNAME/$REPO/releases/latest" | # Get latest release from GitHub api
    grep --only-matching --perl-regexp '"tag_name": "\K(.*)(?=")' |            # Get the latest tag
    xargs -I {} curl --location --output /tmp/delta.deb --remote-name "https://github.com/$USERNAME/$REPO/releases/download/{}/git-delta_{}_amd64.deb"
sudo apt install /tmp/delta.deb

Then add the following config

[core]
    pager = delta
[interactive]
    diffFilter = delta --color-only
[delta]
    line-numbers = true
    syntax-theme = base16
    # side-by-side diffs are nice, but are too wide to be helpful in fzf --preview windows
    # TODO: Perhaps git-gl should be rewritten to pass git -c delta.side-by-side=false for the preview
    # but not to the pager view?
    # TODO: There's also something weird about the terminal width when switching from the preview
    # to the pager.
    # side-by-side = true

I have a number of aliases that are so-ingrained in my muscle memory that I almost can't use Git on a colleague's computer.

[alias]
    lg    = log --color --graph --pretty=format:'%C(auto)%h%d %s %C(black)%C(bold)%an, %ar' --abbrev-commit --decorate
    ll    = log --pretty=format:'%C(red)%h%C(reset) -%C(yellow)%d%C(reset) %s %C(bold blue) <%an>' --decorate --numstat --abbrev-commit
    rf    = reflog --pretty=format:'%C(auto)%h %gd %gs%C(black)%C(bold), %cr'
    co    = checkout
    sw    = switch
    amend = commit --amend --no-edit
    st    = status
    b     = branch -vv
    rb    = rebase
    chp   = cherry-pick

Lastly, I can't live without git-gl, which is an interactive commit browser powered by FZF. Briefly, it's a pager for git-log that supports searching commit messages, previewing the diffs, performing interactive rebases, copying hashes to the system clipboard, and cherry-picking selected commits.