Héberger un serveur Git avec Gitosis (Linux / OSX)
Par Christophe Porteneuve • Publié le 18 juillet 2010 • 14 min

Il y a plusieurs raisons pour vouloir disposer d’un hébergement Git. Les trois principales sont :

  • disposer d’un backup distant de son travail,
  • mettre son travail à la disposition d’autrui,
  • collaborer avec d’autres sur un projet.

Hébergement public ou privé ?

Lorsque le projet est libre, ou qu’en tout cas son contenu est public, le plus simple consiste à exploiter un bon service d’hébergement Git qui serait gratuit pour les dépôts publics. Dans ce domaine, l’acteur désormais incontournable (et particulièrement bien foutu) est le célèbre GitHub.

En revanche, pour les dépôts privés, soit on reste sur GitHub, mais dès lors, c’est payant (pas cher ceci dit : à partir de $9/mois, ce qui permet de bénéficier de l’infrastructure massive du service, notamment en termes de fiabilité de stockage). Soit on crée son propre hébergement Git, ce qui a notamment l’intérêt éventuel de le rendre interne à sa société (et donc, potentiellement, très rapide d’accès sur un réseau local performant).

Ce billet vise à explorer en détail l’option technique favorite pour héberger son propre serveur Git : Gitosis.

Quand on connaît la procédure, récupérer, installer et configurer Git, qu’on soit sur OSX ou Linux, est l’affaire de 10 à 15 minutes. En lisant ce billet et les explications, ce sera naturellement un peu plus long. Mais ça reste rapide et facile !

Principes de fonctionnement de Gitosis

Comme GitHub, Gitosis base sa gestion d’accès sur le protocole SSH et des clés asymétriques.

Chaque utilisateur reconnu par le système est identifié au moyen d’une paire de clés asymétriques dont le serveur connaît la partie publique : côté client, il faut donc disposer de la clé privée correspondante dans son « portefeuille de clés » SSH. Un même utilisateur logique dans Gitosis peut être associé à un nombre quelconque de clés (par exemple une depuis sa machine au boulot, une depuis son laptop, une par compte sur un serveur où du code est déployé…).

C’est à mon sens une excellente façon de procéder. En effet, révoquer l’accès peut se faire de façon très granulaire si on utilise une clé par couple Utilisateur + Machine : il devient alors possible de révoquer les accès depuis une machine quelconque, ou pour un utilisateur à travers toutes les machines, ou pour un cas Utilisateur + Machine précis.

Un autre aspect de Gitosis que j’aime particulièrement est que toute sa configuration est dans un… dépôt Git, ce qui fait qu’on la met à jour avec un git push. C’est délicieusement récursif, j’adore !

Installer le serveur

Allez, c’est parti, on va se coller un p’tit Gitosis perso.

Récupérer les dépendances éventuelles

Déjà, il vous faut Git. Mais si vous lisez ce blog, je suppose que vous l’avez déjà…

Il faut aussi Python et son module setup-tools. Sur OSX on a déjà l’équivalent, mais sur un Linux il faudra installer le paquet correspondant. Par exemple, sur Debian/Ubuntu :

apt-get install python-setuptools

À partir de là, on récupère Gitosis depuis son dépôt et procéder à l’installation :

cd /tmp
git clone git://eagain.net/gitosis.git
cd gitosis
sudo python setup.py install

Cette installation nous fournit une série de binaires (programmes) pour Gitosis, au premier rang desquels gitosis-serve, qui va servir de « shell » à l’utilisateur système git que nous allons créer. Ce binaire fournit toutes les fonctions du serveur Git, ainsi que le mécanisme d’authentification, de gestion des droits, etc.

Créer et configurer l’utilisateur « git »

