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.
[user] useConfigOnly = true name = Toon Claes
Core config
[core]
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 = ";"
CR/LF
Don’t do output conversion on end-of-line.
autocrlf = input
Init
[init]
Default branch
When creating a new repo, set the default branch to main
.
defaultBranch = main
Commit
[commit]
Template
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
Status
[status]
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
Merge
[merge]
Mergiraf
Use mergiraf for better automatic merge conflict resolving.
name = mergiraf driver = mergiraf merge --git %O %A %B -s %S -x %X -y %Y -p %P
Fetching
[fetch]
Prune
Prune the local tracking branches and tags when fetching from remote.
prune = true pruneTags = true
Pull
[pull]
Fast-forward only
When pulling only update the current branch by fast-forwarding.
ff = only
Transfer
[transfer]
Bundle-URI
Enable the use of Bundle-URI when the server advertises it.
bundleURI = true
Aliases
Define a set of aliases.
[alias]
Scrub
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
Work-in-progress
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. Improved wip
from
Aziz Nal, who took the idea from ohmyzsh, which might have gotten it from
@haacked.
wip = !git add --all && git commit --no-verify --no-gpg-sign --message 'WIP [skip ci]' unwip = !git log --max-count=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' -
Loglog
Fancy shortlog.
loglog = log --graph --oneline --all
Rerere
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.
[rerere] 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\\\")\"
LFS
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
Sendmail
Configure git to send mails directly from git.
Instructions came from git-send-email.io.
I have msmtp
configured to send mails through Emacs, so I can just
use that tool here too.
[sendemail] smtpserver = /usr/bin/msmtp
B4
I’m using b4 to send patches to the Git mailing list. This can use some configuration.
[b4]
Signing
Don’t sign mails with patatt.
send-no-patatt-sign = yes
Skip auto-CC
On the Git mailing list there isn’t an auto-CC script, so skip that step.
prep-pre-flight-checks = disable-needs-auto-to-cc
Global Gitignore
Ruby vendoring
Ignore gems installed in the local vendor
directory.
**/vendor/bundle
GNU Global
For a while I used GNU Global for tagging code, so ignore the TAGS
files from being committed.
GPATH GRTAGS GTAGS TAGS
CCLS LSP
For C code I’m using CCLS with LSP in emacs. Ignore the files this creates.
.ccls-cache/
Emacs
Dir locals
Most project don’t like Emacs .dir-locals.el
.
.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.
!.gitlab !.gitlab-ci.yml !.github
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.
compile_flags.txt .ccls
There is also compile_commands.json
, which needs to be generated. You can
either use Bear to generate it. This is done by prefixing the make
command
with bear --
, or tools like Meson generate it automatically.
compile_commands.json
Byebug
Byebug creates a history file in the current working directory, and that’s not something you want to check in.
.byebug_history
Git attributes
Diff
Improve diff output for various file types.
From: https://tekin.co.uk/2020/10/better-git-diff-output-for-ruby-python-elixir-and-more
*.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
Merge
Use mergiraf
for automatically conflict resolution.
mergiraf languages --gitattributes
nil
Hooks
User-wide hooks.
Pre-push
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 else exit 0 fi
Standalone commands
A section of small, standalone git command scripts
Abort
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 fi if [[ $git_status =~ $(echo 'currently (editing a commit while )?rebasing') ]]; then git rebase --abort echo "Ongoing rebase aborted." exit 0 fi if [[ $git_status =~ 'you are still merging' ]]; then git merge --abort echo "Ongoing merge aborted." exit 0 fi echo >&2 "Nothing found to abort." exit 1
Continue
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 fi if [[ $git_status =~ $(echo 'currently (editing a commit while )?rebasing') ]]; then git rebase --continue echo "Continuing ongoing rebase." exit 0 fi if [[ $git_status =~ 'you are still merging' ]]; then echo "Continuing ongoing merge." git commit exit 0 fi echo >&2 "Nothing found to continue." exit 1
Modified
Get all files that are modified.
There was some discussion on this on team chat.
git diff --name-only --diff-filter=d
Unlock
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.
# Look up branch description first branch="$(git rev-parse --abbrev-ref $commit)" msg="$(git config branch.$branch.description || true)" # Next try to find a commit that can be used as cover letter commit="${COMMIT}" if [ -z "${commit}" ] then commit="$(git log --grep '--- b4-submit-tracking ---' --format=%H)" fi if [ -z "${commit}" ] then commit="HEAD". fi if [ -z "${msg}" ] then msg="$(git log -1 --format=%B ${commit})" fi title="$(echo "${msg}" | head -1)" desc="$(echo "${msg}" | tail -n+3 | 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})" opts="" if [ -n "${milestone}" ] then opts+=" -o merge_request.milestone=${milestone}" fi for l in ${labels} do opts+=" -o merge_request.label=${l}" done opts+=" -o merge_request.label=group::git" set -x git push --set-upstream \ -o merge_request.create \ -o merge_request.title="${title}" \ -o merge_request.description="${desc}" \ $opts \ ${@}