Utiliser Git avec plusieurs identités
Tags: git
Published: 2021-08-25 12:33:27

Dès le premier commit, Git ne manque pas de nous rappeler de renseigner un nom et un email.

Pour configurer une identité dans Git, tout se passe dans le fichier de configuration global. Ce fichier de configuration `.gitconfig` se trouve généralement dans votre dossier `$HOME`.

On peut le configurer en ligne de commande :
```
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
```

Ou encore avec notre éditeur préféré. Le principal est que Git puisse trouver et lire ce fichier.

On peut afficher ces paramètres de plusieurs façon. En lisant directement tous les fichier `.gitconfig` ou en utilisant la ligne de commande.
```
$ cat ~/.gitconfig
[user]
	name = John Doe
	email = johndoe@example.com

$ git config --list
user.name=John Doe
user.email=johndoe@example.com
```

Et si vous y rajoutez l'option `--show-origin`, vous saurez en plus de quel fichier provient la configuration:
```
$ git config --list --show-origin
file:/Users/vixsty/.gitconfig   user.name=John Doe
file:/Users/vixsty/.gitconfig   user.email=johndoe@example.com
```

Je préfère cette solution à un simple `cat ~/.gitconfig` car elle a l'avantage de montrer la configuration résolue par Git en agrégeant les configurations system, global et local.

Ainsi, chaque commit Git se fera en utilisant ces paramètres. Mais voilà, ils sont définis une fois pour tous les projets, indépendamment du serveur Git qui peut changer, d'un projet différent ou tout autre facteurs pouvant vous conduire à utiliser une autre adresse email ou un autre nom.


## Plusieurs Identités

Certaines interfaces graphiques de Git viennent instrumenter cette fonctionnalité et permettent de changer entre différentes identités lors des commits. Mais j'utilise autant les applications que la ligne de commande. Je préfère une solution qui fonctionne à un plus bas niveau et de façon transparente.

