Configuration targets

In this section, we look at the different layers that can be configured. The layers are:

SYSTEM: This layer is system-wide and found in /etc/gitconfig

GLOBAL: This layer is global for the user and found in ~/.gitconfig

LOCAL: This layer is local to the current repository and found in .git/config

Getting ready

We will use the jgit repository for this example, as shown in the following command:

$ git clone https://git.eclipse.org/r/jgit/jgit$ cd jgit

How to do it...

In the previous example, we saw how we could use the command git config --list to list configuration entries. This list is actually made from three different levels of configuration that Git offers: system-wide configuration, SYSTEM; global configuration for the user, GLOBAL; and local repository configuration, LOCAL.

For each of these configuration layers, we can query the existing configuration. On a Windows box with a default installation of the Git extensions, the different configuration layers will look approximately like the following:

We can also query a single key and limit the scope to one of the three layers, by using the following command:

$ git config --global user.emailaske.olsson@switch-gears.dk

We can set the e-mail address of the user to a different one for the current repository:

$ git config --local user.email aske@switch-gears.dk

Now, listing the GLOBAL layer user.email will return aske.olsson@switch-gears.dk, listing LOCAL gives aske@switch-gears.dk, and listing user.email without specifying the layer gives the effective value that is used in the operations on this repository, in this case, the LOCAL value aske@switch-gears.dk. The effective value is the value, which takes precedence when needed. When two or more values are specified for the same key, but on different layers, the lowest layer takes precedence. When a configuration value is needed, Git will first look in the LOCAL configuration. If not found here, the GLOBAL configuration is queried. If it is not found in the GLOBAL configuration, the SYSTEM configuration is used. If none of this works, the default value in Git is used.

In the previous example, user.email is specified in both the GLOBAL and LOCAL layers. Hence, the LOCAL layer will be used.

How it works...

Querying the three layers of configuration simply returns the content of the configuration files: /etc/gitconfig for system-wide configuration, ~/.gitconfig for user-specific configuration, and .git/config for repository-specific configuration. When not specifying the configuration layer, the returned value will be the effective value.

There's more...

Instead of setting all the configuration values on the command line by the key value, it is possible to set them by just editing the configuration file directly. Open the configuration file in your favorite editor and set the configuration you need, or use the built-in git config -e repository to edit the configuration directly in the Git-configured editor. You can set the editor to the editor of your choice either by changing the $EDITOR environment variable or with the core.editor configuration target, for example:

$ git config --global core.editor vim

Querying the existing configuration

In this example, we will look at how we can query the existing configuration and set the configuration values.

Getting ready

We'll use jgit again by using the following command:

$ cd jgit

How to do it...

To view all the effective configurations for the current Git repository, run the following command:

How it works...

Git's configuration is stored in plaintext files, and works like a key-value storage. You can set/query by key and get the value back. An example of the text-based configuration file is shown as follows (from the jgit repository):

There's more...

It is also easy to set configuration values. Just use the same syntax as when querying the configuration except add an argument to the value. To set a new e-mail address on the LOCAL layer, we can execute the following command line:

git config user.email askeolsson@example.com

The LOCAL layer is the default if nothing else is specified. If you require whitespaces in the value, you can enclose the string in quotation marks, as you would do when configuring your name:

git config user.name "Aske Olsson"

You can even set your own configuration, which does not have any effect on the core Git, but can be useful for scripting/builds and so on:

$ git config my.own.config "Whatever I need"

List the value

$ git config my.own.config Whatever I need

It is also very easy to delete/unset configuration entries:

$ git config --unset my.own.config

List the value

$ git config my.own.config

Templates

In this example, we will see how to create a template commit message that will be displayed in the editor when creating a commit. The template is only for the local user and not distributed with the repository in general.

Getting ready

In this example, we will use the example repository:

$ git clone https://github.com/dvaske/data-model.git$ cd data-model

We'll use the following code as a commit message template for commit messages:

Short description of commit
Longer explanation of the motivation for the change
Fixes-Bug: Enter bug-id or delete line
Implements-Requirement: Enter requirement-id or delete line

Save the commit message template in $HOME/.gitcommitmsg.txt. The filename isn't fixed and you can choose a filename of your liking.

How to do it...

To let Git know about our new commit message template, we can set the configuration variable commit.template to point at the file we just created with that template; we'll do it globally so it is applicable to all our repositories:

