Tutoriel GIT - Programmes Européens 2014

Transcription

Tutoriel GIT - Programmes Européens 2014
Tutoriel GIT
1 sur 11
http://www.moussu.fr/git/
Tutoriel GIT
Auteurs : Bertrand Chazeau, Clément Moussu, Laurent Charignon et Vaibhav Singh.
Table des matières
I. Introduction
II. Commandes de base
III. Gestion des branches
IV. Exemple
I. Introduction
Objectifs du tutoriel
À la fin de ce tutoriel vous :
Saurez ce que sont les systèmes de gestions de versions (Concurrent Version System en anglais)
Pourrez installer et configurer le système de gestion de version Git
Connaîtrez les commandes de bases pour une utilisation quotidienne de Git
Aurez la possibilité de travailler de manière flexible à l'aide des branches
Ne serez pas perdu avec la cheat sheet et les pointeurs que nous vous fournissons
Systèmes de gestions de versions : vocabulaire
Un logiciel de gestion de versions (VCS en anglais) permet de stocker les différentes versions d'un ensemble de fichiers afin de faciliter l'évolutivité d'une production informatique. Par la suite nous utiliserons l'abréviation VCS pour désigner les
logiciels de gestions de versions. Certains termes sont spécifiques au monde des VCS ou à GIT et il est important de bien les comprendre pour pouvoir travailler efficacement :
Commit et branche
Le mot commit désigne à la fois la création d'une nouvelle version (lorsque c'est un verbe) et cette nouvelle version (lorsque c'est un nom). "Je commit" veut dire, j'entérine les changements que j'ai effectué et ils constituent une version. "Le deuxième
commit de mon projet", désigne sa deuxième version. Les commits sont organisés en arbre et la figure suivante en donne un exemple :
Source : Progit
C0...C5 désignent des versions. Les flèches représentent des liens de parentés, entendons par là que C1 a été créé à partir de C0, C1 est postérieur à C0. Au niveau de C2 le développement s'est divisé en deux branches : master et iss53. Ces branches
correspondent à la notion intuitive d'une branche d'un arbre par exemple: la branche master va de C0 à C4. Ne prenez pas garde à la notation adoptée pour les branches, elle sera expliquée par la suite.
Dépôt (Repository)
Désigne l'ensemble des fichiers conservés par le système de gestion de version.
Remote
Désigne un dépôt distant (par opposition à un dépôt local).
Systèmes de gestions de versions : architecture
Le système de gestion de versions se déclinent en trois types :
Local : le dépôt se situe uniquement sur la machine de l'utilisateur
Centralisé : les informations se situent sur un serveur central. L'information y est principalement située et le ou les utilisateurs doivent s'y connecter pour travailler
Distribué : les informations sont stockées sur un ensemble de machines de manière distribuée. On peut reconstituer le contenu du projet à partir de n'importe quelle machine, il n'y a pas un point de faiblesse comme dans le cas de l'architecture
centralisée.
A partir de maintenant nous nous intéresserons à Git qui est un système de gestion de versions distribué.
Git : aperçu des possibilités
Vous commencez certainement à deviner le fonctionnement des système de gestion de version, voici une partie des avantages qu'apporte Git aux développeurs :
Possibilité de revenir en arrière dans le développement
Possibilité de travailler en parallèle
Possibilité de travailler uniquement en local(par exemple sans internet) et de transmettre toutes les informations dans un second temps
Possibilité de débugger automatiquement pour localiser l'introduction d'un bug parmi l'arbre des commits
...
Installer Git
Sous ubuntu ou debian il vous suffit d'installer le paquet git pour pouvoir disposer de toutes les fonctionnalités du logiciel.
sudo aptitude install git
Sous mac os X 10.6 git est déjà présent de base dans le système.
Configurer Git
Git se configure soit en ligne de commande, soit directement dans un fichier de config qui se trouve dans votre home : .gitconfig. En voici un exemple :
[user]
name = Laurent Charignon
email = [email protected]
[core]
editor = vim
[alias]
co = checkout
br = branch
st = status
a = add
Ici il faut comprendre que mon nom et mon email sont spécifiés, que j'utilise vim et que j'utilise des alias pour quatre commandes. Pour modifier ce fichier par le biais de la ligne de commande on utilise la commande git config avec la syntaxe suivante
:
git config --global catégorie.champ valeur
par exemple :
git config --global user.name "Laurent Charignon"
L'option --global permet de faire que le choix s'applique à tous les projets que l'on utilise. Si vous voulez faire des configurations spécifiques à un projet vous pouvez retirer cette option. man git config fournit une liste de toutes les options
configurables avec la syntaxe mentionnée ci-dessus.
Interface graphique pour visualiser les branches
17/02/13 19:05
Tutoriel GIT
2 sur 11
http://www.moussu.fr/git/
Vous pouvez utiliser différentes interfaces graphiques pour visualiser l'arbre des commits. En voici deux exemples :
17/02/13 19:05
Tutoriel GIT
3 sur 11
http://www.moussu.fr/git/
Gitk et son interface Tk
17/02/13 19:05
Tutoriel GIT
4 sur 11
http://www.moussu.fr/git/
Qgit et son interface Qt
17/02/13 19:05
Tutoriel GIT
5 sur 11
http://www.moussu.fr/git/
II. Commandes de base
Commande git clone
Essayons de voir ce qui se passe dans un projet. Au départ, nous supposons que notre projet est déjà présent dans un dépôt distant. Pour télécharger les fichiers (i.e. pour cloner le dépôt), nous utilisons la commande suivante :
git clone <URL>
par exemple : git clone [email protected]:2011/PrenomNom.git Notre machine contient une copie du dépôt distant. Maintenant, si nous voulons ajouter un nouveau fichier à notre projet, nous créons le fichier, par exemple
EthernetDriver.c, mais git ne sait rien sur ce fichier. L'état actuel du fichier est dit untracked.
Commande git status
Lorsque l'on a modifié le contenu du fichier, il devient modified pour git. Le répertoire dans lequel nous travaillons est appelé répertoire de travail ou working directory. Nous utilisons ce répertoire pour travailler. Pour obtenir le statut des différents
fichiers du dépôt on utilise la commande git status.
Voilà ce que renvoie cette commande.
$ git status Etherenet.c
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
#
EthernetDriver.c
nothing added to commit but untracked files present (use "git add" to track)
Commande git add
Pour ajouter ce fichier dans dépôt local, nous utilisons la commande git add <file> Après cela, git commencera le suivi de ce fichier et le statut de ce fichier passera de untracked à tracked. Lors d'un changement du contenu de ce fichier git détecte
ce changement et le fichier passe dans l'état modified Après avoir fait tous les changements, nous devons tester nos modifications et une fois satisfaits nous pouvons procéder à l'enregistrement de ces changements. C'est ce que l'on appelle un commit.
Avant de regrouper ces fichiers au sein d'un commit, il faut préciser à git lesquels il doit envoyer pour constituer ce commit. Pour ce faire on utilise soit la commande git add suivie du nom d'un fichier. On peut utiliser la commande git add -u qui
tient compte de toutes les modifications effectuées sur les fichiers déjà suivis par git.
Un snapshot est fait au moment du add. C'est ce snapshot qui sera commité. Les modifications intermédiaires ne seront pas commitées. Pour qu'elles le soit, il suffit de refaire un add.
Commande git rm
Pour arrêter de suivre un fichier nous utilisons la commande git rm suivi du nom d'un fichier.
Pour enlever un fichier de la staging area, utiliser : git rm --cached suivi du nom d'un fichier.
Commande git commit
git commit
permet d'enregistrer les changements en staging area dans le dépôt. Seuls les changements se trouvant dans la staging area feront partie du prochain commit. On peut utiliser la commande :
git commit -a
pour indiquer à git d'envoyer dans le prochain commit tous les fichiers déjà suivis en prenant compte toutes les modifications effectuées depuis le dernier commit les ayant concernés.
Commande git log
Pour afficher le log des commits :
git log
et d'un fichier particulier :
git log <file>
Commande git diff
Pour afficher les évolutions entre deux commits :
git diff <sha1_1> <sha1_2> <file>
On peut aussi utiliser :
git log -p
Commande git show
Pour afficher un fichier d'un commit particulier :
git show <sha1> <file>
Commande git grep
Cette commande permet de rechercher du texte dans les fichiers trackés par git. Cela est plus rapide et plus efficace qu'un grep standard qui va rechercher dans tous les fichiers temporaires, fichiers objets, exécutables...
Commande git revert
Cette commande permet d'insérer au sommet de la branche courante le dual d'un commit. Cela est particulièrement utile dans les branches telles que master dans laquelle on ne peut pas se permettre d'avoir une perte d'historique. Par exemple si nous
sommes dans la situation suivante :
$ git log
commit 67d9dfa351f642159480b306f1004c158a283e92
Author: John Doe <[email protected]>
Date:
Mon Mar 7 21:52:24 2011 +0100
Fix issue #1344.
commit dc7767340c492fd93b55955a28572c98636a10f7
Author: John Doe <[email protected]>
Date:
Mon Mar 7 21:38:06 2011 +0100
Add feature #755.
commit b12caf55ad9c254b1997435031da49a5a05f902b
Author: John Doe <[email protected]>
Date:
Mon Mar 7 21:27:09 2011 +0100
Add feature #740.
Notre collègue John Doe a introduit des erreurs dans son commit ajoutant la fonctionnalité 740. Ce commit étant sur la branche master nous ne pouvons pas l'en enlever. Nous allons donc exécuter la commande :
git revert b12caf55
Cela va appliquer le diff inverse de celui du commit b12caf55 annulant ainsi tous les changements faits dans ce commit. Git va faire cela de manière autonome, à condition que les commits dc776734 et 67d9dfa3 ne modifient pas les mêmes parties
des fichiers. Dans ce cas il faudra les fusionner manuellement.
Commande git reset
Cette commande permet de déplacer le pointeur HEAD et la branche courante vers un commit arbitraire. Il existe néanmoins plusieurs variantes :
git reset --hard <commit>
L'option --hard provoque la perte de tous les changements actuels et remet la branche courante à l'état du commit donné en paramètre.
17/02/13 19:05
Tutoriel GIT
6 sur 11
http://www.moussu.fr/git/
git reset --soft <commit>
L'option --soft remet la branche courante à l'état du commit donné en paramètre mais laisse les différence entre ce commit et HEAD (avant le reset) dans la staging area. Il sont ainsi prêts à être enregistrés dans un autre commit.
Commande git bisect
Cette commande très utile permet de chercher par dichotomie le commit qui aurait introduit un dysfonctionnement. Si le code versionné ne fonctionne plus, que l'on est capable de donner un commit pour lequel le code marchait et que l'on peut
formuler un test permettant de savoir si le code fonctionne alors git bisect trouvera en un temps optimal le commit responsable. Cette page du Git Community Book explique en détail l'utilisation de git bisect.
III. Gestion des branches
1 - Gestion locale des branches
Au sein d'un dépôt, les commits sont organisés sous la forme d'un arbre. On peut voir sur le schéma suivant que chaque commit est identifié par un numéro unique : le sha1 (le risque de collisions étant très faible). On remarque aussi que chaque
commit pointe vers le commit précédent. De plus le commit f30ad a deux fils : c2b9e et 87ab2. Il y a un embranchement, les deux branches étant master et testing.
Source : Progit
Une branche n'est rien plus qu'un pointeur vers un commit. Un autre pointeur est HEAD qui pointe vers le commit sur lequel on est en train de travailler (le pointeur est sauvegardé dans le fichier .git/HEAD). La branche master est particulière, c'est la
branche principale, de production, elle n'est censée contenir que du code fonctionnel. Les branches sont très utiles. En effet, elles permettent de faire du développement en parallèle. Supposons qu'on veuille ajouter une fonctionnalité sans perturber
l'avancée de master (par d'autre ou par nous-mêmes). Il suffit pour cela de travailler dans une autre branche br1. On pourra à tout moment revenir dans master sans être gêné par les modifications faites dans br1.
Voici quelques commandes pour créer des branches et se déplacer entre elles :
Pour créer une branche au niveau du commit courant :
git branch <name>
Pour en supprimer une :
git branch -d <name>
Pour lister les branches (le * dans le listing correspond à la branche courante) :
branches locales :
git branch
branches distantes :
git branch -r
branches mergées (cf partie 3) à la branche courante :
git branch --merged
branches non mergées à la branche courante :
git branch --unmerged
Pour voir le dernier commit de chaque branche :
git branch -v
Pour se déplacer sur un commit/une branche :
git checkout <commit/branch>
Pour créer une branche et la rendre active en même temps :
git checkout -b <name>
2 - Interactions avec un dépôt distant
Pour partager son travail et se protéger de toutes pertes de données qui pourraient advenir, il faut envoyer fréquemment ses commits sur un serveur distant (dans notre cas hg.comelec.enst.fr). Pour cela plusieurs commandes :
git fetch
Permet de se synchroniser avec le serveur sans changer l'état courant.
On reçoit l'ensemble des nouveaux commits et une copie locale des branches distantes (leur nom est préfixé par le nom du serveur, en général origin/)
git push <remotename> <branch>:<remotebranch>
Envoie au serveur <remotename> les nouveaux commits de <branch> et met à jour le pointeur <remotebranch> au niveau de <branch>
git push <remotename> <branch>
Équivalent de :
git push <remotename> <branch>:<branch>
git push <remotename> :<remotebranch>
Supprime la branche distante
Pour tracker une branche, c'est-à-dire qu'elle soit associée à une branche distante, plusieurs moyens :
git push -u <remotename> <branch>:<remotebranch>
git checkout -b <branch> <remotename>/<branch>
17/02/13 19:05
Tutoriel GIT
7 sur 11
http://www.moussu.fr/git/
git checkout --track <remotename>/<branch>
On peut alors utiliser push et pull(voir plus loin) sans argument sur ces branches.
3. Merge
Une fois que l'on a terminé de travailler sur une branche, on peut vouloir la fusionner avec une autre (master par exemple).
Source : Progit
Dans l'exemple ci-dessus, une branche iss53 a été créée en C2, puis deux commits (C3 et C4) y ont été fait. En parallèle un nouveau commit (C4) a été fait dans master. On veut appliquer les modifications de iss53 sur master en mergeant cette branche.
Il en résulte un commit (C6) ayant deux parents.
La procédure pour faire un merge est la suivante :
Se déplacer dans la branch où merger :
git checkout <branch1>
Merger :
git merge <branch2>
Il peut y avoir un merge conflict si git n'arrive pas à fusionner un ou plusieurs fichiers. Cela arrive s'ils ont été modifiés aux mêmes lignes dans les deux branches. Dans ce cas-là :
Faire un git status pour voir les problèmes
Les résoudre par exemple avec git mergetool qui utilise le logiciel configuré avec git config --global merge.tool <software>.
Git ajoute des balises pour délimiter les problèmes :
<<<<<<< <branch1>:<filename>
<ligne(s) version 1>
=======
<ligne(s) version 2>
>>>>>>>> <branch2>:<filename>
Il ne reste plus qu'à faire un commit
Dans les deux cas, avant de faire un commit et de pusher, il ne faut pas oublier de tester le code résultant du merge.
Notez aussi la commande
git pull <remotename> <branch>:<branch>
qui fetch et ensuite merge la branche.
4. Rebase
Une autre façon de fusionner des branches est le rebase, détaillé dans l'exemple suivant. Cela consiste à appliquer les modifications d'une branche, commit par commit, au sommet d'une autre :
Supposons que l'on ait une branche experiment et qu'on veuille la fusionner avec master :
Source : Progit
Un merge donnerait cela :
Source : Progit
Avec un rebase on crée un nouveau commit C3' au sommet de master contenant les mêmes modifications que C3 :
Source : Progit
Il suffit ensuite de faire un fastforward de master :
Source : Progit
Les commandes pour réaliser cela sont :
git rebase <branch>
rebase la branche courante sur branch
git rebase <--onto><branch1> <branch2>
rebase branch2 sur branch1
17/02/13 19:05
Tutoriel GIT
8 sur 11
http://www.moussu.fr/git/
On utilise ensuite la commande merge dans branch pour faire un fastforward
git checkout <branch1> && git merge <branch2>
Il peut y avoir avoir des conflits comme pour un merge. Après les avoir résolus avec git mergetool faire un :
git rebase --continue
git rebase --abort
permet d'annuler un rebase en cours.
Attention à ne pas faire de rebase d'une branche d'où part d'autres branches ou sur laquelle d'autres personnes travaillent. Cela rendrait difficile les merges/rebases futurs.
git pull --rebase
fait un rebase à la place d'un merge.
IV. Exemple
Dans un premier temps vous allez vouloir cloner votre dépôt. Cela se fait avec la commande git clone qui va créer une copie locale de votre dépôt distant. Ce dépôt local va contenir tout l'historique de votre projet. Si le dépôt distant venait à être
perdu votre dépôt local contiendrait toutes les informations nécessaires à sa recréation à l'exception des commits faits après votre dernier clone / pull / fetch. Git utilise par défaut le protocole ssh donc, dans notre cas, nous n'avons rien besoin
d'ajouter avant le nom d'utilisateur (pas de http://, https://, git:// ...) :
git clone [email protected]:2011/Projet.git
Pour éviter toute confusion précisons que 2011 n'est pas un numéro de port mais un répertoire sur le serveur. Notez que vous ne clonez pas en tant que vous même mais avec le nom d'utilisateur elecinf381. C'est votre clé ssh qui vous identifie et vous
permet de vous connecter. Plus précisément, pour les gens qui se mélangent un peu dans les clés, c'est la clé dont vous avez transmis la partie publique aux enseignant. Pour que cela fonctionne il faut donc que vous ayez cette clé sur l'ordinateur
duquel vous voulez cloner. Elle doit se trouver dans le répertoire ~/.ssh/ sous la forme de deux fichiers id_Xsa.pub (clé publique) et id_Xsa (clé privée à ne jamais transmettre à personne). Votre clé privée est elle-même chiffrée à l'aide d'un mot
de passe que vous devez entrer au moment du clone (sauf si vous avez un agent ssh). Vous devez donc vous retrouver face à une invite semblable à :
Enter passphrase for key '~/.ssh/id_Xsa':
Si l'invite demande un mot de passe et non une passphrase :
Password:
c'est que votre clé n'a pas été trouvée ou que ce n'est pas la clé que vous avez transmise aux enseignants. Ne vous acharnez donc pas à entrer votre passphrase ou votre mot de passe de l'école cela ne marchera pas.
Figure 1 : Clonage d'un dépôt
Regardons la figure 1. Nous remarquons que le dépôt local contient exactement les mêmes commits que le dépôt distant. On remarque qu'une branche b1 est présente et a divergé de master trois commits dans le passé. Considérons que cette branche
contient des commits effectué par un collègue, Bob, et qui corrigent une erreur connue dans le code source. Notons également l'apparition de nouvelles branches origin/... ici représentées en gris. Ces branches sont des copies locales des branches
distantes. Ce concept un peu dur à appréhender vous semblera très vite évident. Elles indiquent la position d'une branche sur le serveur distant. Par exemple si vous faites évoluer votre branche master, la branche origin/master n'évolue pas et vous
permet ainsi savoir dans quel état est la branche sur le serveur avant que vous ne pushiez. Une fois que vous pushez sur le serveur la branche origin/master se déplace sur le même commit que master.
Mais pourquoi origin/... ? Nous avons vu précédemment que git est distribué. Cela implique que tout ne passe pas par un seul serveur comme dans les gestionnaires de version centralisés. Il vous est possible d'avoir plusieurs serveurs distants sur
lesquels vous pushez, mais vous pouvez également puller des changements sur des dépôts de vos collègues. Notez que pour pusher sur un dépôt il doit être bare; vous ne pouvez donc pas pusher sur le dépôt d'un collègue sauf si il vous a mis à
disposition un dépôt bare. Git nomme tous ces différents dépôts distants remote. Une remote va donc se composer de l'adresse d'un dépôt et d'information sur les branches à suivre et avec lesquelles se synchroniser. Lorsque vous clonez git crée
automatiquement une remote spéciale appelée origin qui contient l'adresse du dépôt que vous avez cloné. Si vous avez deux remotes origin et enst, un dépôt que vous avez à l'école par exemple, vous aurez donc pour une branche locale master
deux branches origin/master et enst/master qui décrivent l'état des branches sur ces deux remotes.
Essayons de créer une branche. Pour cela plaçons nous au sommet de master :
git checkout master
Puis créons une nouvelle branche que l'on nomme b2 :
git branch b2
Enfin positionnons nous sur cette nouvelle branche :
git checkout b2
Se positionner sur une branche signifie que c'est cette branche que le prochain commit déplacera. On remarque sur la figure 2 l'apparition de la branche b2 au niveau de master.
Figure 2 : Création d'une branche
Mais pourquoi se placer au sommet de master ? Et qu'est ce que master ? La branche master est, comme son nom l'indique, la branche principale de vos développements. Cette branche est communément utilisée pour du code en production c'est à
dire du code qui compile et qui marche. Pourquoi ? Parce que c'est la branche commune à tout le monde et la branche sur laquelle tout le monde s'appuie. Il serait embêtant de devoir corriger les erreurs de quelqu'un d'autre avant de commencer votre
travail. S'appuyer sur master est la garantie que l'on part d'une base stable (à condition que tout le monde respecte les règles du jeu) avant d'implémenter sa fonctionnalité. On travaillera donc toujours en trois étapes : travail dans une branche,
vérifications et tests et enfin intégration dans master.
17/02/13 19:05
Tutoriel GIT
9 sur 11
http://www.moussu.fr/git/
C'est ce que nous allons faire ici. Notre branche b2 étant créée, nous allons faire des modifications qui ajoutent une fonctionnalité à nos développements :
<modifications> ...
git add <files>
git commit
Les commits devant rester atomiques, supposons que l'on réitère ces opérations trois fois pour ajouter notre fonctionnalité. La figure 3 montre l'état du dépôt local après ces modifications. Le dépôt distant reste pour l'instant inchangé : tous les
changements sont locaux.
Figure 3 : Modifications
Nous allons maintenant vouloir transférer nos modifications sur le serveur. Qu'ils soient stables ou non, justifiés ou non, importants ou non, nous vous conseillons de transférer régulièrement vos changements sur le serveur. Cela vous permettra avant
tout d'avoir une sauvegarde en cas de problème avec votre ordinateur (perte, vol, détérioration, mauvaise manipulation, bug...). Cela permet également à vos collègues de voir votre travail et de vous faire des remarques et des suggestions. La figure 4
nous montre l'état des dépôts local et distant après l'execution de la commande :
git push origin b2
Cette commande qui est un raccourci pour git push origin b2:b2 transfère au serveur tous les commits qu'il n'a pas déjà puis déplace la branche distante b2 sur le commit pointé par la branche locale b2. Notons la création d'une branche origin/b2
dont vous avez maintenant compris la sémantique et l'utilité.
Figure 4 : Mise à jour d'une branche distante
Supposons à présent que Bob, qui est en week-end, vous appelle et vous dise qu'il faut absolument que vous intégriez sa branche b1 dans master avant la votre car Sally, une autre collègue, en a besoin rapidement. Ce scénario qui semble tiré par les
cheveux est en réalité très courant.
Vous allez dans un premier temps laisser votre branche b2 de coté pour vous concentrer sur celle de Bob : b1. Il ne serait pas judicieux (et compliqué) d'intégrer les commits baf et cac entre les commits a3f et 1ed de master. Cela modifierait
l'historique de master ce qu'il ne faut jamais faire. En effet, si on faisait ça, les commits 1ed f5a 053 devraient être réappliqués au dessus de cac et changeraient donc d'identifiant sha-1. Cela signifie que toutes les personnes qui auraient fait partir
une branche d'un de ces trois commits ne sont plus reliés à master de la façon qu'ils pensent. Cela implique qu'ils auraient potentiellement beaucoup d'opérations compliquées à faire alors qu'ils ne sont pas responsables de ces changements. À éviter...
Dans ce cas on va préférer utiliser une opération appelée rebase. Un rebase d'une branche sur une autre permet de réappliquer les commits de la première au sommet de la seconde. Le fait de réappliquer ces commits au dessus d'autres qui contenaient
de nouvelles modifications modifie leur identifiant sha-1. La figure 5 présente l'état du dépôt local après l'execution de la commande :
git rebase master b1
Ou si nous nous trouvions déjà sur b1 :
git rebase master
Nous constatons que les branches b1 et origin/b1 sont devenues deux branches totalement différentes : différences des points de divergence avec master et différence des identifiants des commits. La branche b1 est désormais au sommet de master et
est prête à y être intégrée, à condition que d'autres commits ne soient pas pushés sur la branche master du serveur entre temps. Dans ce cas il faudrait refaire des rebase tant qu'il y a des nouveaux commits sur master, cela arrive fréquemment sur des
dépôts où beaucoup de gens pushent.
Figure 5 : Rebaser une branche sur une autre
Le rebase n'est néanmoins pas une opération anodine. Les changements intervenus entre l'ancien et le nouveau point de divergence peuvent entrer en conflits avec les changements sur la branche. Git va donc pouvoir vous demander de fusionner des
fichiers quand il ne peut pas le faire lui même. Dans ce cas le rebase s'arrête à l'endroit du conflit, dans le même état que juste avant de faire le commit, et git vous laisse faire ce que vous voulez pour corriger le conflit. Vous pouvez faire absolument
tout ce que vous voulez, vous êtes dans le passé et vous faites ce que vous pouvez pour que le présent soit possible. Vous pouvez par exemple utiliser l'outil de fusion défini dans votre fichier de configuration en exécutant la commande :
git mergetool
Une fois les changements et les fusions réalisées (plus de both modified dans le git status) vous continuez le rebase en exécutant :
git rebase --continue
Enfin si vous êtes en train de tout casser et que vous ne voyez plus comment vous en sortir vous aimerez la possibilité d'annuler le rebase en cours et de revenir dans le même état (état du dépôt et pas seulement de la branche en cours) à l'aide de la
commande :
17/02/13 19:05
Tutoriel GIT
10 sur 11
http://www.moussu.fr/git/
git rebase --abort
Pour pouvoir intégrer b1 dans master nous aurions également pu utiliser un merge. Nous utilisons ici un rebase car il plus adapté à ce que nous voulons faire. Il est pratique que l'historique de la branche master reste linéaire, notamment pour chercher
l'apparition d'une erreur dans l'historique. Un merge introduit un nouveau commit qui fusionne les deux branches et qui a de ce fait deux parents. Il est moins aisé de remonter dans l'histoire d'une branche si il y a plusieurs chemins possibles. Par
ailleurs en utilisant un rebase on efface de l'historique le fait que la fonctionnalité ait été développée sur une branche et le moment auquel cela a été fait. Dans la plupart des cas cette information n'apporte rien, mais on peut très bien vouloir la
conserver et faire un merge.
Nous allons maintenant vouloir synchroniser la branche b1 du serveur avec la notre. Essayons d'exécuter la commande suivante :
git push origin b1
Git affiche un message d'erreur de la forme :
To [email protected]:2011/Project.git
! [rejected]
master -> master (non-fast forward)
error: failed to push some refs to ‘[email protected]:2011/Project.git’
Ce push est spécial, il transfère les nouveaux commits 3a4 et 83d sur le serveur puis tente de déplacer le pointeur b1 sur du commit cac vers le commit 83d. Mais quid du commit cac ? Il devient pendant ou dangling puisque référencé par aucune
branche. Cette opération implique donc une perte d'historique. Dans la terminologie git on appelle cela un push non fast-forward. Il est possible de forcer git à faire des push non fast-forward en utilisant git push avec l'option --force ou -f pour
spécifier que l'on est conscient de et d'accord avec une éventuelle perte d'historique. L'exécution de la commande suivante :
git push -f origin b1
nous amène donc dans l'état illustré par la figure 6. L'ancienne branche b1 est perdue et origin/b1 pointe désormais sur 83d.
Figure 6 : Synchronisation forcée (non fast-forward) avec du serveur
Les push non fast-foward sont donc nécessaires mais doivent être utilises avec certaines précautions. La première d'entre elles est que pour les raisons citées plus haut personne ne doit jamais faire un push non fast-forward sur la branche master.
On ne détruit pas l'historique sur lequel se sont appuyé les autres. Si un commit de master est erroné on utilise git revert <commit> qui introduit au sommet le commit dual. Plus généralement on ne fait pas de push non fast-forward sur des branches
où l'on travaille à plusieurs, ou on s'assure que les autres sont conscients que l'on va le faire.
Enfin on veut intégrer les changements de Bob dans master. Pour cela plusieurs solutions. Nous pouvons le faire en local et pusher sur le serveur :
git checkout master
git merge b1
git push origin master
Ici, la branche b1 étant au sommet de master aucun commit n'est introduit par le merge et master est simplement déplacée au sommet de b1. Cela s'appelle un merge fast-forward. On déplace ensuite la branche b1 du serveur (ainsi qu'origin/b1) au
sommet de b1 à l'aide du git push origin master.
On peut également faire évoluer la branche master distante dans un premier temps puis récupérer les changements :
git push origin b1:master
git pull origin master
La première commande synchronise les commits avec le serveur puis déplace la branche master distante au niveau de la branche b1 locale. La figure 7 présente l'état des dépôts local et distant après cette opération. On exécute ensuite la commande
git pull origin master pour mettre à jour la branche master locale avec la distante. La figure 8 présente l'état des dépôts après l'exécution de cette commande. Dans cette figure on a en plus supprimé les branches b1 locale et distante à l'aide des
commandes :
git branch -d b1
git push origin :b1
Dans les deux cas le push est fast-forward (pas besoin d'utiliser l'option -f) puisque nous avons rebasé notre branche sur master. On fait donc évoluer la branche master de deux commits sans perdre d'historique.
Figure 7 : Intégration des changements de b1 dans master sur le serveur
17/02/13 19:05
Tutoriel GIT
11 sur 11
http://www.moussu.fr/git/
Figure 8 : Suppression des branches et synchronisation avec la branche master distante
Notre branche b2 ayant été testée nous souhaitons maintenant intégrer les changements dans master. Problème : elle n'est plus au sommet de master. De la même manière que précédemment nous allons donc utiliser un rebase :
git rebase master b2
git push -f origin b2
Les commits de la branche b2 sont réappliqués au sommet de master. Et branche b2 distante est mise à jour à l'aide d'un push non fast-forward.
Figure 9 : Mise à jour de b2 sur le dépôt distant
Enfin on veut intégrer les changements de la branche b2 dans master. Pour cela on exécute les commandes :
git
git
git
git
push origin b2:master
branch -D b2
push origin :b2
pull origin master
La figure 10 montre une branche master, synchronisée avec le dépôt distant, qui contient tous les changements que l'on a voulu intégrer dans l'ordre que l'on a choisi. L'historique est linéaire et tous les détails relatifs aux branches sur lesquelles on a
développé telle ou telle fonctionnalité (ici considérés inutiles) en sont absents.
Figure 10 : Intégration de b2 dans master
Cet exemple décrit un mode de fonctionnement qui sera continuellement répété tout au long du développement de votre projet. Les différentes opérations décrites sont fondamentales et nous espérons que vous en avez maintenant compris toutes les
subtilités car vous allez en avoir besoin très prochainement. N'hésitez pas à nous poser des questions. Bon courage !
17/02/13 19:05

Documents pareils

1 Utilisation de Git sous RStudio - Informatique de MIA

1 Utilisation de Git sous RStudio - Informatique de MIA l’item revert. Cette opération annule les changements réalisés depuis le dernier commit. Remarque : cette opération est irréversible ! On peut aussi avoir besoin de revenir à la précédente version ...

Plus en détail