Workflow Centralisé

Passer d’un workflow distribué depuis un système de gestion de version centralisé peut faire peur. Mais il n’est pas forcément nécessaire de modifier complètement ses habitudes pour bénéficier des avantages de Git. L’équipe peut très bien continuer de développer comme elle avait l’habitude de le faire avec SVN

En utilisant Git chaque développeur possède un environnement local indépendant et peux « commiter » en local et oublier le répository centralisé jusqu’au moment où cela lui convient. Le tout en conservant son historique et en bénéficiant du mécanisme robuste de « branching » et « merging » de Git.

Comment ça marche ?

Comme pour SVN, un workflow centralisé utilise un répository central qui sert de point d’entrée à toute modification sur le projet. A la place de trunk, la branche par défaut de développement est appelée master. Toutes les modifications sont committer dans cette branche par défaut.

Les développeurs commencent par cloner le répository central. Dans leur copie locale, ils peuvent modifier les fichiers et committer ces changements comme ils le feraient sur SVN. Cependant ces nouveaux commit sont stockés localement et complètement isolés du répository central. Cela permet à chacun de décaler la synchronisation lorsque cela convient au mieux.

Pour publier ces modifications dans le répository central, les développeurs push leur branche master locale dans celle du répository central. C’est l ‘équivalent du commit de SVN, sauf que cela ajoute tous les commit locaux qui manquent dans le répository central.

En gros, le commit de SVN correspond aux commit+push de Git.

Gestions des Conflits

Le répository central représente le projet officiellement, il est donc primordial de traiter son historique des commit avec respect. Si les commits locaux d’un développeur diverge de celui-ci, Git va refuser le push de ces changements.

Aussi avant de pouvoir publier ses propres modifications, le développeur doit faire un fetch du répository central pour mettre à jour le sien en local, et ensuite faire un rebase de ses propres changements pour les ajouter par dessus ceux du répository central. On obtient ainsi un historique parfaitement linéaire comme avec un workflow traditionnel sous SVN.

Si ces modifications locales sont en conflit avec les commits du «  upstream » Git va geler le processus de rebase et donner l’opportunité au développeur de résoudre ses conflits manuellement. La bonne chose à propos de Git (en ligne de commande) est qu’il utilise les mêmes commandes status et add pour générer les commits et résoudre les conflits. De plus si les développeurs rencontrent un problème, il est très aisé avec Git d’arrêter le rebase pour recommencer ou chercher de l’aide.

Exemple

Prenons maintenant l’exemple d’une petite équipe de trois développeurs (Dev1, Dev2 et Dev3) et voyons ce qui se passe dans le cas de ce workflow.

Dans notre exemple le répository central est créé et chacun des développeurs en a fait un clone.

Dev1 travaille sur ses propres fonctionnalités

Git Workflows: Edit Stage Commit Feature Process

Dans son répository, Dev1 code ses fonctionnalités en utilisant le processus standard de commit de Git : edit, (stage), commit. Le stage est un moyen de préparer un commit sans avoir à inclure tous les changements du répertoire de travail.

 status  
 add  
 commit

Comme les commits sont locaux, Dev1 peut répéter ces commandes autant de fois qu’il le souhaite sans se préoccuper de ce qui se passe sur le répository central. Ce qui peut être très pratique si on travaille sur une grosse fonctionnalité qui a besoin d’être découpée en plus petites parties plus simple à traiter.

Dev2 travaille sur ses propres fonctionnalités

Git Workflows: Edit Stage Commit Feature

De son côté Dev2 fait la même chose en utilisant les mêmes commandes. Comme Dev1, Dev2 travaille sur ses fonctionnalités sans se préoccuper du répository central, ni des changements de Dev2, puisque tous ces changements sont locaux et privés.

Dev1 publie ses fonctionnalités

Git Workflows: Publish Feature

Une fois que Dev1 a fini son travail, il doit publier ses commits locaux dans le répository central pour en donner l’accès aux autres membres de l’équipe. Il fait cela au moyen de la commande *push *:

git push origin master  

origin est la connexion au répository central créée par Git au moment où Dev1 l’a cloné. Les arguments de la commande Git indique que Dev1 essaie de rendre la branche master du répository central à l’identique de la branche master de son répository local.

Dans notre scénario le répository central n’a pas été mis à jour depuis que Dev1 l’a cloné, il n’y a ainsi pas de conflit et la commande push va fonctionner sans problème.

Dev2 essaie de publier ses fonctionnalités

Git Workflows: Push Command Error

Voyons maintenant ce qui se passe si Dev2 essaie de faire un push de ses propres modifications locales dans le répository central. La commande est exactement la même :

git push origin master  

mais comme son historique local a divergé de celui du répository, Git refuse la commande avec un message sibyllin du type :

