Configuring Git

So far, while describing how Git works and how to use it, we have introduced a number of ways to change its behavior. Here, it will be explained in the systematic fashion how to configure Git operations on a temporary and permanent basis. We will also see how you can make Git behave in a customized fashion by introducing and reintroducing several important configuration settings. With these tools, it's easy to get Git to work the way you want it to.

Command-line options and environment variables

Git processes the switches that change its behavior in a hierarchical fashion, from the least specific to the most specific one, with the most specific one (and shortest term) moved earlier taking precedence.

The most specific one, overriding all the others, is the command-line options. They affect, obviously, only the current Git command.

Note

One issue to note is that some command-line options, for example--no-pager or --no-replace-objects go to the git wrapper, not to the Git command itself. Examine, for example, the following line to see the distinction:

$ git --no-replace-objects log -5 --oneline --graph --decorate

You can find the conventions used through the Git command-line interface on https://www.kernel.org/pub/software/scm/git/docs/gitcli.html manpage.

The second way to change how the Git command works is to use environment variables. They are specific to the current shell, and you need to use export to propagate the variables to the subprocesses if replacement is used. There are some environment variables that apply to all core Git commands, and some that are specific to a given (sub)command.

Git also makes use of some nonspecific environment variables. These are meant as a last resort; they are overridden by their Git specific equivalents. Examples include variables such as PAGER and EDITOR.

Git configuration files

The final way to customize how Git works is with the configuration files. In many cases, there is a command-line option to configure an action, an environment variable for it, and finally a configuration variable, in the descending order of preference.

Git uses a series of configuration files to determine nondefault behavior that you might want to have. There are three layers of those that Git looks for configuration values. Git reads all these files in order from the least specific to the most specific one. The settings in the later ones override those set in the earlier ones. You can access the Git configuration with the git config command: by default, it operates on the union of all the files, but you can specify which one you want to access with the command-line options. You can also access any given file following the configuration file syntax (such as the .gitmodules file mentioned in Chapter 9, Managing Subprojects - Building a Living Framework) by using the --file=<pathname> option (or the GIT_CONFIG environment variable).

Note

You can also read the values from any blob with configuration-like contents; for example, you may use git config--blob=master:.gitmodules to read from the .gitmodules file in the master branch.

The first place Git looks for configuration is the system-wide configuration file. If Git is installed with the default settings, it can be found in /etc/gitconfig. Well, at least, on Linux it is there, as the Filesystem Hierarchy Standard (FHS) states that /etc is the directory for storing the host-specific system-wide configuration files; Git for Windows puts this file in the subdirectory of its Program Files folder. This file contains the values for every user on the system and all their repositories. To make git config read and write from and to this file specifically (and to open it with --edit), pass the --system option to the git config command.

You can skip the reading settings from this file with the GIT_CONFIG_NOSYSTEM environment variable. This can be used to set up a predictable environment or to avoid using a buggy configuration you can't fix.

The next place Git looks is ~/.gitconfig, falling back to ~/.config/git/config if it exists (with the default configuration). This file is specific to each user and it affects all the user's repositories. If you pass the option --global to git config, it would read and write from this file specifically. Reminder: here, as in the other places, ~ (the tilde character) denotes the home directory of the current user ($HOME).

Finally, Git looks for the configuration values in the per-repository configuration file in the Git repository you are currently using, which is (by default and for nonbare repositories) .git/config. Values set there are specific to that local single repository. You can make Git read and write to this file by passing the --local option.

Each of these levels (system, global, and local) overrides values in the previous level, so for example, values in .git/config trump those in ~/.gitconfig; well, unless the configuration variable is multivalued.

Note

You can use this to have your default identity in the per-user file and to override it if necessary on a per-repository basis with a per-repository configuration file.

The syntax of Git configuration files

Git's configuration files are plain text, so you can also customize Git's behavior by manually editing the chosen file. The syntax is fairly flexible and permissive; whitespaces are mostly ignored (contrary to .gitattributes). The hash # and semicolon ; characters begin comments, which last until the end of the line. Blank lines are ignored.

The file consists of sections and variables, and its syntax is similar to the syntax of INI files. Both the section names and variable names are case-insensitive. A section begins with the name of the section in square brackets [section] and continues until the next section. Each variable must begin at some section, which means that there must be a section header before the first setting of a variable. Sections can repeat and can be empty.

Sections can be further divided into subsections. Subsection names are case-sensitive and can contain any character except newline (double quotes " and backslash must be escaped as " and \, respectively). The beginning of the subsection will look as follows:

