Git installation and configuration

Installation

For iX distributions, Git should be in the standard repository.

The git-all package provides a complete Git working environment. Install it with:

$ sudo apt install git-all

To install only Git the git package suffices:

$ sudo apt install git

The bash autocompletion makes Git easier to use on the command line. The according package is called bash-completion. Install it with:

$ sudo apt install bash-completion

There are several different ways to install Git on a Mac. Probably the easiest way to do is to install the Xcode Command Line Tools. For this you only have to call up git in the terminal for the first time:

$ git --version

git-completion you can install with Homebrew:

Then you have to add the following line to the file ~/.bash_profile:

[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"

Go to https://git-scm.com/download/win and start the download automatically. Further information can be found at https://gitforwindows.org/.

Configuration

The author of every change needs to be transparent. Specify your name and email address as follows:

$ git config --global user.name "NAME"

defines the name NAME associated with your commit transactions.

$ git config --global user.email "EMAIL-ADDRESS"

defines the email address EMAIL-ADDRESS that will be linked to your commit transactions.

For better readability, activate the coloring of the command line output:

$ git config --global color.ui auto

The ~/.gitconfig file

For example, the following file can be created with the commands given above:

[user]
    name = veit
    email = veit@cusy.io

[color]
    ui = auto

However, aliases can also be specified in the ~/.gitconfig file:

[alias]
    st = status
    ci = commit
    br = branch
    co = checkout
    df = diff
    dfs = diff --staged

The editor can also be specified, for example with:

[core]
    editor = vim

or for Visual Studio Code:

[core]
    editor = code --wait

Note

On macOS, you must first start Visual Studio Code, then open the command palette with +-p and finally execute the Install 'code' command in PATH.

The highlighting of space errors in git diff can also be configured:

[core]
    # Highlight whitespace errors in git diff:
    whitespace = tabwidth=4,tab-in-indent,cr-at-eol,trailing-space

Note

In addition to ~/.gitconfig, since version 1.17.12 Git also looks in ~/.config/git/config for a global configuration file.

Under Linux, ~/.config can sometimes be a different path set by the environment variable XDG_CONFIG_HOME. This behaviour is part of the X Desktop Group (XDG) specification. You can get the other path with:

$ echo $XDG_CONFIG_HOME

See also

Since you can set options at multiple levels, you may want to keep track of where Git reads a particular value from. With git config --list [1] you can list all the overridden options and values. You can combine this with --show-scope [2] to see where Git is getting the value from:

$ git config --list --show-scope
system  credential.helper=osxkeychain
global  user.name=veit
global  user.email=veit@cusy.io

You can also use --show-origin [3] to list the names of the configuration files:

$ git config --list --show-origin
file:/opt/homebrew/etc/gitconfig        credential.helper=osxkeychain
file:/Users/veit/.config/git/config     user.name=veit
file:/Users/veit/.config/git/config     user.email=veit@cusy.io

Alternative configuration file

You can use other configuration files for certain working directories, for example to distinguish between private and professional projects. You can use a local configuration in your repository or conditional includes at the end of your global configuration:


[includeIf "gitdir:~/private"]
path = ~/.config/git/config-private

This construct ensures that Git includes additional configurations or overwrites existing ones when you work in ~/private.

Now create the file ~/.config/git/config-private and define your alternative configuration there, for example:

[user]
    email = kontakt@veit-schiele.de
[core]
    sshCommand = ssh -i ~/.ssh/private_id_rsa

See also

Manage login data

Since Git version 1.7.9, the access data to git repositories can be managed with gitcredentials. To use this, you can, for example, specify the following:

$ git config --global credential.helper Cache

This will keep your password in the cache for 15 minutes. If necessary, the timeout can be increased, for example with:

$ git config --global credential.helper 'cache --timeout=3600'

With macOS you can use osxkeychain to store the login information. osxkeychain requires Git version 1.7.10 or newer and can be installed in the same directory as Git with:

$ git credential-osxkeychain
git: 'credential-osxkeychain' is not a git command. See 'git --help'.
$ curl -s -O http://github-media-downloads.s3.amazonaws.com/osx/git-credential-osxkeychain
$ chmod u+x git-credential-osxkeychain
$ sudo mv git-credential-osxkeychain /usr/bin/
Password:
git config --global credential.helper osxkeychain

This enters the following in the ~/.gitconfig file:

[credential]
    helper = osxkeychain

For Windows, Git Credential Manager (GCM) is available. It is integrated in Git for Windows and is installed by default. However, there is also a standalone Installer in Releases.

It is configured with

$ git credential-manager configure
Configuring component 'Git Credential Manager'...
Configuring component 'Azure Repos provider'...

This will add the [credential] section to your ~.gitconfig file:

[credential]
    helper =
    helper = C:/Program\\ Files/Git/mingw64/bin/git-credential-manager.exe

Now, when cloning a repository, a Git Credential Manager window opens and asks you to enter your credentials.

In addition, the ~/.gitconfig file is supplemented, for example by the following two lines:

[credential "https://ce.cusy.io"]
    provider = generic

Note

You can find a comprehensive example of a ~/.gitconfig file in my dotfiles repository: .gitconfig.

The .gitignore file

In the .gitignore file you can exclude files from version management. A typical .gitignore file can look like this:

/logs/*
!logs/.gitkeep
/tmp
*.swp

In doing so, Git uses Globbing patterns, among others:

Pattern

Example

Description

**/logs

logs/instance.log, logs/instance/error.log, prod/logs/instance.log

You can put two asterisks to prefix directories anywhere.

**/logs/instance.log

logs/instance.log, prod/logs/instance.log but not logs/prod/instance.log

You can put two asterisks to prefix files with their name in a parent directory.

*.log

instance.log, error.log, logs/instance.log

An asterisk is a placeholder for null or more characters.

/logs
!/logs/.gitkeep

/logs/instance.log, /logs/error.log, but not /logs/.gitkeep or /instance.log

An exclamation mark in front of a pattern ignores it. If a file matches a pattern, but also a negating one that is defined later, it is not ignored.

/instance.log

/instance.log, but not logs/instance.log

With a preceding slash, the pattern only matches files in the root directory of the repository.

instance.log

instance.log, logs/instance.log

Usualy the pattern match files in any directory.

instance?.log

instance0.log, instance1.log, but not instance.log or instance10.log

A question mark fits exactly on a charater.

instance[0-9].log

instance0.log, instance1.log, but not instance.log or instance10.log

Square brackets can be used to find a single character from a specific range.

instance[01].log

instance0.log, instance1.log, but not instance2.log or instance01.log

Square brackets match a single character from a given set.

instance[!01].log

instance2.log, but not instance0.log, instance1.log or instance01.log

An exclamation mark can be used to find any character from a specified set.

logs

logs logs/instance.log prod/logs/instance.log

If no slash appended, the pattern fix both files and the contents of directories witch this name.

logs/

logs/instance.log, logs/prod/instance.log, prod/logs/instance.log

Appending a slash indicates that the pattern is a directory. The entire contents of any directory in the repository that matches the name – including all its files and subdirectories – are ignored.

var/**/instance.log

var/instance.log, var/logs/instance.log, but not var/logs/instance/error.log

Two Asterisks match null or more directories.

logs/instance*/error.log

logs/instance/error.log, logs/instance1/error.log

Wildcards can also be used in directory names.

logs/instance.log

logs/instance.log, but not var/logs/instance.log or instance.log

Pattern, that specify a particular file in a directory are relative to the root of the repository.

Git-commit empty folder

In the example above you can see that with /logs/* no content of the logs directory should be versioned with Git, but an exception is defined in the following line: !logs/.gitkeep allows the file .gitkeep to be managed with Git. The logs directory is then also transferred to the Git repository. This construction is necessary because empty folders cannot be managed with Git.

Warning

However, this technique has several disadvantages:

  • Both .gitignore and log/.gitkeep must be edited.

  • When renaming the directory, it is easy to forget to change the .gitignore file as well.

  • .gitkeep is a completely normal file for Git; however, the name suggests that the file would be treated specially by Git.

A better option is to create a .gitignore file with the following content in an empty directory:

# ignore everything except .gitignore
*
!.gitignore

excludesfile

However, you can also exclude files centrally for all Git repositories. For this purpose, you can set excludesfile in the ~/.gitconfig file:

[core]

    # Use custom `.gitignore`
    excludesfile = ~/.gitignore
    

Note

You can find helpful templates in my dotfiles repository or on the gitignore.io website.

Ignoring a file from the repository

If you want to ignore a file that has already been added to the repository in the past, you need to delete the file from your repository and then add a .gitignore rule for it. Using the --cached option on git rm means that the file will be deleted from the repository but will remain in your working directory as an ignored file.

$ echo *.log >> .gitignore
$ git rm --cached *.log
rm 'instance.log'
$ git commit -m "Remove log files"

Note

You can omit the --cached option if you want to remove the file from both the repository and your local file system.

Commit an ignored file

It is possible to force the commit of an ignored file to the repository with the -f (or --force) option on git add:

$ cat data/.gitignore
*
$ git add -f data/iris.csv
$ git commit -m "Force add iris.csv"

You might consider this if you have a general pattern (like *) defined, but want to commit a specific file. However, a better solution is usually to define an exception to the general rule:

$ echo '!iris.csv' >> data/.gitignore
$ cat data/.gitignore
*
!iris.csv
$ git add data/iris.csv
$ git commit -m "Add iris.csv"

This approach should be more obvious and less confusing for your team.

Troubleshooting .gitignore files

For complicated .gitignore patterns, or patterns that are spread across multiple .gitignore files, it can be difficult to figure out why a particular file is being ignored.

With git status --ignored=matching [4], an Ignored Files section is added to the output, showing all ignored files and directories:

$ git status --ignored=matching
On branch main
Ignored Files:
  (use "git add -f <file>...", to pre-mark the changes for committing
    .DS_Store
    docs/.DS_Store
    docs/_build/doctrees/
    docs/_build/html/
    docs/clean-prep/.ipynb_checkpoints/

    nothing to commit, working tree clean

You can use the git check-ignore command with the -v (or --verbose) option to determine which pattern is causing a particular file to be ignored:

$ git check-ignore -v data/iris.csv
data/.gitignore:2:!iris.csv  data/iris.csv

The output shows FILE_CONTAINING_THE_PATTERN:LINE_NUMBER_OF_THE_PATTERN:PATTERN FILE_NAME

You can pass multiple filenames to git check-ignore if you like, and the names themselves don’t even have to match the files that exist in your repository.

You can get a complete list of all ignored files with git ls-files --ignored --exclude-standard --others [5]. With --exclude-standard the standard ignored files are read and with --others the non-versioned files are displayed instead of the versioned ones:

$ git ls-files --ignored --exclude-standard --others
.DS_Store
_build/doctrees/clean-prep/bulwark.doctree
_build/doctrees/clean-prep/dask-pipeline.doctree
_build/doctrees/clean-prep/deduplicate.doctree

Occasionally you may want to bypass the global ~/.gitignore file to see which files Git always ignores, regardless of your configuration. You can do this by switching to another exclude option, --exclude-per-directory, which uses only the repository’s .gitignore files:

$ git ls-files --ignored --exclude-per-directory=.gitignore --others
docs/_build/doctrees/clean-prep/bulwark.doctree
docs/_build/doctrees/clean-prep/dask-pipeline.doctree
docs/_build/doctrees/clean-prep/deduplicate.doctree

Note that the .DS_Store file is no longer listed as ignored.

If you replace --others with --cached, git ls-files will list files that would be ignored unless they have already been committed:

$ git ls-files --ignored --exclude-per-directory=.gitignore --cached
data/iris.csv

You may have such files because someone added them to a .gitignore file before the relevant patterns, or because someone added them with git add --force. Either way, if you no longer want to manage the file with Git, you can remove it from Git management with the following one-liner, but don’t delete it:

$ git ls-files --ignored --exclude-per-directory=.gitignore --cached | xargs -r git rm --cached
rm 'data/iris.csv'