Vous avez sans doute déjà remarqué que les URLs « lecture-écriture » d’un dépôt distant Git sont généralement de la forme git@le-serveur.tld:le-depot.git (ou plus rarement ssh://git@le-serveur.tld:le-depot.git, ce qui revient au même). La convention veut en effet qu’on configure un utilisateur système git sur le serveur, seul moyen d’accéder au serveur Git.

Cet utilisateur n’a pas de mot de passe, mais au lieu de fournir un shell classique (ce qui, vu le manque de mot de passe frontal, serait assez dangereux…), il exécute uniquement votre serveur Git, lequel se charge alors de l’authentification.

En revanche, le home directory (répertoire personnel) de ce compte constituera le dossier racine de vos dépôts Git sur le serveur. Du coup, plutôt que d’utiliser la convention usuelle (/home/login sur Linux, /Users/login sur OSX), on aura tendance à préciser un dossier plus conforme à ce type d’usage, typiquement /var/lib/git. Vous pouvez en fait mettre ce que vous voulez, mais je précise le raisonnement ici histoire que vous ne soyez pas surpris dans les lignes de commande qui vont suivre.

Il nous faut donc configurer cet utilisateur. C’est là que la procédure diverge entre les Linux (qui ont tous les binaires système adduser et/ou useradd) et OSX (dont la gestion des utilisateurs repose directement sur son mécanisme d’annuaire, c’est-à-dire sa couche LDAP).

Commençons par la version Linux. Selon la commande que vous voulez employer, les options ne sont pas exactement les mêmes. Voici les deux variantes :

sudo adduser --system --shell /bin/sh --gecos 'git version control' \
--group --disabled-password --home /var/lib/git git

ou

sudo useradd --system --shell /bin/sh --comment 'git version control' \
--user-group --home-dir /var/lib/git git

Passons rapidement en revue la signification de ces arguments :

  • --system précise qu’il s’agit d’un utilisateur système : entre autres choses, son home directory ne sera pas initialisé avec les squelettes (fichiers initiaux d’un compte, présents en général dans /etc/skel).
  • --shell précise le shell par défaut de l’utilisateur. Vu que l’accès SSH sur ce compte ne l’exploitera pas, ce n’est pas une source de danger, en revanche il peut être pratique de disposer d’un shell pour une authentification locale (avec su ou sudo -i) histoire de triturer le compte sous son identité directe en cas de besoin majeur.
  • --gecos ou --comment fournissent le descriptif utilisateur qui sera associé dans l’annuaire des comptes locaux.
  • --group ou --user-group spécifie que le seul groupe qui doit être associé à l’utilisateur est son groupe propre : il ne fera partie d’aucun des groupes utilisateurs par défaut qui pourraient être configurés sur le système, et n’aura donc pas d’accès indû à certains programmes ou emplacements du système de fichiers.
  • --disabled-password indique que le compte n’a pas de mot de passe associé (vu qu’il n’est pas conçu pour faire l’objet d’une authentification). Dans useradd, qui n’est pas une commande interactive, le simple fait de ne pas passer de mot de passe dans les arguments a le même effet.
  • --home ou --home-dir fournit le home directory du compte.
  • On termine avec le nom du compte : git

La commande crée automatiquement le home directory et lui affecte les bons propriétaire et groupe. Ce ne sera pas le cas sous OSX, où on se contente de manipuler l’annuaire, ce qui n’impacte jamais le reste du système de fichiers. Voyons justement comment procéder :

sudo dscl . create groups/git gid 405
sudo dscl . create users/git uid 405
sudo dscl . create users/git NFSHomeDirectory /var/lib/git
sudo dscl . create users/git gid 405
sudo dscl . create users/git UserShell /bin/bash
sudo dscl . create users/git Password '*'
sudo mkdir /var/lib/git
sudo chown git:git /var/lib/git

Le binaire dscl (Directory Services Command Line) est l’interface en ligne de commande fournie par OSX pour manipuler son annuaire interne. Il nous faut d’abord créer le groupe, puis l’utilisateur à proprement parler. Attention, vous ne pouvez pas regrouper les dscl . create dans une seule et même commande, comme on pourrait être tenté de le déduire de la documentation pour cette commande : il est impératif que chaque couple clé+valeur ait sa propre commande.

Notez comme on termine en créant explicitement le home directory et en calant son propriétaire et son groupe.

Sur un OSX classique, les identifiants groupe et utilisateur 405 sont normalement libres. Vous pouvez toutefois le vérifier en amont avec les commandes suivantes, et si besoin prendre d’autres IDs qui, eux, seraient libres (ils n’ont pas besoin d’être identiques, mais c’est plus conventionnel) :

dscl . list groups gid
dscl . list users uid

Un petit détour au pays des clés…

Il nous faut à présenter définir comment cet utilisateur va se comporter en cas de connexion SSH utilisant son compte. Le boulot correspondant, ainsi que l’initialisation finale de Gitosis, sont fait par la même commande. Celle-ci a toutefois besoin de votre clé publique SSH afin de vous autoriser, notamment, à manipuler le serveur et le compte Git à votre guise.

Peut-être n’avez-vous pas de clé pour le moment ? Vérifiez avec la commande suivante :

ls -la ~/.ssh

Si vous voyez que le répertoire n’existe pas, ou que vous n’y voyez pas de paire de fichiers id_dsa/id_dsa.pub (ou id_rsa/id_rsa.pub), vous n’avez pas encore de clé. Et d’ailleurs, même si vous en avez déjà une, vous voulez peut-être en créer une seconde, spécifique pour votre exploitation de Git.

Pour générer une paire de clés asymétriques, on utilise la commande ssh-keygen. Je vous conseille d’exploiter l’algorithme DSA plutôt que le RSA, utilisé par défaut, qui est un peu moins sécurisé. Le chemin proposé par défaut pour votre clé sera ~/.ssh/id_dsa ; si vous avez déjà une telle clé mais en voulez une supplémentaire, pensez à changer le nom vers quelque chose de plus spécifique (par exemple ~/.ssh/id_dsa_git) pour ne pas écraser la clé existante.

Voici ce que ça donne sur mon OSX Snow Leopard :

$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/Users/tdd/.ssh/id_dsa): /Users/tdd/.ssh/id_dsa_git
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/tdd/.ssh/id_dsa_git.
Your public key has been saved in /Users/tdd/.ssh/id_dsa_git.pub.
The key fingerprint is:
9d:ab:3c:56:ef:ad:06:e1:da:13:d5:1f:74:4e:a9:46 tdd@CodeMagic.local
The key's randomart image is:
+--[ DSA 1024]----+
| .|
| E oo|
| ..oo.|
| ....o...|
| S.oo. ..|
| =. .|
| +.+ |
| .+.o o. |
| .o. +o.. |
+-----------------+