$ git config --global commit.template $HOME/.gitcommitmsg.txt

Now, we can try to change a file, add it, and create a commit. This will bring up our preferred editor with the commit message template preloaded:

$ git commitShort description of commitLonger explanation of the motivation for the changeFixes-Bug: Enter bug-id or delete lineImplements-Requirement: Enter requirement-id or delete line# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.# On branch master# Changes to be committed:# (use "git reset HEAD <file>..." to unstage)## modified: another-file.txt#~~"\.git/COMMIT_EDITMSG" 13 lines, 396 characters

We can now edit the message according to our commit and save to complete the commit.

How it works...

When commit.template is set, Git simply uses the content of the template file as a starting point for all commit messages. This is quite convenient if you have a commit-message policy as it greatly increases the chances of the policy being followed. You can even have different templates tied to different repositories, since you can just set the configuration at the local level.

A .git directory template

Sometimes, having a global configuration isn't enough. You will also need to trigger the execution of scripts (aka Git hooks), exclude files, and so on. It is possible to achieve this with the template option set to git init. It can be given as a command-line option to git clone and git init, or as the $GIT_TEMPLATE_DIR environment variable, or as the configuration option init.templatedir. It defaults to /usr/share/git-core/templates. The template option works by copying files in the template directory to the .git ($GIT_DIR) folder after it has been created. The default directory contains sample hooks and some suggested exclude patterns. In the following example, we'll see how we can set up a new template directory, and add a commit message hook and exclude file.

Getting ready

First, we will create the template directory. We can use any name we want, and we'll use ~/.git_template, as shown in the following command:

$ mkdir ~/.git_template

Now, we need to populate the directory with some template files. This could be a hook or an exclude file. We will create one hook file and an exclude file. The hook file is located in .git/hooks/name-of-hook and the exclude file in .git/info/exclude. Create the two directories needed hooks and info, as shown in the following command:

$ mkdir ~/.git_template/{hooks,info}

To keep the sample hooks provided by the default template directory (the Git installation), we copy the files in the default template directory to the new one. When we use our newly created template directory, we'll override the default one. So, copying the default files to our template directory will make sure that except for our specific changes the template directory is similar to the default one, as shown in the following command:

The hook is very simple and will just add Hi from the template commit-msg hook to the end of the commit message. Save it as commit-msg in the ~/.git_template/hooks directory and make it executable by using the following command:

chmod +x ~/.git_template/hooks/commit-msg

Now that the commit message hook is done, let's also add an exclude file to the example. The exclude file works like the .gitignore file, but is not tracked in the repository. We'll create an exclude file that excludes all the *.txt files, as follows:

$ echo *.txt > ~/.git_template/info/exclude

Now, our template directory is ready for use.

How to do it...

Our template directory is ready and we can use it, as described earlier, as a command-line option, an environment variable or, as in this example, to be set as a configuration:

$ git config --global init.templatedir ~/.git_template

Now, all Git repositories we create using init or clone will have the default files of the template directory. We can test if it works by creating a new repository as follows:

$ git init template-example$ cd template-example

Let's try to create a .txt file and see what git status tells us. It should be ignored by the exclude file from the template directory:

$ echo "this is the readme file" > README.txt$ git status

The exclude file worked! You can put in the file endings yourself or just leave it blank and keep to the .gitignore files.

To test if the commit-msg hook also works, let us try to create a commit. First, we need a file to commit. So, let's create that and commit it as follows:

How it works...

When Git creates a new repository, either via init or clone, it will copy the files from the template directory to the new repository when creating the directory structure. The template directory can be defined either by a command-line argument, environment variable, or configuration option. If nothing is specified, the default template directory will be used (distributed with the Git installation). By setting the configuration as a --global option, the template directory defined will apply to all of the user's (new) repositories. This is a very nice way to distribute the same hooks across repositories, but it also has some drawbacks. As the files in the template directory are only copied to the Git repositories, updates to the template directory do not affect the existing repositories. This can be solved by running git init in each existing repository to reinitialize the repository, but this can be quite cumbersome. Also, the template directory can enforce hooks on some repositories where you don't want them. This is quite easily solved by simply deleting the hook files in .git/hooks of that repository.

A few configuration examples

There are configuration targets in the core Git system. In this section, we'll take a closer look at a few of them that might be useful in your daily work.

