Git setup with multiple profiles and hooks per profile

Separate Git profiles based on working directory

I organize my home folder something like this:

/home/varl
├── dev
│   ├── work
│   └── personal
└── .gitconfig

In the main Git configuration file ~/.gitconfig I set my defaults:

# ~/.gitconfig
[pull]
    rebase = true

[branch]
    autosetuprebase = always

[commit]
    gpgSign = true

[core]
    editor = vim

[user]
    name = Viktor Varland
    email = varl@personal.com
    signingkey = PERSONAL_KEY_TO_SIGN_WITH

These settings are used everywhere, which is the normal way to do things. However I would like to change user.email and user.signingkey when I am working on different projects (e.g. work account and key for work, and personal account and key for, well, personal projects).

For each context under ~/dev I add another .gitconfig file:

/home/varl
├── dev
│   ├── work
│   │   └── .gitconfig
│   └── personal
│       └── .gitconfig
└── .gitconfig

Back in the main ~/.gitconfig, add the includeIF directive to conditionally include[1] configuration for different contexts:

# ~/.gitconfig

# ... trunc ...

[includeIF "gitdir:~/dev/work/"]
    path = ~/dev/work/.gitconfig

[includeIF "gitdir:~/dev/personal/"]
    path = ~/dev/personal/.gitconfig

This makes Git load additional configuration if I am using Git within e.g. ~/dev/work.

In ~/dev/work/.gitconfig I can override or set new options valid for the repos within ~/dev/work:

# ~/dev/work/.gitconfig

[user]
    name = Viktor Varland
    email = viktor.varland@work.com
    signingKey = WORK_KEY_TO_SIGN_WITH

Note that I can use a different git user when working in repos below ~/dev/work than I use for my personal stuff.

Multi-project Git hooks

Now, create the folder ~/dev/work/.hooks. We are going to use this to define hooks that apply for all the projects under ~/dev/work.

/home/varl
├── dev
│   ├── work
│   │   ├── .hooks
│   │   └── .gitconfig
│   └── personal
│       └── .gitconfig
└── .gitconfig
# ~/dev/work/.gitconfig

[core]
    hooksPath = ~/dev/work/.hooks

# ... trunc ...

This makes it so that we can use global git hooks for all repos that belong to the same context, in this example, work.

For example, the ~/dev/work/.hooks/pre-commit hook looks like this:

#!/bin/bash

if [[ -f "$PWD/package.json" ]]; then
  npm run --if-present lint
fi

So each time we try to commit in a repo that has a main package.json file, it attempts to run the lint script.

We could add similar conditional statements for Rust, Go, Java, as well, and have a hook that does (mostly) the right thing based on the project.

The pre-commit script can be customized to run validation tools per repo, or made general like above.

Hooks can also be skipped anytime with the --no-verify flag[2].

On pre-push we can do something similar, but for tests.

[1] https://git-scm.com/docs/git-config#_includes [2] https://git-scm.com/book/fa/v2/Customizing-Git-Git-Hooks