[section "subsection"]

All the other lines (and the remainder of the line after the section header) are recognized as a setting variable in the name = value form. As a special case, just name is a shorthand for name = true (boolean variables). Such lines can be continued to the next line by ending it with "" (the backslash character), that is by escaping the end-of-line character. Leading and trailing whitespaces are discarded; internal whitespaces within the value are retained verbatim. You can use double quotes to preserve leading or trailing whitespaces in values.

You can include one config file from another by setting the special variable include.path to the path of the file to be included. The included file will be expanded immediately, similar to the mechanism of #include in C and C++. The path is relative to the configuration file with the include directive. You can turn this feature off with --no-includes.

Tip

Types of configuration variables and type specifiers

While requesting (or writing) a config variable, you may give a type specifier. It can be --bool, which ensures that the returned value is true or false; --int, which expands the optional value suffix of k (1024 elements), m (1024k), or g (1024m); --path, which expands ~ for the value of $HOME; and ~user for the home directory of the given user. There is also --bool-or-int and a few options related to storing colors and retrieving color escape codes; see the git config documentation.

Accessing the Git configuration

You can use the git config command to access the Git configuration, starting from listing the configuration entries in a canonical form, through examining individual variables, to editing and adding entries.

You can query the existing configuration with git config --list, adding an appropriate parameter if you want to limit to a single configuration layer. On a Linux box with the default installation, in the fresh empty Git repository just after git init, the local (per-repository) setting would look approximately like the following:

$ git config --list --local
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true

You can also query a single key with git config, limiting or not the scope to the specified file, by giving the name of configuration variable as a parameter (optionally preceded by --get), with the section, optional subsection, and variable name (key) separated by dot:

$ git config user.email
[email protected]

This would return the last value, that is, the one with the most precedence. You can get all the values with --get-all, or specific keys with --get-regexp=<match>. This is quite useful while accessing a multivalued option like refspecs for a remote.

With --get, --get-all, and --get-regexp, you can also limit the listing (and the settings for multiple-valued variables) to only those variables matching the value regexp (which is passed as an optional last parameter). For example:

$ git config --get core.gitproxy 'for kernel.org$'

You can also use the git config command to set the configuration variable value. For example, to set the e-mail address of the user, which is to be common to most of his or her repositories, you can run the following:

$ git config --global user.name "Alice Developer"

To change multivar, you can use:

$ git config core.gitproxy '"ssh" for kernel.org'  'for kernel.org$'

The local layer (per-repository file) is the default for writing, if nothing else is specified. For multivalue configuration options, you can add multiple lines to it by using the --add option.

It is also very easy to delete configuration entries with git config --unset.

Instead of setting all the configuration values on the command line, as shown in the preceding example, it is possible to set or change them just by editing the relevant configuration file directly. Simply open the configuration file in your favorite editor, or run the git config --edit command.

The local repository configuration file just after a fresh init on Linux looks as follows:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true

Basic client-side configuration

You can divide the configuration options recognized by Git into two categories: client-side and server-side. The majority of the options are about configuring your personal working preferences; they are client-side. The server-side configuration will be touched upon in more detail in Chapter 11, Git Administration; here you will find only basics.

There are many supported configuration options, but only a small fraction of them needs to be set; a large fraction of them has sensible defaults , and explicitly setting them is only useful in certain edge cases. There are a lot of options available; you can see a list of all the options with git config --help. Here we'll be covering only the most common and most useful options.

Two variables that really need to be set up are user.email and user.name. Those configuration variables define the author's identity. Also, if you are signing annotated tags or commits (as discussed in Chapter 5, Collaborative Development with Git), you might want to set up your GPG signing key ID. This is done with the user.signingKey configuration setting.

By default, Git uses whatever you've set on the system as your default text editor (defined with the VISUAL or EDITOR environment variables; the first only for the graphical desktop environment) to create and edit your commit and tag messages. It also uses whatever you have set as the pager (PAGER) for paginating and browsing the output of the Git commands. To change this default to something else, you can use the core.editor setting. The same goes for core.pager. Git would ultimately fall back on the vi editor and on the less pager.

Note

With Git, the pager is invoked automatically. The default less pager supports not only pagination, but also incremental search for example. Also, with the default configuration (the LESS environment variable is not set) less invoked by Git works as if it was invoked with LESS=FRX. This means that it would skip pagination of there is less than one page of output, it would pass through ANSI color codes, and it would not clear screen on exit.