error: failed to push some refs to '/path/to/repo.git'  
hint: Updates were rejected because the tip of your current branch is behind  
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')  
hint: before pushing again.  
hint: See the 'Note about fast-forwards' in 'git push --help' for details.```  

Cela empêche donc Dev2 d’écraser les commits officiels. Il a besoin ici de récupérer les modifications de Dev1 dans son répository local, de les intégrer à ses propres modifications et de retenter le push.

Dev2 « rebase » par dessus le(s) commit(s) de Dev1

Git Workflows: Git Pull Rebase

Dev2 peut utiliser la commande pull (un pull est équivalent à un fetch + merge) pour incorporer les modifications centrales dans son répository local. Cette commande récupère entièrement le l’historique des commits centraux dans le local et tente de les intégrer aux commits locaux. La commande est la suivante :

git pull -- rebase origin master  

L’option — rebase indique à Git de déplacer tous les commits de Dev2 au sommet de la branche master après avoir synchroniser les changements depuis le répository central comme indiqué dans le schéma ci-dessous.

Notons que cette commande (le pull en général) peut être effectuée n’importe quand le développeur souhaite intégrer des nouveaux changements mis à disposition dans le répository central. Notons également que la plupart des interfaces graphiques (Eclipse, MS Project, etc..) indique cette commande avec l’option — rebase simplement comme « Rebase ».

Un pull simple (sans – rebase) pourrait toujours fonctionner mais entraînerait tout un tas de « merge commit » superflus à chaque tentative de synchronisation avec le répository central. Dans ce type de workflow, il est toujours préférable de faire un rebase plutôt qu’un merge commit.

Dev2 résout un conflit de « merge »

Git Workflows: Rebasing on Commits

Faire un travail de rebase consiste à transférer chaque commit local à la branche master un par un. Cela signifie que vous récupérez les conflits commit par commit, à la place de le faire sur un commit massif comme avec SVN. Ainsi les commits restent contextuels à un périmètre et permettent un historique clair et ordonné. Il est ainsi beaucoup plus simple de trouver où un bug a été incorporé et, si nécessaire, de faire un retour arrière avec un impact minimalisé.

Si Dev1 et Dev2 travaillent sur des fonctionnalités qui ne sont pas liées, il est peu probable que le processus de rebase génère de conflit. Mais si cela arrive, Git va faire une pause dans le processus de rebase du commit en cours et générer un message, avec quelques instructions du type :

CONFLICT (content): Merge conflict in <some-file>

Une bonne chose avec Git est que n’importe qui peut résoudre ses propres conflits. Dans notre exemple, Dev2 peut simplement exécuter un status pour voir où le problème se trouve. Les fichiers en conflit vont apparaître dans la section « Unmerged paths » :

 Unmerged paths:
 (use "git reset HEAD <some-file>..." to unstage)
 (use "git add/rm <some-file>..." as appropriate to mark resolution)

 both modified: <some-file>>```

Dev2 va alors modifier les fichiers comme il l’entend. Une fois ces modifications faites, il peut alors faire un « stage » des fichiers et laisser un nouveau rebase faire le reste :

git add
git rebase --continue
Et voilà tout ce qu’il y a à faire. Git va alors passer au commit suivant et recommencer le processus pour tout autre commit qui générerait un conflit.

Si on arrive à ce point et que l’on se rende compte que l’on est pas certain de ce qui se passe, il est inutile de paniquer. Il suffit juste d’exécuter la commande pull — rebase et on se retrouve au point de départ, sans aucun impact pour le reste de la communauté:

git rebase --abort

Exemple avec Eclipse

Le menu Merge de Git dans Eclipse ouvre une boite de dialogue demandant quelle mode on souhaite utiliser. La manière la plus simple de procéder consiste à choisir l’option “Use HEAD (the last local version) of conflicting files” comme mode de merge. De cette façon on peut voir les modifications originales à gauche et les conflits à droite.

On peut ainsi modifier manuellement le texte à gauche ou utiliser le bouton “Copy current change from right to left” pour transférer les modifications en conflit de droite à gauche.

Copy changes from right to left Une fois ce travail effectué il faut sélectionner le menu contextuel “Team → Add” pour indiquer que le conflit est résolu et commiter toutes le modifications avec le menu “Team → Commit”.

Dev2 publie ses modifications avec succès

Git Workflows: Synchronize Central Repo

Après avoir fini la synchronisation avec le répository central, Dev2 peut alors publier ses modifications avec succès :

git push origin master

Et maintenant ?

Comme nous avons pu le voir il est possible de reconstituer l’environnement de développement traditionnel de SVN en utilisant quelques commandes de Git. C’est intéressant pour une équipe qui viendrait du monde SVN, mais cela n’utilise pas la nature distribuée de Git. Il s’agit néanmoins d’un point de départ possible pour appréhender Git plus facilement.

Une fois l’équipe plus confortable avec l’outil Git, ce sera probablement le moment de passer à un workflow utilisant les branches. Les avantages d’un tel workflow couvrent très largement les inconvénients de l’investissement initial.

Références

Billet largement inspiré de celui d’Atlassian sur le sujet ainsi que de l’article du blog Vogella sur Egit.