====== Git ======
{{tutoriaux:git-logo.png|}}
[[http://git.or.cz/|Git]] est un logiciel de gestion de versions décentralisées créé par Linus Torvalds.\\
Il est connut pour être le gestionnaire de version du [[http://kernel.org/|noyau Linux]] qui a remplacé la solution propriétaire [[http://en.wikipedia.org/wiki/Bitkeeper|Bitkeeper]].\\
Au début du projet, Git était tout d'abord pensé comme un gestionnaire de fichier puis à évolué pour intégrer toutes les fonctionnalités d'un gestionnaire de versions.\\
« //git// » est de l'argo anglais et se traduit par « //connard// ».\\
Quand il a été demandé à Torvalds ce que cela signifiait, il a répondu:\\
> //je ne suis qu'un égocentrique, donc j'appelle tous mes projets d'après ma propre personne. D'abord Linux, puis git.//
Ce tuto à pour but d'expliquer les commandes de base de Git et de donner des sources de documentation.\\
===== Système de fonctionnement =====
Git est un système de gestion de version décentralisé alors que [[http://en.wikipedia.org/wiki/Concurrent_Versions_System|CVS]] ou [[http://en.wikipedia.org/wiki/Subversion_%28software%29|SVN]] ont un fonctionnement centralisé autour d'un dépôt unique.\\
Avec Git, c'est comme si vous aviez un dépôt à part entière pour votre projet en local.\\
Cela signifie que avec votre dépôt git personnel, vous pouvez:\\
* Récupérer n'importe qu'elle version/révision du projet (''git-checkout'')\\
* Enregistrer vos changements dans ce dépot en tant que nouvelle révision (''git-commit'')\\
* Revenir sur une ancienne révision en cas d'erreur (''git-reset'')\\
* Consulter les changements effectués et l'historique (''git-log'')\\
* Gérer des branches de développement sans s'emmêler les versions (''git-branch'')\\
* Importer et envoyer les révisions entre dépôts Git (''git-pull'' & ''git-push'')\\
* Et plein d'autres fonctionnalités.. (''man git'')\\
En résumé, dans le cadre d'un développement collaboratif, chaque contributeur aura son dépôt Git personnel sur sa machine.\\
Il faudra donc par la suite diffuser les révisions entre dépôts et gérer les conflits.\\
:!: **Attention** :!: \\
A l'Éfrei la commande ''git'' est pour le moment (grossomodo) un lien symbolique vers la commande ''gitfm'' qui n'a pas grand chose à voir (gitfm - GNU Interactive Tools File Manager)\\
Faites attention sous Debian, il faut surement installer le packet "git-core" et non "git".
===== Commandes de base =====
Quand l'on installe git, on peut l'utiliser comme ceci:
$ git COMMANDE [arguments]
Ou comme ceci (tapez //git-// puis tabulez et vous verrez la flopée de commandes)
$ git-COMMANDE [arguments]
Cela revient au même au final, pas de panique.
Si vous voulez avoir de l'aide sur une commande en particulier:
$ man git-COMMANDE
La doc est bien faite, n'hésitez pas à en abuser si vous avez un doute.
==== Configuration ====
Pour faire les choses proprement, nous allons commencer par effectuer quelques configurations de base: identité, mail et éditeur favoris (vim dans l'exemple).
$ git config --global user.name "John the pwner"
$ git config --global user.email john@pwnage.net
$ git config --global core.editor vim
==== Creer le dépot Git ====
Nous allons initialiser un dépôt Git dans le répertoire d'un projet existant:\\
Commençons par télécharger et extraire le {{tutoriaux:42.tar.gz|projet 42}} de [[membres:John]]...
$ tar zxvf 42.tar.gz
$ cd 42
$ ls -la
Pour initialiser le dépôt git dans ce répertoire:
$ git init
Initialized empty Git repository in .git/
Le dossier .git est maintenant créé (et ça sera le seul même si le projet comporte des sous-répertoires).\\
Le dépôt ne comporte pas encore les fichiers du projet.\\
La commande ''git status'' nous renseigne rapidement sur l'état de tout fichier dans le dépôt:
$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add ..." to include in what will be committed)
#
# Makefile
# pied.c
nothing added to commit but untracked files present (use "git add" to track)
Nous allons donc obéir sans tarder et ajouter dans le cache les fichiers à commiter...
$ git add *
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached ..." to unstage)
#
# new file: Makefile
# new file: pied.c
Il nous reste maintenant plus qu'a effectuer notre premier commit:
$ git commit
Une fois dans l'éditeur, tapez une description. Ici mon peut taper: //Premier commit//\\
Ce qui commence par des # correspond à du commentaire et ne sera pas pris en compte. Enregistrez et quittez ...
Created initial commit 948ccbf: Premier commit
2 files changed, 18 insertions(+), 0 deletions(-)
create mode 100644 Makefile
create mode 100644 pied.c
Vous venez d'effectuer le premier commit de votre dépôt Git.
$ git status
# On branch master
nothing to commit (working directory clean)
==== Envoyer des changements ====
Nous allons maintenant effectuer quelques changements sur un fichier en envoyer cette nouvelle version dans le dépôt Git:
* Éditer le ficher que vous voulez:
$ echo "/* Pouette */" >> pied.c
* Ajoutez le fichier modifié au cache à commiter:
$ git add pied.c
* Vous pouvez regarder ce qui est sur le point d'être commité:
$ git diff --cached
* Commitez la nouvelle version et précisez vos modifications:
$ git commit
Created commit 9c1d111: Seconde version
1 files changed, 1 insertions(+), 0 deletions(-)
Git sait quels fichiers versionés ont été modifiés ou pas.
Si vous souhaitez commiter tous les fichiers modifiés au lieu de faire ''git add'' pour chaque fichier, vous pouvez utiliser l'option -a.
$ git commit -a -m "Seconde version"
L'option -m permet de préciser directement le contenu du message attaché au commit.
Bien pratique pour les petits commits ne nécessitant pas beaucoup de description mais de manière générale, prenez le temps d'expliquer avec soin vos modifications. Ca sera utile pour le prochain lecteur ou pour vous même dans le futur.
==== Annuler des changements ====
Par moment, ça arrive de faire une erreur et de vouloir revenir à une version precedente. On va utiliser ''git-reset'' pour effectuer ça.
Commençons par regrettablement supprimer/altérer fortement un fichier.
$ rm Makefile
Pour réinitialiser de manière brutale répertoire de travail ENTIER dans l'état de sa dernière version:
$ git-reset --hard
D'autres méthodes plus "soft" sont possibles (voir ''man git-reset'').
Imaginons que vous avez supprimé un seul fichier et que vous souhaitez récuperer sa dernière version sans alterer les autres changements:
$ git checkout fichier
==== Consulter l'historique ====
Pour consulter l'historique de votre dépôt, rien de plus simple:
$ git log
commit 9c1d1116c3a7c3c532de5e57950ddf89b678ee3d
Author: John the pwner
Date: Wed Jul 16 02:18:02 2008 +0000
Seconde version
commit 948ccbf8a130a2c5a36e81e280daaa8d67766afd
Author: John the pwner
Date: Wed Jul 16 00:03:27 2008 +0000
Premier commit
On constate que le nom des commit n'est pas franchement un numéro de révision mais un hash. On y retrouve surtout les commentaires déposés précédemment: quand et par qui.\\
''git-log'' permet de faire des sorties bien plus poussées et meme de générer des patchs.\\
Essayez aussi la commandes: ''git log -p'' pour voir les différences entre les commits
Plus d'infos avec ''man git-log''
Par la suite avec un projet un peu plus lourd comportant plusieurs branches, il sera plus souple d'utiliser des utilitaires comme ''gitview'' (qui est déjà dans le package git) ou ''gitk'' pour consulter les logs.\\
==== Nom des versions ====
Quelques mots sur le nom des versions:\\
Les hash sont pas très //human friendly// il donc existe d'autres possibilités pour désigner une version:
* ''git-show HEAD'' : Montre la dernière révision
* ''git-show HEAD^'' : Le parent de HEAD
* ''git-show HEAD^^'' : Le grand-parent de HEAD
* ''git-show HEAD~7'' : L' arrière-arrière-arrière-[...]-grand-parent de HEAD
* ''git-show 948ccbf8a130a2c5a36e81e280daaa8d67766afd'' : montre la révision ayant ce hash
* ''git-show 948c'' : montre la meme révision mais en ne spécifiant que le début du hash (à condition que ça soit le seul).
On peut aussi déposer un //tag// sur une version pour la retrouver facilement:
$ git tag v1.0 948c
$ git show v1.0
==== Gérer les branches ====
=== Création d'une simple branche ===
Les branches sont plutôt simples à gérer dans les périodes de non-conflit.
Par défaut, nous travaillons dans la branche "master".\\
Pour faire la liste des branches actuelles:
$ git branch
* master
Maintenant, créons une nouvelle branche "experimental" issue de la branche "master":
$ git branch experimental master
$ git branch
experimental
* master
L'étoile nous indique que nous travaillons toujours dans la branche master.\\
Comme nous voulons travailler dans la version experimentale:
$ git checkout experimental
Switched to branch "experimental"
$ git branch
* experimental
master
Nous sommes maintenant dans la branche experimentale mais nous ne voyons pas encore de changement puisque nous n'en avons pas encore fait.
Modifions un des fichiers de la branche experimentale:
$ echo "/* Experimentipouet */" >> pied.c
Puis effectuons un commit: (toujours dans la branche experimentale)
$ git commit -a -m "Commit de l'experimentipouet"
Created commit 3ab74aa: Commit de l'experimentipouet
1 files changed, 1 insertions(+), 0 deletions(-)
Repassons maintenant dans la branche master:
$ git checkout master
Switched to branch "master"
En examinant le fichier pied.c, nous constatons que nous sommes bien repassés dans la branche master et donc que les changements faits juste avant ne sont plus présents.\\
Si vous tentez d'examiner l'historique, vous verrez que ''git-log'' ne montre par défaut que les logs de branche actuelle.
=== Suppression d'une branche ===
Pour supprimer la branche experimentale:
$ git branch -D experimental
Deleted branch experimental.
(pensez à ne pas scier la branche sur laquelle vous travaillez)\\
=== Création d'une branche à partir d'une version ===
Nous pouvons également créer une branche à partir d'une révision précise (ici, la version HEAD). Dans l'exemple, nous réalisons un checkout de la dernière révision (HEAD) en tant que nouvelle branche appelée "exp":
$ git-checkout -b exp HEAD
Switched to a new branch "exp"
=== Fusion de branches ===
Nous allons maintenant faire un exemple rapide d'un merge de branches sans conflit:
* Commitons une nouvelle version dans la branche experimentale:
$ git branch
* exp
master
$ echo "/* Niii */" >> pied.c
$ git commit -a -m "Commit experimental"
Created commit 380071d: Commit experimental
1 files changed, 1 insertions(+), 0 deletions(-)
* Retournons dans la branche master:
$ git checkout master
* Mergons la branche experimentale avec la branche master:
$ git merge exp
Updating 9c1d111..380071d
Fast forward
pied.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
En résumé les branches sont bien pratiques et plutôt simple à utiliser.\\
Nous verrons par la suite comment gérer des conflits potentiels.
==== Travail collaboratif ====
Comme chaque développeur va avoir son dépôt Git, il va falloir partager tous les commits effectués.\\
Pour cela, on peut choisir entre 3 méthodes de travail:
* Un dépôt central en libre accès aux contributeurs
* Un dépôt central restreint où seul quelques mainteneurs autoriseront des modifications
* Des dépôts décentralisés
=== Récupérer un dépôt distant ===
Clonons notre dépôt et appelons la copie bob:
$ cd ..
$ git clone ./42 ./bob
Initialized empty Git repository in /home/john/Desktop/bob/.git/
0 blocks
La commande a crée un nouveau répertoire "bob" contenant un clone du dépôt d'origine.\\
Ici notre dépôt distant n'est pas très "loin" mais on peut aussi utiliser cette commande via ssh, rsync, git, http, https\\
(voir ''man git-clone'')\\
On peut consulter la liste des dépôts suivis avec la commande ''git-remote'' :
$ git-remote
origin
$ git-remote show origin
* remote origin
URL: /home/john/Desktop/42/.git
Remote branch(es) merged with 'git pull' while on branch master
master
Tracked remote branches
master
=== Metre à jour son dépôt ===
Commençons par faire quelques modifications dans le dépôt d'origine:
$ cd ./42
$ echo "/* Artung */" >> pied.c
$ git commit -a -m "Commit dans le depot d'origine"
Retournons dans le dépôt bob et effectuons une mise à jour:
$ cd ../bob
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/john/Desktop/42/
380071d..306d664 master -> origin/master
Updating 380071d..306d664
Fast forward
pied.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
En regardant ''git log'', nous avons bien récupéré le commit effectué dans le dépôt d'origine.
Cela merge les changement de la branche master du dépôt d'origine avec la branche actuelle du dépôt bob (qui est la branche master également).
=== Pousser des modifications dans un dépôt distant ===
C'est la même opération dans le sens inverse:
Commitons une nouvelle version dans le dépot bob:
$ echo "/* j'aime les frittes */" >> pied.c
$ git commit -a -m "Commit du depot bob"
Puis envoyons ces changements dans le dépôt d'origine:
$ git push
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 325 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/john/Desktop/42/.git
306d664..7887012 master -> master
=== Ajouter un dépôt à surveiller ===
Dans le cas d'un travail collaboratif sans dépôt central, il faut ajouter les dépôts des autres contributeurs.
Commençons par ajouter un autre dépôt "ni" à coté de "bob" et "42":
$ cd ..
$ git clone ./42 ./ni
$ cd ./ni
Ajoutons le dépot bob à surveiller:
$ git remote add bob /home/john/Desktop/bob
$ git remote
bob
origin
Effectuons des changements dans le dépôt de bob et dans le dépôt 42 sans provoquer de conflits:
$ cd ../bob
$ echo -e "clean:\n\trm pied" >> Makefile
$ git commit -a -m "Makefile modif"
$ cd ../42
$ echo "/* grom */" >> pied.c
$ git commit -a -m "Gromification dans pied.c"
Maintenant, retournons dans le dépôt ni et allons chercher les mises à jours:
$ cd ../ni
$ git pull origin master
$ git pull bob master
Nous avons maintenant les changements du dépôt bob et 42 dans le dépot ni.
==== Gérer les conflits ====
Nous allons présenter quelques exemples de conflits et montrer par qu'elle manière nous les avons résolus.
=== Conflit au merge de deux branches ===
Commençons par créer deux branches "Snarf" et "Opossum":
$ git-checkout -b Opossum HEAD
$ git-checkout -b Snarf HEAD
Nous sommes actuellement dans la branche Snarf, commitons dans les deux branches des changements conflictuels:
$ echo "/* Snarf, c'est plus Snarf que toi */" >> pied.c
$ git commit -a -m "Just Snarf it"
$ git-checkout Opossum
$ echo "/* Le Possum aime le poulet */" >> pied.c
$ git-commit -a -m "Possum commit"
Et maintenant, le moment testostérock tant attendu: le merge de Snarf et de Opossum
$ git merge Snarf
Auto-merged pied.c
CONFLICT (content): Merge conflict in pied.c
Automatic merge failed; fix conflicts and then commit the result.
$ git status
pied.c: needs merge
[...]
Similairement à svn, le fichier pied.c est la version "mélangée" entre la branche Snarf et Opossum.\\
Editez le fichier, faites vos choix entre les deux version, enregistrez et quittez. Après ça, commitez votre nouvelle version mergée.
$ vim pied.c
[...]
$ git add pied.c
$ git commit
Created commit 863783e: Merge branch 'Snarf'
(private joke: on verra bien les logs d'ici quelques mois =) )
=== Conflit lors d'un pull/push ===
Allons dans le dépot 42 et effectuons un commit:
$ echo "/* Commit 42 */" >> pied.c
$ git commit -m "Commit 42" -a
Puis Allons dans le dépôt bob mais effectuons un commit sur la même ligne ...
$ echo "/* Commit bob */" >> pied.c
$ git commit -m "Commit bob" -a
Pas de soucis pour le moment, effectuons maintenant un pull..
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/john/Desktop/42/
06e524e..f8a9780 master -> origin/master
Auto-merged pied.c
CONFLICT (content): Merge conflict in pied.c
Automatic merge failed; fix conflicts and then commit the result.
Tout comme le conflit entre branches, les fichiers à merger sont un mixe des deux versions.\\
Ouvrez les fichiers concernés et choisissez la version correct à choisir...\\
Une fois toutes les modifications apportés, commitez la nouvelle version..
$ vim pied.c
[...]
$ git add pied.c
$ git commit
Created commit 1b6ef3f: Merge branch 'master' of /home/john/Desktop/42/
La résolution d'un conflit pendant un pull se déroule (presque) de la même manière:
* L'édition du fichier à merger lors du conflit
* Commitez les changements dans son dépôt
* Retenter de pousser la version mergée.
===== Quelques utilitaires =====
[[http://sourceforge.net/projects/gitview|gitview]] : le viewer graphique intégré dans le package git\\
[[http://cola.tuxfamily.org/|Git-Cola]]: Une interface complète pour git.\\
Gitweb: Une interface web de base pour les dépôts git, demo sur [[http://git.kernel.org/]].\\
[[http://www.gitready.com/advanced/2009/07/31/tig-the-ncurses-front-end-to-git.html|Tig]]: Interface en ncurses pour naviguer dans un dépôt git.\\
[[http://hjemli.net/git/cgit/|cgit]]: Une interface web rapide (alternative à gitweb)\\
[[http://projects.ceondo.com/p/indefero/|Indefero]]: Un projet de site web vous permettant d'heberger plusieurs projets git, gérer les utilisateurs, la doc, les bugs, etc... Une copie version libre de google code (alternative à trac mais qui gère git, svn et mercurial).\\
[[https://dev.launchpad.net/|Launchpad]]: Un service d'hebergement de projets qui se repose sur une interface web complète. Vous pouvez vous inscrire sur le site ou bien utiliser librement le code pour refaire cette plateforme chez vous.\\
[[http://gitorious.org/gitorious|Gitorious]]: De meme, Gitorious propose d'héberger les projets libres et le code de base de la plateforme est libre.
===== Sources et Documentations =====
[[http://git-scm.com/|Site officiel du projet Git]]\\
[[http://fr.wikipedia.org/wiki/Git|Article Wikipedia sur Git]]\\