git configuration

git config

This file generates my git global config.

User config

Only define my name, and not my email. And instruct git to avoid trying to guess a default for the email. This will enforce me to set the email for each git repo.

        useConfigOnly = true
        name = Toon Claes

Core config


Global ignore file

Use a global ignore file.

I’m using the default ~/.config/git/ignore so no need to set this explicitly.

excludesfile = ~/.config/git/ignore

Commit comments

I tend to use markdown in my commit messages, and markdown uses the # symbol to define headings. So instead of the default symbol # used to define comments in commit messages, use ;.

commentchar = ";"


Don’t do output conversion on end-of-line.

autocrlf = input




Auto-correct mistyped commands after 0.1 seconds.

autoCorrect = 1



Default branch

When creating a new repo, set the default branch to main.

defaultBranch = main




Specify a git commit template that will help me to write good commit messages.

Here we start with an empty block to force org-tangle to start the file with an empty line. The padline header argument, defaulting to yes, causes empty lines between the above and the below codeblock.

; Why is this change needed?
;Prior to this change,

; How does it address the issue?
;Let's ...

; Provide links to any relevant tickets, articles or other resources

;Label: bug::performance
;Label: bug::availability
;Label: bug::vulnerability
;Label: bug::mobile
;Label: bug::functional
;Label: bug::ux
;Label: bug::transient

;Label: feature::addition
;Label: feature::enhancement
;Label: feature::consolidation

;Label: maintenance::refactor
;Label: maintenance::removal
;Label: maintenance::dependency
;Label: maintenance::scalability
;Label: maintenance::usability
;Label: maintenance::test-gap
;Label: maintenance::pipelines
;Label: maintenance::workflow
;Label: maintenance::performance
;Label: maintenance::release

;Changelog: added
;Changelog: fixed
;Changelog: changed
;Changelog: deprecated
;Changelog: removed
;Changelog: security
;Changelog: performance
;Changelog: other

;EE: true

And configure git to use this template.

template = ~/.config/git/commit_msg.txt



Untracked files

Show also individual files in untracked directories.

showUntrackedFiles = all

Submodule summary

Enable submodule summary showing the summary of commits for modified submodules.

submoduleSummary = true



Moved code

Show moved lines in a different color.

colorMoved = zebra




Prune the local tracking branches and tags when fetching from remote.

prune = true
pruneTags = true



Fast-forward only

When pulling only update the current branch by fast-forwarding.

ff = only




Enable the use of Bundle-URI when the server advertises it.

bundleURI = true


Define a set of aliases.



This alias scrubs away all local branches that are merged.

scrub = !git branch --merged | grep --extended-regexp --invert-match '(^\\*|master|main)' | xargs --no-run-if-empty git branch --delete


I don’t like git stashes. A git stash is where code goes to die. So instead of stashes, create a WIP commit on the current branch. This is much better to keep context of the work-in-progress code, allows me to push the code to a remote and have backup, and maybe also give me some insights on the test results of CI.

I’m also providing an alias that undoes the last commit if it is a WIP commit.

Improved unwip alias by ZJ.

wip = !git add -A && git commit -mWIP
unwip = !git log -1 --format=format:"%H" --invert-grep --grep '^WIP' | xargs git reset --soft

Rebase on remote master

I commonly rebase my working branch on master on the remote. That remote can be origin or upstream. So this alias allows me to fetch master from the <remote> and rebase current branch on <remote>/master.

It expects one argument:

  • the name of the remote
remaster = !sh -c 'git fetch $1 master && git rebase $1/master' -

Resync with remote

When the remote branch was rebased and you want to resynchronize your local branch to the remote state, use this reremote alias.

It expects one argument:

  • the name of the remote
reremote = !sh -c 'git rev-parse --abbrev-ref HEAD | xargs git fetch $1 && git reset --hard FETCH_HEAD' -

Fetch and checkout

Fetch the branch from the remote, create a tracking branch for it and check it out now šŸŽ¶the funk soul brotheršŸŽ¶.

It expects two arguments:

  • the name of the remote
  • the name of the branch
cofetch = !sh -c 'git fetch $1 $2:remotes/$1/$2 && git switch -c $2 remotes/$1/$2' -

Politely force push current branch

When force pushing it’s safer to use Git’s --force-with-lease as this ensures the latest changes were fetched before overwriting changes on the remote.

plush = !sh -c 'git push --force-with-lease $1 HEAD' -


git-rerere stands for: Reuse recorded resolution of conflicted merges. It is an awesome feature that helps you to resolve the same conflicts over and over again.

        enabled = true

Markdown diffing

Below is an attempt to improve diffing markdown files. But I never got it working and I just left it here.

[diff "markdown"]
        tool = lowdown

[difftool "lowdown"]
        cmd = "lowdown-diff -s -Tman $LOCAL $REMOTE | groff -Tascii -man | less"

Emacs diff tool

Use emacs(client) as a difftool.

[difftool "ediffclient"]
        cmd = emacsclient --eval \"(ediff-files \\\"$LOCAL\\\" \\\"$REMOTE\\\")\"


These settings involving LFS are generated by git itself.

