Git config management

Setting, unsetting, and forgetting
The git logo with the title of the article superimposed next to it.

Tie up your loose ends!

We've been looking a lot at how to tweak git to your liking, which is usually done through your git config file. Sometimes, though, you'll find that the configuration isn't working as you wanted, so I thought it'd be a good idea to look at how you can inspect, unset, and list configuration values from the command line.

Before we dive in, let's have a few words about how your git config files work. Your config files use a specific format which, according to the docs, "consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next section begins." That's enough for what we need right now, but go read up if you're interested.

Furthermore, git config files 'cascade', and git, by default, searches four locations on your system, where later entries override earlier ones:

~/etc/gitconfig~
The system wide config
~$XDG_CONFIG_HOME/git/config~
Falls back to $HOME/.config/git/config if $XDG_CONFIG_HOME is not set or empty.
~~/.gitconfig~
Your user-specific, 'global' config file
~$GIT_DIR/config~
Repo-specific config file

Again, see the specific section of the documentation for more information.

One last thing: All setting and unsetting of values apply only to your user configurations, and they are local---repo-specific---by default. To make them apply to your global config, pass the --global flag.

Setting values

To set a value, simply run a command on the form

    # default (local)
    git config 
. # global git config --global
.

For instance, to change the value of the commentChar entry of the core section to ;:

    git config core.commentChar ';'

Unsetting values

Similarly, if you want to unset a value, use the --unset flag:

    git config --unset 
.

So if you have overridden the core.commentChar value, this is the command to undo that and reset it to the default value, #:

    git config --unset core.commentChar

Inspecting config values

Sometimes, you want to know what a value has been set to. For this, pass the --get flag and the section and key of the value you want to look up:

    git config --get 
.

So if you're wondering what your user's name is, for instance:

    git config --get user.name

A few things to note about getting the values

Git will show you the last value it finds in the chain
So if you set your username in your global config, but set a different one in a repo and invoke this command from the repo, it would show you only the repo-specific one.
The output will be empty if you have not explicitly set the value
In other words, if it's not in your config files, git won't show it to you. So if you're looking to inspect git's default values, that's not going to work.

Finding the config value sources

Sometimes you just want to inspect what values you're setting in your config. You can do this by passing the --list flag:

    git config --list

By default, this will just throw all your configuration at you, which isn't all that helpful. However, it takes a pretty handy flag, --show-origin, which lists your configuration in two columns, with the first one being the file in which it was set and the second being the the key-value pairs.

If you ever find that some value isn't taking effect the way you expect, this is a great way to check what values are getting set in what locations:

    git config --list --show-origin

For instance, if you've changed your global user name, but it's not taking effect in the current repo, you could pipe it into grep and look for duplicates:

    git config --list --show-origin | grep "user.name="

The git logo with the title of the article superimposed next to it.

Removing paper cuts

Here's a statement for ya: if you're working on a project with someone else, you should be using a commit message template. Not only will it help increase the consistency and usefulness of your messages, but also, and perhaps more importantly, it will reduce the cognitive load required to write a message and the time it takes to write it. I can only speak for myself, but I know that having to go look up information about how a commit message should be structured and what it references causes some much unneeded context switching, resulting in a less efficient workflow.

Setup

Setting up commit message templates requires only a few steps:

  1. Create a template.

You can use any text file as a template; just put what you want to show up in the commit message buffer in there. Plain lines of text will show up as content, while lines starting with the comment character (# by default) show up as comments.

  1. Add it to the git config as a commit template.
    git config commit.template 
  1. Done! Now try committing something.

When doing this, you're assigning the template locally (to the current repo). If you'd rather do it globally, pass the --global option.

If you want to share the template across multiple projects in a certain subdirectory, you can do that by combining the template with git's functionality for conditional includes. Be aware, however, that given a relative path to a file, git will look for the file relative to the including configuration's location. While this can potentially be exploited to use different templates for different projects, I've not found the need for it thus far, and have stuck with a single template with an absolute path.

If you want to unset the template, use the git config --unset functionality:

    git config --unset commit.template

Commit template ideas

Different teams and projects have different needs when it comes to commit messages, so there's no 'one size fits all', but here's a few ideas based on message formats in teams I've been a part of.

Label listing

If you label your commits with the type of work they contain to and have a defined set of labels (e.g. 'feature', 'bug', 'chore', 'refactor', etc.), it might be a good idea to list all the different options in the template, so that you can look through them whenever you're committing some code. List them using commented lines and you don't even have to delete them; they'll just show up as extra info in the commit buffer.

While experienced members of the team are likely to know these by heart, it's very helpful for a new person to have the list right there in the commit buffer so that they don't have to go look them up.

Issue identification

If the standard says to have an issue/ticket ID listed in the commit message, you could put a little placeholder in the template that tells you to replace it with the relevant ID.

If you also work with feature branches and name them according to the feature/issue ID that they correspond to, you could even have git fill in this issue number automatically by using hooks, as explained in my previous post. I have found this to save me a surprisingly large amount of time and mental overhead.

Very specific formats

For when your message needs to match a very specific format and it's hard to remember exactly what goes where, templates are a perfect solution.

For instance, if you're working with a combination of the above two, where you want both the label and the ID, and you want them in a specific order, maybe even with a specific separator (seems like a hassle, but just roll with it), you could create a template that you could just fill in with the relevant parts, saving you having to look up what the format is every time.


Rebasing off a repo root

Rewrite your history
The git logo with the title of the article superimposed next to it.

Burn your biographies 🎶

Ever had to change something about all the commits in a git repo before pushing it to a remote? As in from the very first commit in a repo? Maybe you've started a repo locally and when you're ready to push it, you realize that you've used the wrong author or messed up the format of the commit messages; or maybe you just want to squash those first few commits into a single concise package.

In these situations, my first response is to do to an interactive rebase (git rebase -i). Usually when I'm rebasing, though, I'm in a project that has an upstream and where I'm rebasing off a specific commit or branch. For situations where you don't have a commit to rebase off, but you want to rebase the entire history or at least the very first commit: What do you do?

The answer, my friend, is that you pass the --root option:

  git rebase -i --root

That'll let you pick, reword, edit, squash, fixup, exec, drop, label, reset, or merge all of your commits from the 'dawn of time.'

Have fun!