We'll look at the following three different configuration areas:

Rebase and merge setup

Expiry of objects

Autocorrect

Getting ready

In this exercise, we'll just set a few configurations:

$ cd data-model

How to do it...

Let's take a closer look at the previously mentioned configuration areas.

Rebase and merge setup

By default, when performing git pull, a merge commit will be created if the history of the local branch has diverged from the remote one. However, to avoid all these merge commits, a repository can be configured so it will default to rebase instead of merging when doing git pull. Several configuration targets related to the option exist as follows:

pull.rebase: This configuration, when set to true, will pull to rebase the current branch on top of the fetched one when performing a git pull. It can also be set to preserve so that the local merge commit will not be flattened in the rebase, by passing --preserve-merges to git rebase. The default value is false as the configuration is not set. To set this option in your local repository, run the following command:

$ git config pull.rebase true

branch.autosetuprebase: When this configuration is set to always, any new branch created with <git branch or git checkout that tracks another branch will be set up to pull to rebase (instead of merge). The valid options are as follows:

never: This is set to pull to rebase (default)

local: This is set to pull to rebase for local tracked branches

remote: This is set to pull to rebase for remote tracked branches

always: This is set to pull to rebase for all tracked branches

To set this option for all the new branches regardless of tracking remote or local branches, run the following command:

$ git config branch.autosetuprebase always

branch.<name>.rebase: This configuration, when set to true, applies only to the <name> branch and tells Git to pull to rebase when performing git pull on the given branch. It can also be set to preserve so that the local merge commit will not be flattened when running git pull. By default, the configuration is not set for any branch. To set the feature/2 branch in the repository to default to rebase instead of merge, we can run the following command:

$ git config branch.feature/2.rebase true

Expiry of objects

By default, Git will perform garbage collection on unreferenced objects and clean reflog for entries, both of which are older than 90 days. For an object to be referenced, something must point to it; a tree, a commit, a tag, a branch, or some of the internal Git bookkeeping like stash or reflog. There are three settings that can be used to change this time as follows:

gc.reflogexpire: This is the general setting to know for how long a branch's history is kept in reflog. The default time is 90 days. The setting is a length of time, for example, 10 days, 6 months and it can be turned completely off with the value never. The setting can be set to match a refs pattern by supplying the pattern in the configuration setting. gc.<pattern>.reflogexpire: This pattern can, for example, be /refs/remotes/* and the expire setting would then only apply for those refs.

gc.reflogexpireunreachable: This setting controls how long the reflog entries that are not a part of the current branch history should be available in the repository. The default value is 30 days, and similar to the previous option, it is expressed as a length of time or set to never in order to turn it off. This setting can, as the previous one, be set to match a refs pattern.

gc.pruneexpire: This option tells git gc to prune objects older than the value. The default is 2.weeks.ago, and the value can be expressed as a relative date like 3.months.ago. To disable the grace period, the value now can be used. To set a non-default expiry date only on remote branches, use the following command:

Autocorrect

This configuration is useful when you get tired of messages like the following one just because you made a typo on the keyboard:

$ git statisgit: 'statis' is not a git command. See 'git --help'.Did you mean this? status

By setting the configuration to help.autocorrect, you can control how Git will behave when you accidentally send a typo to it. By default, the value is 0 and it means to list the possible options similar to the input (if statis is given status will be shown). A negative value means to immediately execute the corresponding command. A positive value means to wait the given number of deciseconds (0.1 sec) before running the command, (so there is some amount of time to cancel it). If several commands can be deduced from the text entered, nothing will happen. Setting the value to half a second gives you some time to cancel a wrong command, as follows:

$ git config help.autocorrect 5$ git statisWARNING: You called a Git command named 'statis', which does not exist.Continuing under the assumption that you meant 'status'in 0.5 seconds automatically...# On branch master# Changes to be committed:# (use "git reset HEAD <file>..." to unstage)## modified: another-file.txt#

How it works...

Setting the configuration targets will change the way Git behaves. The previous examples describe a few useful methods to get Git to act differently than its default behavior. You should be sure when you are changing a configuration that you completely understand what that configuration does. So, check the Git configuration help page by using git help config.

There's more...

There are a lot of configuration targets available in Git. You can run git help config and a few pages down all of them are displayed and explained.

Git aliases