[filter "lfs"]
        smudge = git-lfs smudge -- %f
        process = git-lfs filter-process
        required = true
        clean = git-lfs clean -- %f


Enable git protocol version 2.

Read about it.

        version = 2


Configure git to send mails directly from git.

Instructions came from

I have msmtp configured to send mails through Emacs, so I can just use that tool here too.

    smtpserver = /usr/bin/msmtp

Global Gitignore

Ruby vendoring

Ignore gems installed in the local vendor directory.


GNU Global

For a while I used GNU Global for tagging code, so ignore the TAGS files from being committed.



For C code I’m using CCLS with LSP in emacs. Ignore the files this creates.



Dir locals

Most project don’t like Emacs .dir-locals.el.


Auto-save files

Emacs creates auto-save files named by appending # to the front and rear of the visited file name.


GitLab & GitHub

Do not ignore .gitlab and .gitlab-ci.yml, and be explicit about it. This will also make rg(1) search these. And do the same for GitHub’s files.


Compile flags

For LSP in Emacs you might need to specify extra compiler flags, but not all projects have it, so ignore files setting them.



Byebug creates a history file in the current working directory, and that’s not something you want to check in.


Git attributes

Improve diff output for various file types.


*.c     diff=cpp
*.h     diff=cpp
*.c++   diff=cpp
*.h++   diff=cpp
*.cpp   diff=cpp
*.hpp   diff=cpp
*.cc    diff=cpp
*.hh    diff=cpp
*.cs    diff=csharp
*.css   diff=css
*.html  diff=html
*.xhtml diff=html
*.ex    diff=elixir
*.exs   diff=elixir
*.go    diff=golang
*.php   diff=php
*.pl    diff=perl
*.py    diff=python
*.md    diff=markdown
*.rb    diff=ruby
*.rake  diff=ruby
*.rs    diff=rust
*.lisp  diff=lisp
*.el    diff=lisp


User-wide hooks.


When a Gemfile is found, and rubocop is enabled, run Rubocop on the files modified compared to the last merge commit. And since in our codebase everything is applied to master with a merge commit, this can be considered the upstream commit.

We also could have used @{upstream}, but that requires each branch to set it’s upstream branch, and that is not always the case.

if [[ -f Gemfile.lock && -x $(bundle exec which rubocop) ]]; then
  echo "> Running Rubocop.."
  git diff --name-only --diff-filter=d $(git log --merges -1 --pretty=format:%H) | xargs bundle exec rubocop
  exit 0

Standalone commands

A section of small, standalone git command scripts


A short command to abort any action that is ongoing. That could be:

  • cherry-pick
  • rebase
  • merge
git_status=$(git status)

if [[ $git_status =~ 'currently cherry-picking' ]]; then
  git cherry-pick --abort
  echo "Ongoing cherry-pick aborted."
  exit 0

if [[ $git_status =~ $(echo 'currently (editing a commit while )?rebasing') ]]; then
  git rebase --abort
  echo "Ongoing rebase aborted."
  exit 0

if [[ $git_status =~ 'you are still merging' ]]; then
  git merge --abort
  echo "Ongoing merge aborted."
  exit 0

echo >&2 "Nothing found to abort."
exit 1


A short command to continue any action that is ongoing. That could be:

  • cherry-pick
  • rebase
  • merge
git_status=$(git status)

if [[ $git_status =~ 'currently cherry-picking' ]]; then
  echo "Continuing ongoing cherry-pick."
  git cherry-pick --continue
  exit 0

if [[ $git_status =~ $(echo 'currently (editing a commit while )?rebasing') ]]; then
  git rebase --continue
  echo "Continuing ongoing rebase."
  exit 0

if [[ $git_status =~ 'you are still merging' ]]; then
  echo "Continuing ongoing merge."
  git commit
  exit 0

echo >&2 "Nothing found to continue."
exit 1


Get all files that are modified.

There was some discussion on this on team chat.

git diff --name-only --diff-filter=d


Sometimes your git can get locked, .git/index.lock in particular.

rm .git/index.lock

MR push

Push the current branch and create an MR. This uses the trailers from the commit template to add tags to the MR.

branch="$(git rev-parse --abbrev-ref $commit)"
desc="$(git config branch.$branch.description)"
if [ -z ${desc} ]
  desc="$(git log -1 --format=%s ${commit})"
desc="$(git log -1 --format=%b ${commit} | sed -z 's/\n/\\n/g')"
milestone="${MILESTONE:-$(git log -1 --format='%(trailers:key=Milestone,valueonly=true)' ${commit})}"
labels="$(git log -1 --format='%(trailers:key=Label,valueonly=true)' ${commit})"


if [ -n ${milestone} ]
  opts+=" -o merge_request.milestone=${milestone}"

for l in ${labels}
  opts+=" -o merge_request.label=${l}"

opts+=" -o merge_request.label=group::gitaly"
opts+=" -o merge_request.label=group::gitaly::git"

set -x

git push --set-upstream \
    -o merge_request.create \
    -o merge_request.title="${title}" \
    -o merge_request.description="${desc}" \
    $opts \