turellement, vous n’aurez pas la même empreinte (fingerprint) et donc pas le même randomart ; c’est normal, le contraire serait d’ailleurs inquiétant.)

Utilisez ce que vous voulez comme passphrase pour la clé, mais évitez un truc bateau, encore plus du vide. Inutile de rendre votre clé utilisable par tout le monde…

Une fois la génération terminée, vous devriez voir deux nouvelles clés, la privée et la publique (celle dont le nom finit en .pub) dans votre dossier SSH :

$ ls -l ~/.ssh
total 48
-rw------- 1 tdd tdd 90 déc 11 2009 config
-rw------- 1 tdd tdd 736 aoû 23 2007 id_dsa
-rw------- 1 tdd tdd 609 aoû 23 2007 id_dsa.pub
-rw------- 1 tdd tdd 736 jul 17 09:17 id_dsa_git
-rw-r--r-- 1 tdd tdd 609 jul 17 09:17 id_dsa_git.pub
-rw------- 1 tdd tdd 668 avr 6 22:53 id_dsa_sips
-rw-r--r-- 1 tdd tdd 23286 jul 16 12:20 known_hosts

Installer Gitosis à proprement parler

OK, cette parenthèse refermée, nous pouvons lancer la commande d’initialisation de Gitosis :

sudo -H -u git gitosis-init < ~/.ssh/id_dsa_git.pub

(Si vous voulez utiliser une autre clé publique, ajustez simplement le chemin indiqué)

L’option -H s’assure que la commande gitosis-init (installée tout à l’heure par notre python setup.py install) s’exécutera depuis le home directory de l’utilisateur indiqué par -u git, et non depuis le répertoire courant.

La commande gitosis-init requiert sur son entrée standard la clé publique de l’utilisateur susceptible de triturer le compte manuellement.

Sur OSX, il va aussi nous falloir contourner un problème de PATH (liste des endroits où le système cherche les binaires qu’on lui demande d’exécuter), en ajoutant la commande suivante :

sudo su git; echo "export PATH=$(git --exec-path):$PATH" > ~/.bashrc; exit

De cette façon, on est certain que l’utilisateur git, pour exécuter ses binaires, aura bien tous les bons chemins sous la main. Encore une fois, pas besoin de ça sous Linux : juste OSX.

