Supposons qu’Alice aie démarré un nouveau projet dans son dépôt git situé dans /home/alice/project et que Bob, qui a un répertoire utilisateur sur la même machine (/home/bob/), veuille y contribuer.

Bob commence par :

$ git clone /home/alice/project mondepot

Cela crée un répertoire mondepot qui contient un clone du dépôt d’Alice. Le clone est une copie parfaite du projet original, contenant aussi sa propre copie de l’historique du projet original.

Bob fait quelques changements et les commit :

(éditer des fichiers)
$ git commit -a
(répéter autant que nécessaire)

Quand il est prêt, il dit à Alice de récupérer (pull) ses changements depuis son dépôt situé dans /home/bob/mondepot. Alice fait alors :

$ cd /home/alice/project
$ git pull /home/bob/myrepo master

Cela merge les changements de la branche master de Bob dans la branche courante d’Alice. Si Alice a fait ses propres changements pendant ce temps, alors elle devra peut être résoudre des conflits à la main (l’option master dans la commande ci-dessus n’est pas nécessaire car c’est l’option par défaut).

La commande pull travaille donc en deux étapes : elle récupère les changements d’une branche distante et merge ces changements dans la branche courante.

Quand vous travaillez dans une petite équipe soudée, il est courant que tous interagissent très souvent avec le même dépôt. En définissant un raccourci pour le dépôt « distant », nous pouvons rendre ces opération plus simples :

$ git remote add bob /home/bob/mondepot

Avec ça, Alice peut effectuer la première opération en utilisant seulement la commande git fetch, elle ne mergera pas les modifications avec sa propre branche :

$ git fetch bob

Contrairement à la version longue, quand Alice récupère (fetch) les données de Bob en utilisant un raccourci configuré avec git remote, alors ce qui est récupéré est stocké dans une branche de suivi distant, dans notre cas bob/master. Donc maintenant :

$ git log -p master..bob/master

montre la liste de tous les changements que Bob a fait depuis qu’il a créé une branche depuis la branche master d’Alice.

Après avoir examiné ces changements, Alice peut les merger dans sa branche master :

$ git merge bob/master

Ce merge peut aussi être fait en récupérant les données depuis sa propre branche de suivi distante, comme ceci :

$ git pull . remotes/bob/master

Git récupère toujours les merges dans la branche courante, quelques soient les options de la ligne de commande.

Plus tard, Bob peut mettre à jour son dépôt avec les dernières modifications d’Alice en utilisant :

$ git pull

Il n’a besoin de donner le chemin vers le dépôt d’Alice. Quand Bob a cloné le dépôt d’Alice, git a stocké l’adresse de son dépôt dans la configuration du dépôt et cette adresse est utilisée pour récupérer les données avec pull :

$ git config --get remote.origin.url
/home/alice/project

(la configuration complète créée par git-clone est visible en lançant git config -l et la page de documentation de git config explique chacune de ces options)

Git conserve aussi sa propre copie de la branche master d’Alice sous le nom origin/master :

$ git branch -r
  origin/master

Si Bob décide plus tard de travailler avec un hébergeur différent, il pourra toujours créer des clones et récupérer les données en utilisant le protocole ssh :

$ git clone alice.org:/home/alice/project myrepo

D’une autre manière, git contient un protocole natif ou peut aussi utiliser rsync ou http. Voir git pull pour plus de détails.

Git peut aussi être utilisé de manière plus similaire à CVS, avec un dépôt central sur lequel de nombreux utilisateurs envoient leur modifications. Voir git push et gitcvs-migration.

Les dépôt git publics

Une autre façon d’envoyer des modifications à un projet est d’avertir le chef de ce projet afin qu’il récupère les changements depuis votre dépôt en utilisant git pull. C’est un manière d’obtenir les mises à jours du dépôt principal mais cela fonctionne aussi dans l’autre sens.

Si vous et le chef de projets avaient tous les deux un compte sur le même ordinateur, alors vous pouvez échanger les modifications de vos dépôts respectifs directement. Les commandes qui acceptent des URL de dépôts comme options, accepteront aussi un chemin de répertoire local :

$ git clone /path/to/repository
$ git pull /path/to/other/repository

ou une adresse ssh :

$ git clone ssh://yourhost/~you/repository

Pour les projets avec quelques développeurs ou pour synchroniser quelques projets privés, cela peut vous suffire.

