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 noyau Linux qui a remplacé la solution propriétaire 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.
Git est un système de gestion de version décentralisé alors que CVS ou 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:
git-checkout)git-commit)git-reset)git-log)git-branch)git-pull & git-push)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”.
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.
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
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 projet 42 de John the pwner…
$ 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 <file>..." 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 <file>..." 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)
Nous allons maintenant effectuer quelques changements sur un fichier en envoyer cette nouvelle version dans le dépôt Git:
$ echo "/* Pouette */" >> pied.c
$ git add pied.c
$ git diff --cached
$ 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.
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
Pour consulter l'historique de votre dépôt, rien de plus simple:
$ git log commit 9c1d1116c3a7c3c532de5e57950ddf89b678ee3d Author: John the pwner <john@pwnage.net> Date: Wed Jul 16 02:18:02 2008 +0000 Seconde version commit 948ccbf8a130a2c5a36e81e280daaa8d67766afd Author: John the pwner <john@pwnage.net> 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.
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évisiongit-show HEAD^ : Le parent de HEADgit-show HEAD^^ : Le grand-parent de HEADgit-show HEAD~7 : L' arrière-arrière-arrière-[…]-grand-parent de HEADgit-show 948ccbf8a130a2c5a36e81e280daaa8d67766afd : montre la révision ayant ce hashgit-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
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.
Pour supprimer la branche experimentale:
$ git branch -D experimental
Deleted branch experimental.
(pensez à ne pas scier la branche sur laquelle vous travaillez)
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"
Nous allons maintenant faire un exemple rapide d'un merge de branches sans conflit:
$ 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(-)
$ git checkout 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.
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:
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
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).
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
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.
Nous allons présenter quelques exemples de conflits et montrer par qu'elle manière nous les avons résolus.
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
)
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:
gitview : le viewer graphique intégré dans le package git
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/.
Tig: Interface en ncurses pour naviguer dans un dépôt git.
cgit: Une interface web rapide (alternative à gitweb)
Indefero: Un projet de site web vous permetant 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).