Ultime étape de l’installation : activer le post-update hook de Gitosis afin que toute mise à jour de configuration soit bien prise en compte :

sudo chmod u+x /var/lib/git/repositories/gitosis-admin.git/hooks/post-update

Les serveurs Git proposent en effet tous un mécanisme de hooks (points d’ancrage) qui permet de faire exécuter au serveur une ou plusieurs tâches, de façon automatique, à divers moments. Le post-update hook, équivalent du commit hook de Subversion, est le plus employé, puisqu’il se déclenche après toute mise à jour d’un dépôt sur le serveur, et permet donc, par exemple, de déclencher la suite de tests dans le cadre d’une démarche d’intégration continue, ou encore de signaler le push sur divers canaux (e-mail, messagerie instantanée, IRC…).

Dans le cadre de Gitosis, le post-update hook du dépôt de configuration (gitosis-admin) s’emploie à refléter les changements du dépôt dans la configuration concrète du serveur (notamment en ce qui concerne les utilisateurs, leurs clés, et les dépôts reconnus).

Ce qui nous fait une excellente transition vers la section suivante…

Configurer Gitosis… avec Git

Eh oui, Gitosis se configure en accédant au dépôt pré-installé gitosis-admin. On le récupère comme suit :

# cd où ça vous semble bien, puis…
git clone git@localhost:gitosis-admin.git
cd gitosis-admin

(Si vous avez installé Gitosis sur une autre machine que celle où vous faites le git clone, remplacez localhost par le nom ou l’IP du serveur).

Le dépôt est constitué principalement de deux choses :

  • Un fichier de configuration gitosis.conf, qui définit les dépôts, comptes utilisateurs, groupes d’utilisateurs et droits d’accès aux dépôts.
  • Un répertoire keydir/ contenant, pour chaque nom d’utilisateur présent dans gitosis.conf, un fichier avec le même nom et l’extension .pub, qui liste les clés publiques associées à cet utilisateur.

Définir les utilisateurs

Le fichier de configuration gitosis.conf a une syntaxe très simple, similaire aux bons vieux fichiers .ini. Voici le début de mon gitosis.conf pour la première session des Ateliers Git Attitude :

[gitosis]

[group gitosis-admin]
writable = gitosis-admin
members = tdd

[group git-attitude]
writable = prems
members = tdd xavier olivier hugo maurice nicolas kaelig francois mickael jonathan thierry

Notez qu’on peut aussi fournir un accès en lecture seule dans un groupe, en utilisant la clé readonly au lieu de la clé writable. Toutefois, on passe quand même par le protocole SSH, pas par un accès anonyme de type git:// voire http://. Pour ces types d’accès, le mieux est d’utiliser git-daemon, qui est fourni avec Git lui-même. J’en parlerai un de ces jours…

Il existe de multiples façons de définir les dépôts, groupes, utilisateurs et droits. Ma préférence personnelle va au choix qu’on retrouve ci-dessus :

  • Définir un [group] par « groupe de projets » (le nom du groupe est sans importance mais doit être unique)
  • Y placer le ou les noms des dépôts accessibles en lecture/écriture dans la clé writable
  • Y placer le ou les noms des dépôts accessibles en lecture dans la clé readonly
  • Y placer le ou les noms des utilisateurs concernés dans la clé members

Il existe d’autres manières de procéder, mais quoi qu’on fasse, les noms des utilisateurs sont à votre convenance. En effet, ils ne seront jamais utilisés par l’extérieur (notamment dans les URLs de dépôts distants), ce sont des « noms logiques » internes à Gitosis. La seule contrainte, c’est que le fichier recensant les clés publiques associées à un utilisateur soit nommé keydir/nom-logique-utilisateur.pub.

Ainsi, mon dossier keydir/ a l’aspect suivant :