Cependant, la pratique la plus courante est de maintenir un dépôt public (généralement sur le même host) pour que les autres puissent y récupérer les changements. Cela est souvent plus efficace et vous permet de séparer proprement le travail privé en cours de réalisation des projets publics et visibles.

Vous continuerez à travailler au jour-le-jour sur votre dépôt personnel, mais périodiquement vous enverrez (push) les modifications de votre dépôt personnel sur votre dépôt public, permettant alors aux autres développeurs de récupérer (pull) les changements disponible dans ce dépôt. Donc le flux de travail, dans une situation où un autre développeur fournit des changement dans son dépôt public, ressemble à ça :

                           vous envoyez (push)
  votre dépôt personnel ---------------------------> votre dépôt public
            ^                                               |
            |                                               |
            | vous récupérez (pull)                         | ils récupèrent (pull)
            |                                               |
            |                                               |
            |               ils envoient (push)             V
  leurs dépôts publics <---------------------------  leurs dépôts

Publier des modifications sur un dépôt public

L’utilisation des protocoles http et git permet aux autres développeurs de récupérer les derniers changements mais ils n’auront pas l’autorisation d’écrire sur ce dépôt. Pour cela, vous devrez mettre à jour votre dépôt public avec les derniers changements obtenus depuis votre dépôt privé.

La façon la plus simple de procéder est d’utiliser git push et ssh. Pour mettre à jour la branche master avec le dernier état de votre branche master, lancez :

$ git push ssh://yourserver.com/~you/proj.git master:master

ou juste :

$ git push ssh://yourserver.com/~you/proj.git master

Comme avec git-fetch, git-push se plaindra qu’il n’y a pas eu d’avance rapide (fast-forward), allez à la section suivante pour plus de détails pour gérer ce cas.

La cible d’un push est normalement un dépôt « nu ». Vous pouvez aussi publier vers un dépôt qui contient une arborescence de travail mais cette arborescence ne sera pas mise à jour durant la publication. Cela pourra vous amener à des résultats inattendus, par exemple si la branche que vous publiez est l’actuelle branche de travail sur le dépôt public.

Comme avec git-fetch, vous pouvez aussi rajouter des options de configuration pour vous permettre d’aller plus rapidement, par exemple, après :

$ cat >>.git/config <<EOF
[remote "public-repo"]
    url = ssh://yourserver.com/~you/proj.git
EOF

vous devriez pouvoir effectuer les opérations précédentes avec juste :

$ git push public-repo master

Voir les explications des options remote.<name>.url, branch.<name>.remote, et remote.<name>.push dans git config pour plus de détails.

Que faire quand une publication échoue

Si une publication ne se termine pas avec une avance de la branche distante, alors elle échouera avec un message d’erreur comme celui-ci :

error: remote 'refs/heads/master' is not an ancestor of
local  'refs/heads/master'.
Maybe you are not up-to-date and need to pull first?
error: failed to push to 'ssh://yourserver.com/~you/proj.git'

Cela peut arrivé, par exemple, si :

- vous avez utilisé `git-reset --hard` pour effacer un commit déjà publié ;
- vous avez utilisé `git-commit --amend` pour remplacer un commit déjà publié ;
- vous avez utilisé `git-rebase` pour recombiner un commit déjà publié.

Vous pouvez forcer un git-push à effectuer quand même la mise à jour en rajoutant le préfixe « + » au nom de la branche :

$ git push ssh://yourserver.com/~you/proj.git +master

Normalement, quand le sommet de la branche d’un dépôt public est modifié, il est modifié pour pointer vers un descendant du commit vers lequel il pointait avant. En forçant la publication dans cette situation, vous cassez cette convention.

Néanmoins, c’est une bonne pratique pour les gens qui ont besoin de publier simplement une série de patches des travaux en cours et c’est un compromis acceptable tant que vous prévenez les autres développeurs que vous comptez gérer la branche de cette façon.

Il est aussi possible qu’une publication échoue de cette façon quand d’autres personnes ont le droit de publier sur le même dépôt. Dans ce cas, la solution la plus correcte est de retenter la publication après avoir mis à jour votre travail : soit par un git-pull, soit par un git-fetch suivi d’une recombinaison (rebase). Voir la prochaine partie et gitcvs-migration pour plus de détails.


"GitCast #8: Distributed Workflow"