En regardant de plus près la documentation officielle sur [git-scm.com](https://git-scm.com/docs/git-config#_includes). On y trouve l'instruction `includeIf` :

> The include and includeIf sections allow you to include config directives from another source. These sections behave identically to each other with the exception that includeIf sections may be ignored if their condition does not evaluate to true; see "Conditional includes" below.

> You can include a config file from another by setting the special include.path (or includeIf.*.path) variable to the name of the file to be included. The variable takes a pathname as its value, and is subject to tilde expansion. These variables can be given multiple times.

> The contents of the included file are inserted immediately, as if they had been found at the location of the include directive. If the value of the variable is a relative path, the path is considered to be relative to the configuration file in which the include directive was found. See below for examples.

Cette instruction m'offre la possibilité de charger une configuration alternative selon où ce trouve mon repository Git.

## Organisation par dossier

J'ai surtout une identité reliée à mon travail et mon identité personnelle. Et ça tombe plutôt bien, j'ai déjà une organisation de mes dossiers pour ne pas mélanger boulot et perso.

J'ai donc deux dossier
* `~/Documents/dev/work`
* `~/Documents/dev/perso`

mais l'on peut imaginer la même chose pour séparer GitHub, gitlab.com et un GitLab interne.
* `~/Documents/dev/github.com`
* `~/Documents/dev/gitlab.com`
* `~/Documents/dev/gitlab.example.com`

## Configuration Git

Je crée un fichier de configuration Git dans mon dossier `work` qui contiendra les paramètres de mon identité professionnelle.
```
# en ligne de commande
$ git config -f ~/Documents/dev/work/.gitconfig user.name "Jack Sparrow"
$ git config -f ~/Documents/dev/work/.gitconfig user.email jack.sparrow@example.com

# ou directement en modifiant le fichier
$ cat >> ~/Documents/dev/work/.gitconfig <<EOL
[user]
        name = Jack Sparrow
        email = jack.sparrow@example.com
EOL
```

Dans ma configuration global, j'ajoute un `includeIf` vers mon dossier `work` qui inclura le fichier de configuration précédemment créé.

```
$ cat >> ~/.gitconfig <<EOL
[includeIf "gitdir:~/Documents/dev/work/"]
	path = ~/Documents/dev/work/.gitconfig
EOL

# et je vérifie que le fichier soit cohérent.
$ git config --list --show-origin
file:/Users/vixsty/.gitconfig   user.name=John Doe
file:/Users/vixsty/.gitconfig   user.email=johndoe@example.com
file:/Users/vixsty/.gitconfig   includeif.gitdir:~/Documents/dev/work/.path=~/Documents/dev/work/.gitconfig
```


## Vérification

Pour vérifier que cette configuration fonctionne, il me suffit d'aller dans un repo Git présent dans mon dossier `~/Documents/dev/work/` et d'afficher la configuration

```
$ mkdir -p ~/Documents/dev/work/multipass-test

$ cd ~/Documents/dev/work/multipass-test

$ git init
Initialized empty Git repository in /Users/vixsty/Documents/dev/work/multipass-test/.git/

$ git config --list --show-origin
file:/Users/vixsty/.gitconfig   user.name=John Doe
file:/Users/vixsty/.gitconfig   user.email=johndoe@example.com
file:/Users/vixsty/.gitconfig   includeif.gitdir:~/Documents/dev/work/.path=~/Documents/dev/work/.gitconfig
file:/Users/vixsty/Documents/dev/work/.gitconfig        user.name=Jack Sparrow
file:/Users/vixsty/Documents/dev/work/.gitconfig        user.email=jack.sparrow@example.com
file:.git/config        core.repositoryformatversion=0
file:.git/config        core.filemode=true
file:.git/config        core.bare=false
file:.git/config        core.logallrefupdates=true
file:.git/config        core.ignorecase=true
file:.git/config        core.precomposeunicode=true
```

On peut voir des valeurs de `user.name` et de `user.email` provenant de `/Users/vixsty/.gitconfig` ainsi que de `/Users/vixsty/Documents/dev/work/.gitconfig` et ces dernières valeur remplacent les premières.

Si je fais la même chose dans mon dossier perso, les valeurs de `work` ne sont pas chargées

```
$ mkdir -p ~/Documents/dev/perso/multipass-test

$ cd ~/Documents/dev/perso/multipass-test

$ git init
Initialized empty Git repository in /Users/vixsty/Documents/dev/perso/multipass-test/.git/

$ git config --list --show-origin
file:/Users/vixsty/.gitconfig   user.name=John Doe
file:/Users/vixsty/.gitconfig   user.email=johndoe@example.com
file:/Users/vixsty/.gitconfig   includeif.gitdir:~/Documents/dev/work/.path=~/Documents/dev/work/.gitconfig
file:.git/config        core.repositoryformatversion=0
file:.git/config        core.filemode=true
file:.git/config        core.bare=false
file:.git/config        core.logallrefupdates=true
file:.git/config        core.ignorecase=true
file:.git/config        core.precomposeunicode=true
```

On peut aller plus loin en vérifiant avec un simple commit dans chaque repository que l'identité correspond à notre configuration.

* professionnelle
```
commit d57dcd1729f4f7bcaab7472fb8a98a33e8d356b3 (HEAD -> master)
Author: Jack Sparrow <jack.sparrow@example.com>
Date:   Fri Jun 12 18:46:58 2020 +0200

    first commit
```
* personnelle
```
commit ce6967bcdb9f0c9f652e5b8756e3857712e258a9 (HEAD -> master)
Author: John Doe <johndoe@example.com>
Date:   Fri Jun 12 18:45:12 2020 +0200

    first commit
```


## Conclusion

Le fichier `.gitconfig` peut contenir tous les paramètres reconnus par Git. Avec cette méthode, ceci me permet notamment d'activer la signature GPG en associant mes identités Git avec les identités GPG leur correspondant.

Par exemple :
```
file:/Users/vixsty/.gitconfig   user.signingkey=3E5D********************************8F3A
file:/Users/vixsty/.gitconfig   commit.gpgsign=true
file:/Users/vixsty/Documents/dev/work/.gitconfig        user.signingkey=C16C********************************CFBB
file:/Users/vixsty/Documents/dev/work/.gitconfig        commit.gpgsign=true
```

Si vous cherchez comment gérer vos clés GPG pour signer vos commits, je vous recommande l'utilisation de [keybase.io](https://keybase.io/) et je vous invite à lire cette article [Gérer ses clefs GPG sous MacOS avec Keybase.io](https://blog.stack-labs.com/code/gerer_clefs_gpg_github_keybaseio/) qui traite très bien le sujet.