$ ls -l keydir/
total 40
-rw-r--r-- 1 tdd tdd 610 jul 10 18:28 francois.pub
-rw-r--r-- 1 tdd tdd 618 jul 10 18:30 hugo.pub
-rw-r--r-- 1 tdd tdd 433 jul 10 18:28 jonathan.pub
-rw-r--r-- 1 tdd tdd 609 jul 10 18:28 kaelig.pub
-rw-r--r-- 1 tdd tdd 422 jul 10 18:29 maurice.pub
-rw-r--r-- 1 tdd tdd 604 jul 10 18:27 mickael.pub
-rw-r--r-- 1 tdd tdd 606 jul 10 18:30 nicolas.pub
-rw-r--r-- 1 tdd tdd 603 jul 10 18:29 olivier.pub
-rw-r--r-- 1 tdd tdd 609 jul 7 23:29 tdd.pub
-rw-r--r-- 1 tdd tdd 609 jul 10 18:30 thierry.pub

Chaque fichier recense la ou les clés publiques associées au compte, à raison d’une par ligne. Par exemple, keydir/tdd.pub contient la clé publique de mon compte sur mon laptop (je l’ai découpée ici en plusieurs lignes pour qu’elle soit lisible d’entrée sur le blog, mais encore une fois, en temps normal tout serait sur une seule ligne) :

ssh-dss AAAAB3NzaC1kc3MAAACBALsvVpQOjngftILkQYHMiztcmPq9jHJbmsM
rBiAuxG/6tLhd8n1q8tEP5D8dwcXI6KjsWZxJNfv2rW9f5wz+Pc9Ahxo+Ru9gCv
oFV3a5igMLdaWq/eaNCDn9HtZyBxHf6uXMtPekKroLqTn6lxTKHnve1HcVUgxqr
4cNBiUkEZyjAAAAFQC2ypQSwpy5+XNqHj4lesk39FXsnwAAAIAkqehiHDbd9x0H
cfRIwIGt1hwxUIIzljMfJPsvanI7VgP7e8LveP+ZI8ZE9/swlS9dmYDJlxJ12dI
w93fC7dpi6OWSZy2Bcr1JMNXqR1MSyt2LlcUk7G2hVvN3ThfC0D02Un/lUI/uYH
ciFjVTKttMG92aXy/y1EV/rUwdiW1NaQAAAIBWKFrXAu5hiheDuyL9m1LXPiU9K
ruoQ4AJXBaAKMY45R4rKckJPir4nha8z1lVtXt7MhWbLMendS2pZzkMS5xImxym
bK5i9SQgoXUjawvNiDUvgh0mHUkONkAkS8xl+NAPnUKxSj4l3cszvLhiPlp9m6U
30dMomUHe+M9CIgZrZg== tdd@CodeMagic.local

Si vous utilisez une clé RSA, vous aurez quelque chose d’un peu plus court, et démarrant par ssh-rsa au lieu de ssh-dss, comme c’est par exemple le cas de Mauriz :

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7XwkkW4nDqT1r668SFgTfadtOuI
afNhe7dTRllM76rIfsjEc07nQovQLTU9t24whQr3HS0sEzPJ6Sz+F1SbjtfTCul
iNNinmcizswDJbXcwr6Pe6kDeb+ulB2tnmQ76janpWDpxYFZdYxqFqggHSc+Un+
ubenlENTUOB+sy7PVRn54lfe0Pjlk5HsmCLNNA81VV6O82/77XYTR+7VYMPEmj8
J4GYmpYiuNG5bM7aUnXPxJ73fG0eQXtCsZ9fGCah9eaxubfTQ8UvO+t+2aKc0a0
+unaFEvEndo/17uGIhVdZhIQzop9ygsi/hGQDp8IDLgxDGPhYuO/N8ozjWrPOYw
== mauricesvay@Macbook-Pro-de-Maurice.local

Définir des dépôts

Les dépôts sont définis par la simple présence de leur nom dans une clé readonly ou writable. Toutefois, un dépôt qui ne serait présent dans aucun writable ne pourra être rempli par personne et, du coup, ne servirait pas à grand chose…

Test : votre premier dépôt sur Gitosis

OK, calons ensemble le nécessaire à votre premier dépôt distant.

Paramétrer Gitosis

Primo, définissons l’accès. Supposons que votre nom d’utilisateur au sein de Gitosis sera the-boss, et votre dépôt hot-project. Ajoutez les éléments suivants à votre gitosis.conf :

[group my-projects]
writable = hot-project
members = the-boss

Ensuite, puisque Gitosis ne connaît pas encore votre compte, copiez votre clé publique au bon endroit et ajoutez-la dans l’index :