An alias is a nice way to configure long and/or complicated Git commands to represent short useful ones. An alias is simply a configuration entry under the alias section. It is usually configured to --global to apply it everywhere.

Getting ready

In this example, we will use the jgit repository, with the master branch pointing at b14a93971837610156e815ae2eee3baaa5b7a44b. Clone the repository again, as follows:

How to do it...

First, we'll create a few simple aliases, then a couple of more special ones, and finally a couple of aliases using external commands. Instead of writing git checkout every time we need to switch branches, we can create an alias of that command and call it git co. We can do the same for git branch, git commit, and git status as follows:

The alias method is also good to create the Git commands you think are missing in Git. One of the common Git aliases is unstage, which is used to move a file out of the staging area, as shown in the following command:

$ git config --global alias.unstage 'reset HEAD --'

Try to edit the README.md file in the root of the jgit repository and add it in the root. Now, git status/git st should display something like the following:

A common use case for aliases is to format the history of Git in specific ways. Let's say you want the number of lines added and deleted for each file in the commit displayed along with some common commit data. For this, we can create the following alias so we don't have to type everything each time:

It is also possible to use an external command instead of a Git command. So, small shell scripts and so on can be embedded. To create an alias with an external command, the alias must start with an exclamation mark !. The examples can be used when resolving conflicts from a rebase or merge. In your ~/.gitconfig file under [alias], add the following:

Now, you'll see that this fails to perform the merge, and you can run git st to check the status a lot of files are in a conflicted state, both modified. To open and edit all the conflicted files, we can now run git editconflicted. This brings up $EDITOR with the files. If your environment variable isn't set, use EDITOR=<you-favorite-editor> export to set it.

For this example, we don't actually resolve the conflicts. Just check that the alias works and you're ready for the next alias.

Now that we have solved all the merge conflicts, it is time to add all of those files before we conclude the merge. Luckily, we can create an alias that can help us with that, as follows:

How it works...

Git simply runs the command the alias is short for. It is very convenient for long Git commands, or Git commands that are hard, to remember exactly how to write. Now, all you have to remember is the alias and you can always look in the configuration file for it.

There's more...

Another way to create a kind of Git alias is to make a shell script and save the file with the name git-<your-alias-name>. Make the file executable and place it somewhere in your $PATH. You can now run that file simply by running git<your-alias-name> from the command line.

The refspec exemplified

Though the refspec isn't the first thing that comes to mind when thinking about the Git configuration, it is actually quite close. In a lot of the Git commands the refspec is used, but often implicitly, that is, the refspec is taken from the configuration file. If you don't remember setting any refspec configuration, you are probably right, but if you cloned the repository or added a remote, you'll have a section in .git/config, which looks something like the following (this is for the Jgit repository):

The fetch line contains the configured refspec to fetch for this repository.

Getting ready

In this example, we'll be using the jgit repository as our server repository, but we have to make a clone of it to a bare repository so we can push it. You can't push to the checked out branch on a non-bare repository as this can overwrite the work area and index.

Create a bare repository from the jgit repository and create a new Git repository where we can play with the refspec as follows:

In the previous shell scripting, the $new and $br variables aren't placed in double quotes (") as good practice for shell scripting would otherwise suggest. This is okay as the variables reflect the names of the branches in the repository and branch names cannot contain spaces.

How to do it...

Let us set up our new repository to only fetch the master branch. We do this by changing the fetch line under [remote "origin"] in the configuration file (.git/config), as follows:

As the integration/master branch didn't exist on the remote side, it was created for us.

How it works...

The format of the refspec is in the form of <source>:<destination>. For a fetch refspec, this means that <source> is the source on the remote side and <destination> is local. For a push refspec, <source> is local and <destination> is remote. The refspec can be prefixed by a + to indicate that the ref pattern can be updated even though it isn't a fast-forward update. It is not possible to use partial globs in the refspec pattern, as shown in the following line:

fetch = +refs/heads/stable*:refs/remotes/origin/stable*

But it is possible to use namespacing. That's why we had to rewrite the stable-xxx branches to stable/xxx to fit as a namespace pattern:

fetch = +refs/heads/stable/*:refs/remotes/origin/stable/*

Summary

In this article, we learned cover configuration targets, querying the existing configuration, templates, a .git directory template, a few configuration examples, Git aliases, and the refspec exemplified.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.