Creating commit messages is also affected by commit.template. If you set this configuration variable, Git will use that file as the default message when you commit. The template is not distributed with the repository in general. Note that Git would add the status information to the commit message template, unless it is forbidden to do it by setting commit.status to false.

Such a template is quite convenient if you have a commit-message policy, as it greatly increases the chances of this policy being followed. It can, for example, include the commented-out instructions for filling the commit message. You can augment this solution with an appropriate hook that checks whether the commit message matches the policy (see the Commit process hooks section in this chapter).

The status of the files in the working area is affected by the ignore patterns and the file attributes (see Chapter 4, Managing Your Worktree). You can put ignore patterns in your project's in-tree .gitignore file (usually tracked '.gitignore' is about which files are tracked, and is tracked itself by Git (not by itself). itself), or in the .git/info/excludes file for local and private patterns, to define which files are not interesting. These are project-specific; sometimes, you would want to write a kind of global (per-user) .gitignore file. You can use core.excludesFile to customize the path to the said file; in modern Git, there is a default value for this path, namely,~/.config/git/ignore. There is also a corresponding core.attributesFile for this kind of global .gitattributes files, which defaults to ~/.config/git/attributes.

Note

Actually, it is $XDG_CONFIG_HOME/git/ignore; if the $XDG_CONFIG_HOME environment variable is not set or is empty, $HOME/.config/git/ignore is used.

Although Git has an internal implementation of diff, you can set up an external tool to be used instead with the help of diff.external. You would usually want to create a wrapper script that massages the parameters that Git passes to it, and pass the ones needed in the order external diff requires. By default, Git passes the following arguments to the diff program:

path old-file old-hex old-mode new-file new-hex new-mode

See also the Graphical diff and merge tools section for the configuration of git difftool and git mergetool.

The rebase and merge setup, configuring pull

By default, when performing git pull (or equivalent), Git would use the merge operation to join the local history and the history fetched from the remote. This would create a merge commit if the history of the local branch has diverged from the remote one. Some argue that it is better to avoid all these merge commits and create mostly a linear history by using rebase instead (for example, with git pull --rebase) to join histories. You can find more information on this topic in Chapter 7, Merging Changes Together.

There are several configuration settings that can be used to make the git pull default to rebase, to set up tracking, and so on. There is the pull.rebase configuration option and a branch-specific branch.<name>.rebase option that, when set to true, tells Git to perform rebase instead of merge during pull (for the <name> branch only in a later case). Both can also be set to preserve to run rebase with the--preserve-merges option, to have local merge commits not be flattened in the rebase.

You can make Git automatically set up the per-branch "pull to rebase" configuration while creating specific kinds of new branches with branch.autoSetupRebase. You can set it to never, local (for locally tracked branches only), remote (for remote tracked branches only), or always (for local plus remote).

Preserving undo information – the expiry of objects

By default, Git will automatically remove unreferenced objects, clean reflogs of stale entries, and pack loose objects, all to keep the size of the repository down. You can also run the garbage collection manually with the git gc command. You should know about repository's object-oriented structure from Chapter 8, Keeping History Clean.

Git will, for safety reasons, use a grace period of two weeks while removing unreferenced objects for; this can be changed with the gc.pruneExpire configuration: the setting is usually a relative date (for example, 1.month.ago; you can use dots as a word separator). To disable the grace period (which is usually done from the command line), the value now can be used.