$ cp ~/.ssh/id_dsa_git.pub keydir/the-boss.pub
$ git add keydir/the-boss.pub

Il ne vous reste plus qu’à faire un commit puis un push vers Gitosis :

$ git commit -am "Ajout dépôt hot-project et utilisateur the-boss"
[master e46c687] Ajout dépôt hot-project et utilisateur the-boss
2 files changed, 5 insertions(+), 0 deletions(-)
create mode 100644 keydir/the-boss.git
$ git push
Counting objects: 8, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 984 bytes, done.
Total 5 (delta 2), reused 0 (delta 0)
To git@localhost:gitosis-admin.git
ac757b7..e46c687 master -> master

Le post-update hook fait alors son travail, et votre configuration SSH pour l’utilisateur git est mise à jour. En effet, si vous êtes curieux, vous pouvez jetez un œil au fichier .ssh/authorized_keys de ce compte, et vous y verrez votre utilisateur the-boss et ses réglages dédiés (j’ai ajouté quelques passages à la ligne, et tronqué la clé publique, pour la lisibilité de cet article) :

$ sudo cat /var/lib/git/.ssh/authorized_keys
### autogenerated by gitosis, DO NOT EDIT
command="gitosis-serve the-boss",no-port-forwarding,
no-X11-forwarding,no-agent-forwarding,no-pty
ssh-dss AAAAB3NzaC1kc3MAAACBALs…gZrZg== tdd@CodeMagic.local

C’est grâce à ces lignes que Gitosis sait automatiquement, sur la base des clés privées dont vous disposez et qui correspondent à ses clés publiques recensées, quel utilisateur logique vous êtes et quels traitements SSH vous autoriser. La main passe immédiatement à gitosis-serve (dans un contexte très contrôlé), qui exploite la configuration Gitosis pour les droits d’accès.

Tester le dépôt

Comme toujours avec un dépôt distant, il existe deux cas de figure :

  • Vous avez déjà des travaux locaux, qu’il s’agit alors d’envoyer sur le dépôt distant. Dans ce cas, nous allons simplement ajouter un remote et faire un premier push explicite (ainsi qu’une configuration pour pouvoir, par la suite, utiliser des pulls implicites).
  • Vous n’avez pas encore de travaux locaux, auquel cas le plus simple est de démarrer avec un clone histoire de paramétrer d’un seul coup le remote, le push et le pull.

Voyons déjà le second cas, qui est le plus simple.

Rien dans les poches : on démarre avec git clone

Il vous suffit en effet de vous placer dans le dossier parent de votre futur dossier projet, et de faire un git clone, exactement comme si vous récupériez un projet existant ; la seule différence, c’est que là, le projet sera vide :

# cd où ça vous semble bien, puis…
$ git clone git@localhost:hot-project.git
Initialized empty Git repository in /Users/tdd/tmp/hot-project/.git/
Initialized empty Git repository in /private/var/lib/git/repositories/hot-project.git/
warning: You appear to have cloned an empty repository.

Notez les deux dernières lignes…

« Initialized empty Git repository in /private/var/lib/git/repositories/hot-project.git/ » (le chemin initial ne sera pas forcément le même, ça dépend du home directory que vous aurez donné à l’utilisateur système git, et sur OSX /var est en fait /private/var) indique que le serveur distant a bien connaissance de ce projet mais n’avait pas encore la moindre donnée pour lui : il a donc initialisé le dépôt distant (pour les curieux, il a juste créé le dossier et y a fait un git init --bare).

Sur la dernière ligne, avec « warning: You appear to have cloned an empty repository. », Git vous prévient que vous venez de cloner un dépôt vide, ce qui n’est pas en soi un problème mais reste une situation suffisamment atypique pour être signalée.

L’avantage de cette méthode, lorsque vous n’avez encore aucun travail local existant, est qu’elle configure d’entrée de jeu le remote et les éléments nécessaires pour faire des git push et git pull implicites (c’est-à-dire n’ayant pas besoin de préciser le remote et la branche). Pour vous en convaincre, jetez un œil à la configuration du projet ainsi clôné :

$ cat hot-project/.git/config
# (ou, pour un autre format, git config -f hot-project/.git/config --list)
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = git@localhost:hot-project.git
[branch "master"]
remote = origin
merge = refs/heads/master

Notez la section [remote "origin"], qui définit le remote, et [branch "master"], qui définit la configuration distante de la branche master locale. Grâce à cette dernière section notamment, nous n’avons pas besoin de préciser quel remote et quelle branche distante exploiter pour un push ou un pull : on dit que la branche locale master est configurée pour tracker la branche distante origin/master.

Déjà du boulot en local : remote + push initial explicite + tracking

Bon, ça, c’était le cas simple. Imaginons maintenant que vous aviez déjà du boulot en cours. Simulons ça avec un dépôt local créé vite fait :

$ cd /tmp
$ mkdir existing-hot-project
$ cd existing-hot-project
$ git init
Initialized empty Git repository in /private/tmp/existing-hot-project/.git/
$ echo 'toto' > toto
$ git add toto
$ git commit -am "Du boulot local existant"
[master (root-commit) dbcb4c7] Du boulot local existant
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 toto

Il nous faut maintenant définir notre premier (et généralement notre seul) remote explicitement. Nous allons garder le nom conventionnel pour le remote principal : origin.

$ git remote add origin git@localhost:hot-project.git

À présent, il faut faire un premier push. Faute d’info de tracking résultant d’un clone ou d’une configuration manuelle, nous devons préciser le remote et la branche distante. Il n’y a pas de raison ici (et la plupart du temps) de nommer la distante autrement que la locale, donc on l’appellera aussi master.

$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 229 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@localhost:hot-project.git
* [new branch] master -> master

On y est presque (et manifestement Gitosis marche très bien). En effet, le tracking n’est pas défini automatiquement par notre push explicite tel que nous l’avons fait :

$ git pull
You asked me to pull without telling me which branch you
want to merge with, and 'branch.master.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) for details.

If you often merge with the same branch, you may want to
use something like the following in your configuration file:

[branch "master"]
remote = <nickname>
merge = <remote-ref>

[remote "<nickname>"]
url = <url>
fetch = <refspec>

See git-config(1) for details.

Une première approche consisterait à ajouter la configuration correspondante à la main :

$ git config --add branch.master.remote origin
$ git config --add branch.master.merge refs/heads/master
$ git pull
Already up-to-date.

Je vous montre ça pour des pushes que vous pourriez déjà avoir fait, et où ces réglages pourraient s’avérer utiles. Mais en fait, la meilleure manière dans ces cas-là est d’ajouter une petite option au push explicite : -u (ou sa version longue, --set-upstream). Cette option va réaliser nos configurations pour nous. Essayons en virant le remote et en le rajoutant :

$ git remote rm origin
$ git remote add origin git@localhost:hot-project.git
$ git push origin master -u
Branch master set up to track remote branch master from origin.
Everything up-to-date
$ git pull
Already up-to-date.

Bon, vu qu’on avait déjà fait un push de notre travail local tout-à-l’heure, naturellement au nouveau push, « Everything up-to-date ». En revanche, notez le message « Branch master set up to track remote branch master from origin. », qui montre bien que Git a configuré pour nous le tracking entre branches locale et distante. Et de fait, un pull implicite juste après trouve ses petits.

En conclusion

Voilà, je crois que vous avez désormais toutes les billes pour mettre en place votre propre hébergement Git (et peut-être avez-vous appris au passage une ou deux choses sur la gestion des dépôts distants).

Il est vrai que nous n’avons pas vu comment installer un serveur Git sous Windows, mais au risque de troller, un serveur sous Windows… Soyons sérieux deux minutes ! Plus pragmatiquement, cela est principalement dû au fait que les serveurs Git reposent fortement sur SSH et des configurations avancées d’utilisateurs, toutes choses qui sont ardues à transposer sous Windows, et quand bien même on y arriverait finalement, seraient fortement ralenties par la nécessaire couche intermédiaire basée Cygwin. Sans parler des problématiques de correspondance d’attributs, de gestion de casse MAJ/min et d’horodatage des fichiers entre les univers Linux, OSX et Windows. Du coup, rendez-vous service et si vous hébergez du Git, faites-le sur Linux ou OSX. D’où ce billet.

Les commentaires sont ouverts, aussi n’hésitez pas à me faire part de vos retours, questions et suggestions.

Bon hébergement Git à tous !