The branch tip history is kept for 90 days by default (or gc.reflogExpire, if set) for reachable revisions, and for 30 days (or gc.reflogExpireUnreachable) for reflog entries that are not a part of the current history. Both settings can be configured on a per-refname basis, by supplying a pattern of the ref name to be matched as a subsection name, that is, gc.<pattern>.reflogExpire, and similar for the other setting. This can be used to change the expire settings for HEAD or for refs/stash (see Chapter 4, Managing Your Worktree), or for remote-tracking branches refs/remotes/* separately. The setting is a length of time (for example, 6.months); to completely turn of reflog expiring use the value of never. You can use the latter for example to switch off expiring of stash entries.

Formatting and whitespace

Code formatting and whitespace issues are some of the more frustrating and subtle problems you may encounter while collaborating, especially with cross-platform development. It's very easy for patches and merges to introduce subtle whitespace changes, because of editors silently introducing such changes (often not visible) and a different notion of line endings on different operating systems: MS Windows, Linux, and MacOS X. Git has a few configuration options to help with these issues.

One important issue for cross-platform work is the notion of line-ending. This is because MS Windows uses a combination of a carriage return (CR) character and a linefeed (LF) character for new lines in text files, whereas MacOS and Linux use only a linefeed character. Many editors on MS Windows will silently replace existing LF-style line endings with CRLF or use CRLF for new lines, which leads to subtle but annoying troubles.

Git can handle this issue by auto-converting line endings into LF when you add a file to the index. If your editor uses CRLF line endings, Git can also convert line-endings to the native form when it checks out code in your filesystem. There are two configuration settings that affect this matter: core.eol and core.autocrlf. The first setting, core.eol, sets the line ending to be used while checking out files into the working directory for files that have the text property set (see the next section, Per-file configuration with gitattributes, which summarizes and recalls information about the file attributes from Chapter 4, Managing Your Worktree).

The second and older setting, core.autocrlf, can be used to turn on the automatic conversion of line endings to CRLF. Setting it to true converts the LF line endings in the repository into CRLF when you check out files, and vice versa when you stage them; this is the setting you would probably want on a Windows machine. (This is almost the same as setting the text attribute to auto on all the files and core.eol to crlf.) You can tell Git to convert CRLF to LF on a commit but not the other way around by setting core.autocrlf to input instead; this is the setting to use if you are on a Linux or Mac system. To turn off this functionality, recording the line-endings in the repository as they are set this configuration value to false.

This handles one part of the whitespace issues: line-ending variance, and one vector of introducing them: editing files. Git also comes with the way to detect and fix some of other whitespace issues. It can look for a set of common whitespace problems to notice. The core.whitespace configuration setting can be used to activate them (for those disabled by default), or turn them off (for those enabled by default). The three that are turned on by default are:

  • blank-at-eol: This looks for trailing spaces at the end of a line
  • blank-at-eof: This notices blank lines at the end of a file
  • space-before-tab: This looks for spaces immediately before the tabs at the initial (beginning) indent part of the line

The trailing-space value in core.whitespace is a shorthand to cover both blank-at-eol and blank-at-eof. The three that are disabled by default but can be turned on are:

  • indent-with-non-tab: This treats the line that is indented with space characters instead of the equivalent tabs as an error (where equivalence is controlled by the tabwidth option); this option enforces indenting with Tab characters.
  • tab-in-indent: This watches for tabs in the initial indentation portion of the line (here, tabwidth is used to fix such whitespace errors); this option enforces indenting with space characters.
  • cr-at-eol: This tells Git that carriage returns at the end of the lines are OK (allowing CRLF endings in the repository).

You can tell Git which of these you want enabled or disabled by setting core.whitespace to the comma separated list of values. To disable an option, prepend it with the "-" prefix in front of the value. For example, if you want all but cr-at-eol and tab-in-indent to be set, and also while setting the tab space value to 4, you can use:

$ git config --local core.whitespace 
    trailing-space,space-before-tab,indent-with-non-tab,tabwidth=4

You can also set these options on a per-file basis with the whitespace attribute. For example, you can use it to turn off checking for whitespace problems in test cases to handle whitespace issues, or ensure that the Python 2 code indents with spaces:

*.py whitespace=tab-in-indent

Git will detect these issues when you run a git diff command and inform about them using the color.diff.whitespace color, so you can notice them and possibly fix them before you create a new commit. While applying patches with git apply, you can ask Git to either warn about the whitespace issues with git apply --whitespace=warn, error out with --whitespace=error, or you can have Git try to automatically fix the issue with --whitespace=fix. The same applies to the git rebase command as well.

Server-side configuration

There are a few configuration options available for the server-side of Git. They would be described in more detail in Chapter 11, Git Administration; here you will find a short summary of some of the more interesting parameters.

You can make the Git server check for object consistency, namely, that every object received during a push matches its SHA-1 identifier and that points to a valid object with a receive.fsckObjects Boolean-valued configuration variable. It is turned off by default because git fsck is a fairly expensive operation, and it might slow down operation, especially on large pushes (which are common in large repositories). This is a check against faulty or malicious clients.

If you rewrite commits that you have already pushed to a server (which is bad practice, as explained in Chapter 8, Keeping History Clean) and try to push again, you'll be denied. The client might, however, force-update the remote branch with the --force flag to the git push command. However, the server can be told to refuse force-pushes by setting receive.denyNonFastForward to true.

The receive.denyDeletes setting blocks one of the workarounds to the denyNonFastForward policy, namely, deleting and recreating a branch. This forbids the deletion of branches and tags; you must remove refs from the server manually.

All of these features could also be implemented via the server-side receive-like hooks; this will be covered in the Installing a Git hook section, and also to some extent in Chapter 11, Git Administration.

Per-file configuration with gitattributes

Some of the customizations can also be specified for a path (perhaps via glob) so that Git applies these settings only for a subset of files or for a subdirectory. These path-specific settings are called gitattributes.

The order of precedence of applying this type of settings starts with the per-repository local (per-user) per-path settings in the $GIT_DIR/info/attributes file. Then, the .gitattributes files are consulted, starting with the one in the same directory as the path in question, going up through the .gitattributes files in the parent directories, up to the top level of the worktree (the root directory of a project). Finally, the global per-user attributes file (specified by core.attributesFile, or at ~/.config/git/attributes if this is not set) and the system-wide file (in /etc/gitattributes in the default installation) are considered.

Available Git attributes are described in detail in Chapter 4, Managing Your Worktree. Using attributes, you can, among others, do things such as specify the separate merge strategies via merge drivers for the specific kind of files (for example, ChangeLog), tell Git how to diff non-text files, or have Git filter content during checkout (on writing to the working area, that is, to the filesystem) and checkin (on staging contents and committing changes to the repository, that is, creating objects in the repository database).

Tip

Syntax of the Git attributes file

A gitattributes file is a simple text file that sets up the local configuration on a per-path basis. Blank lines, or lines starting with the hash character (#) are ignored; thus, a line starting with # serves as a comment, while blank lines can serve as separators for readability. To specify a set of attributes for a path, put a pattern followed by an attributes list, separated by a horizontal whitespace:

pattern  attribute1 attribute2

When more than one pattern matches the path, a later line overrides an earlier line, just like for the .gitignore files (you can also think that the Git attributes files are read from the least specific system-wide file to the most specific local repository file).

Git uses a backslash () as an escape character for patterns. Thus, for patterns that begin with a hash, you need to put a backslash in front of the first hash (that is written as #). Because the attributes information is separated by whitespaces, trailing spaces in the pattern are ignored and inner spaces are treated as end of pattern, unless they are quoted with a backslash (that is, written as " ").

If the pattern does not contain a slash (/), which is a directory separator, Git will treat the pattern as a shell glob pattern and will check for a match against the pathname relative to the location of the .gitattributes file (or top level for other attribute files). Thus, for example, the *.c patterns match the C files anywhere down from the place the .gitattributes file resides. A leading slash matches the beginning of the pathname. For example, /*.c matches bisect.c but not builtin/bisect--helper.c., while *.c pattern would match both.

If the pattern includes at least one slash, Git will treat it as a shell glob suitable for consumption by the fnmatch(3) function call with the FNM_PATHNAME flag. This means that the wildcards in the pattern will not match the directory separator, that is, the slash (/) in the pathname; the match is anchored to beginning of the path. For example, the include/*.h pattern matches include/version.h but not include/linux/asm.h or libxdiff/includes/xdiff.h. Shell glob wildcards are: * matching any string (including empty), ? matching any single character, and the […] expression matching the character class (inside brackets, asterisks and question marks lose their special meaning); note that unlike in regular expressions, the complementation/negation of character class is done with ! and not ^. For example to match anything but a number one can use [!0-9] shell pattern, which is equivalent to [^0-9] regexp.

Two consecutive asterisks (**) in patterns may have a special meaning, but only between two slashes (/**/), or between a slash and at the beginning or the end of pattern. Such a wildcard matches zero or more path components. Thus, a leading ** followed by a slash means match in all directories, while trailing /** matches every file or directory inside the specified directory.

Each attribute can be in one of the four states for a given path. First, it can be set (the attribute has special value of true); this is specified by simply listing the name of the attribute in the attribute list, for example, text. Second, it can be unset (the attribute has a special value of false); this is specified by listing the name of the attribute prefixed with minus, for example,-text. Third, it can be set to a specific value; this is specified by listing the name of the attribute followed by an equal sign and its value, for example, -text=auto (note that there cannot be any whitespace around the equal sign as opposed to the configuration file syntax). If no pattern matches the path, and nothing says if the path has or does not have attributes, the attribute is said to be unspecified (you can override a setting for the attribute, forcing it to be explicitly unspecified with !text).

If you find yourself using the same set of attributes over and over for many different patterns, you should consider defining a macro attribute. It can be defined in the local, or global, or system-wide attributes file, but only in the top level .gitattributes file. The macro is defined using [attr]<macro> in place of the file pattern; the attributes list defines the expansion of the macro. For example, the built-in binary macro attribute is defined as if using:

[attr]binary -diff -merge -text
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset