Mise en place d'un serveur et client Git
Contents
- 1 Introduction
- 2 Installation
- 3 Configuration
- 4 Utilisation
- 4.1 Mettre à jour son dépôt local
- 4.2 Ajouter des fichiers
- 4.3 Commiter les changements
- 4.4 Changer l'éditeur et le visualiseur par défaut
- 4.5 Obtenir les logs
- 4.6 Faire une recherche
- 4.7 Ignorer des fichiers
- 4.8 S'assurer qu'il n'y a rien à commiter
- 4.9 Configurer des alias
- 4.10 Revert : annuler un commit
- 4.11 Reset : effacer tous les commits à partir d'un ancien
- 4.12 Restaurer un fichier d'un ancien commit
- 4.13 Supprimer un fichier
- 4.14 Déplacement d'un fichier
- 4.15 Lister le contenu du dépôt
- 4.16 Créer une archive du dépôt
- 4.17 Voir un fichier en particulier d'un commit
- 4.18 Réécrire l'histoire
- 4.19 Faire des diff entre différentes versions
- 4.20 Modifier le texte du dernier commit
- 4.21 Modifier la date du dernier commit
- 4.22 Modifier l'autheur de commits
- 4.23 Synchroniser qu'une partie d'un dépôt
- 4.24 Utiliser un repository git externe dans un git
- 4.25 Les branches
- 4.25.1 Création d'une branche
- 4.25.2 Envoyer une branche au serveur
- 4.25.3 Lister des branches
- 4.25.4 Changement de branche
- 4.25.5 Récupérer une branche depuis un serveur distant
- 4.25.6 Supprimer une branche
- 4.25.7 Merger des branches
- 4.25.8 Rebase : Appliquer un patch sur plusieurs branches
- 4.25.9 Créer une branche lors d'une restauration
- 4.26 Les Tags
- 4.27 Gérer les conflits
- 4.28 Résolution automatique de conflits déjà vu
- 4.29 Utiliser git pour trouver un bug
- 4.30 Connaître ligne par ligne les commits
- 4.31 Déplacer un dossier et son historique vers un autre repository
- 4.32 Hooks
- 5 Utilitaires
- 6 FAQ
- 7 Références
1 Introduction
Git est un logiciel de gestion de versions décentralisé. C'est un logiciel libre créé par Linus Torvalds, le créateur du noyau Linux, et distribué sous la GNU GPL version 2.
Comme BitKeeper, Git ne repose pas sur un serveur centralisé. C'est un outil bas niveau, qui se veut simple et très performant, dont la principale tâche est de gérer l'évolution du contenu d'une arborescence.
Git indexe les fichiers d'après leur somme de contrôle calculée avec la fonction SHA-1. Quand un fichier n'est pas modifié, la somme de contrôle ne change pas et le fichier n'est stocké qu'une seule fois. En revanche, si le fichier est modifié, les deux versions sont stockées sur le disque.
Git n'était pas, au départ, à proprement parler un logiciel de gestion de versions. Linus Torvalds expliquait que, « par bien des aspects, vous pouvez considérer git comme un système de fichiers : il permet un adressage associatif, et possède la notion de versionnage, mais surtout, je l'ai conçu en résolvant le problème du point de vue d'un spécialiste des systèmes de fichiers (mon métier, ce sont les noyaux !), et je n'avais absolument aucun intérêt à créer un système de gestion de version traditionnel. ». Il a aujourd'hui évolué pour intégrer toutes les fonctionalités d'un gestionnaire de versions.
Git est considéré comme performant, au point que certains autres logiciels de gestion de version (Darcs, Arch), qui n'utilisent pas de base de données, se sont montrés intéressés par le système de stockage des fichiers de Git pour leur propre fonctionnement. Ils continueraient toutefois à proposer des fonctionnalités plus évoluées.
2 Installation
Tout d'abord sur le serveur, nous allons installer Git :
aptitude |
aptitude install git git-core |
Si vous voulez rendre le serveur accessible via une adresse en git://, il faut également installer ceci :
aptitude |
aptitude install git-daemon-run |
3 Configuration
3.1 Serveur
Tout d'abord, nous allons créer le repository :
$ mkdir myproject $ cd myproject $ git init Initialized empty Git repository in .git/ |
Maintenant que notre projet est créer, nous allons lui ajouter un petit fichier histoire d'être sûr que tout fonctionne correctement :
touch |
touch test |
Puis nous allons l'ajouter, puis le commiter :
git |
$ git add . $ git commit -m "test" Created initial commit c491bd6: test 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 afile |
Pour vérifier l'état à n'importe quel moment, vous allez effectuer un status :
git |
git status |
Ensuite, nous clonons ce repository :
- clone : permet de créer une copie du dépôt en local
- --bare : copie ne contenant que les infos du dossier myproject
- myproject.git : dossier que git nous a créer
3.1.1 Le protocole git
Si vous souhaitez rendre accessible git via son protocole (git://) vous devez configurer le fichier suivant comme ceci :
/etc/sv/git-daemon/run |
#!/bin/sh exec 2>&1 echo 'git-daemon starting.' exec chpst -ugitdaemon \ "$(git --exec-path)"/git-daemon --verbose --base-path=/var/cache /var/cache/gi |
Ensuite pour chacun de vos projets que vous voulez rendre accessible depuis l'extérieur, il faudra placer un fichier 'git-daemon-export-ok' :
touch |
touch /var/cache/git/myproject.git/git-daemon-export-ok |
Relancer le deamon et c'est maintenant accessible sur le port 9418.
3.2 Client
Vous devez avant de commencer installer également Git, comme effectué sur le serveur.
Ensuite avec votre utilisateur courant, informez lui que vous voulez commiter (assurez vous que votre utilisateur existe bien sur le serveur) :
git |
git config --global user.email "deimos@deimos.fr" git config --global user.name "deimosfr" |
Ce qui devrait donner ceci dans votre ~/.gitconfig :
~/.gitconfig |
[user] email = deimos@deimos.fr name = Deimos |
Si vous souhaitez avoir des identifiants différents pour un repository différent, il faudra simplement retirer global (en vous plaçant dans le repository en question) :
git |
git config user.email "deimos@deimos.fr" git config user.name "Deimos" |
Maintenant nous allons récupérer notre projet sur notre git. Nous utilisons ici la méthode avec ssh, mais git sait également fonctionner sur rsync, http, https et git-daemon.
3.2.1 Avec SSH
Voici la méthode avec SSH :
- Pour les mises à jour du dépôt, il va falloir utiliser l'option push. Sachez qu'avant, les modifications devront être effectuées en locale (c'est l'avantage de git), je parle donc d'un add et d'un commit par exemple (nous le verrons juste après) :
git |
git push ssh://username@host/var/cache/git/myproject.git ou git push ssh://username@host/var/cache/git/myproject.git master |
'master' ici correspond au nom de la branche qui nous intéresse (voir plus bas pour l'utilisation des branches).
- Pour ajouter un fichier à un dépôt SSH :
git |
git remote add test ssh://username@host/var/cache/git/myproject.git git commit -a git push test |
A l'avenir, afin d'éviter de se prendre la tête avec la partie SSH et ses commandes spéciales, ajoutez ceci dans votre fichier ~/.gitconfig :
[remote "myproject"] url = ssh://username@host/var/cache/git/myproject/ |
3.2.2 Avec Git-daemon
Git refusera de se synchroniser si le dossier en question ne contient pas un fichier appeller 'git-daemon-export-ok'. Une fois ce fichier créer, le dossier en question sera accessible par tout le monde :
git |
git clone git://serveur.git/git/myproject ou git clone git://serveur.git/git/myproject.git |
Nous pouvons utiliser les options suivantes :
- '--export-all' : cette option n'oblige plus à créer le fichier 'git-daemon-export-ok'
- '--user-path=gitexport' : cette option va permettre d'utiliser des URL sur les dossiers d'accueil des utilisateurs. Ainsi git://deimos-laptop/~deimos/myproject.git va pointer sur /home/deimos/gitexport/myproject.git
3.2.3 Sur HTTP (avec Nginx)
J'ai passé pas mal de temps à faire cohabiter Git over http(s) et Gitweb, mais ça y est ça fonctionne.
Notes |
Préférez la méthode Gitweb uniquement si vous n'avez pas besoin de de git over http(s) |
Voici la méthode que j'ai utilisé :
Ici j'ai mon git over https qui fonctionne (le http est redirigé vers https) et mon gitweb également puisque tout ce qui est gitweb.cgi est matché. Maintenant, pour la partie git, nous allons devoir autoriser les repository que nous voulons. Pour celà, il va falloir renommer un fichier dans notre repository et lancer une commande :
cd /var/cache/git/myrepo.git hooks/post-update{.sample,} su - www-data -c 'cd /var/cache/git/myrepo.git && /usr/lib/git-core/git-update-server-info' |
Remplacez www-data par l'utilisateur qui a les droits sur le repository. Utilisez www-data pour que ce soit nginx qui ait les droits. Ensuite, vous avez les droits pour cloner :
git |
git clone http://www.deimos.fr/git/deimosfr.git deimosfr |
3.2.4 Configurer un proxy
Si vous devez passer à travers un proxy sur Git, vous pouvez le faire comme ceci (pour sa partie globale) :
git |
git config --global http.proxy http://proxy:8080 |
Pour vérifier les paramètres actuels :
git |
git config --get http.proxy |
Si vous avez besoin de retirer cette configuration :
git |
git config --global --unset http.proxy |
4 Utilisation
4.1 Mettre à jour son dépôt local
Si vous souhaitez mettre à jour votre dépôt local par rapport au serveur, c'est simple :
git |
git pull ou git pull ssh://username@host/var/cache/git/myproject/ |
4.2 Ajouter des fichiers
Pour ajouter des fichiers en masse, utilisez cette commande :
git |
git add * |
4.3 Commiter les changements
Si vous souhaitez mettre en même temps un log et commiter, le tout en une seule ligne (sinon enlevez -m...) :
git |
git commit -a -m "Mon premier ajout" |
Ceci va donc mettre à jour en local le repository (n'oubliez pas le push ensuite si vous travaillez avec un serveur distant).
4.4 Changer l'éditeur et le visualiseur par défaut
Vous pouvez forcer par exemple vim et less and entrant ces lignes :
git |
git config --global core.editor vim git config --global core.pager "less -FSRX" |
4.5 Obtenir les logs
Vous pouvez consulter à n'importe quel moment les logs des anciens commits via cete commande :
git |
git log |
Si vous souhaitez obtenir les logs d'un fichier bien précis :
git |
git log test |
Si votre fichier a été renommé, utilisez l'option --follow pour vois également son nouveau nom.
Pour voir les logs des 2 derniers jours :
git |
git log --since="2 days ago" |
4.6 Faire une recherche
Il est possible de faire une recherche de contenu dans les fichiers comme ceci :
git |
git grep deimos * |
4.7 Ignorer des fichiers
Vous avez peut être envie de ne pas autoriser certains fichiers tel que les fichiers temporaire de vim par exemple. Il va vous falloir placer un fichier .gitignore qui sera à la racine de la copie de travail qui sera en général ajouté lui même au dépôt, soit dans le fichier .git/info/exclude. Voici un exemple :
.gitignore |
*~ *.o |
4.8 S'assurer qu'il n'y a rien à commiter
Il est possible de s'assurer qu'il n'y a rien a commiter de cette façon là :
git |
> git stash No local changes to save |
4.9 Configurer des alias
Il est possible de configurer des alias pour les commandes un peu longues. Par exemple si vous êtes habitué à SVN :
git |
git config --global alias.co 'checkout' git config --global alias.ci 'commit -a -m' |
Ici 'git co' correpond à la commande 'git checkout'. Cette configuration (si elle est globale ou non) peut être située à 2 endroits :
- ~/.gitconfig pour en bénéficier dans tous vos dépôts.
- .git/config d’un projet pour restreindre son accès à cet unique projet.
4.10 Revert : annuler un commit
Git revert permet d'annuler le précédent commit en en introduisant un nouveau. Tout d'abord nous allons faire un git log pour récupérer le numéro du commit :
git |
$ git log commit : bfbfefa4d9116beff394ad29e2721b1ab6118df0 |
Puis nous allons faire un revert pour annuler l'ancien :
git |
git revert bfbfefa4d9116beff394ad29e2721b1ab6118df0 |
En cas de conflit, il suffit de faire les modifications à la main, de refaire un add puis un commit.
Note : vous pouvez aussi utiliser les quelques premiers caractère de l'identifiant (SHA-1) du commit tel que bfbfe. Git fera automatiquement la completion (a la condition qu'un autre commit ne commence pas avec les mêmes caractères).
4.11 Reset : effacer tous les commits à partir d'un ancien
Git reset va supprimer complètement tous les commits effectués après une certaine version :
git |
git reset bfbfefa4d9116beff394ad29e2721b1ab6118df0 |
Git reset permet de réécrire l'histoire. Vous pourrez vous en convaincre avec un 'git log'. Celà peut avoir de grave conséquences si vous travaillez à plusieurs sur un projet. Non seulement parce que les repository en locale des autres personnes vont devoir être resynchroniser, mais en plus parce qu'il peut y avoir beaucoup de conflits. Il est donc conseiller d'utiliser cette commande avec précaution.
Ensuite vous devez refaire un 'git add' de vos fichiers, puis un 'git commit'.
Si vous voulez que la copie de travail en local reflète le dépôt, ajoutez l'option --hard lors du reset :
git |
git reset --hard bfbfefa4d9116beff394ad29e2721b1ab6118df0 |
Dans le cas ou vous êtes dans une salle journée et donc en mode "boulet", si vous avez supprimé des mois de taf et que vous voulez revenir sur vos pas, git a conservé l'état précédent, faites donc :
git |
git reset --hard ORIG_HEAD |
HEAD signifie le dernier commit par défaut.
Si vous avez un message du type :
! [rejected] master -> master (non-fast forward)
Et que vou voulez vraiment forcer le commit pour qu'il ressemble exactement à ce que vous avez sur votre machine :
git |
git push origin +master |
4.12 Restaurer un fichier d'un ancien commit
Vous pouvez choisir de restaurer uniquement un fichier bien particulier d'un ancien commit via la commande checkout :
git |
git checkout bfbfe test |
4.13 Supprimer un fichier
Il existe 2 méthodes, ma préférée, car je la trouve simple :
git |
git rm mon_fichier |
La 2ème méthode consite à faire un rm standart, puis ua prochaine git commit -a, il détectera que ce fichier n'existe plus et le supprimera du dépôt.
4.14 Déplacement d'un fichier
Pour déplacer un fichier au sain de son dépôt git, il faut utiliser cette commande :
git |
git mv test1 test2 |
4.15 Lister le contenu du dépôt
On peut utiliser cette commande pour lister le contenu d'un dépôt :
git |
git ls-files |
Note : les dossiers vide ne seront pas afficher
4.16 Créer une archive du dépôt
Pour faire une archive au format tar gzip, nous allons utiliser l'option archive :
git |
git archive --format=tar --prefix=version_1/ HEAD | gzip > ../version_1.tgz |
- --prefix : permet de donner un dossier qui sera créer lors de la décompression.
- HEAD : permet de spécifier le commit (ici le 1er)
4.17 Voir un fichier en particulier d'un commit
Il est possible de voir un fichier (README) d'un commit :
git |
git show 291d2ee31feb4a318f77201dea941374aae279a5:README |
ou voir tous les diffs d'un coup si on ne spécifie pas le fichier :
git |
git show 291d2ee31feb4a318f77201dea941374aae279a5 |
4.18 Réécrire l'histoire
Si vous avez oublié de faire quelque chose à un précédent commit et que vous souhaitez revenir en arrière, il faut faire un rebase interactif en indiquant le commit le plus loin que l'on souhaite modifier:
git |
git rebase -i cbde26ad^ |
In the default editor, modify 'pick' to 'edit' in the line whose commit you want to modify. Make your changes and then commit them with the same message you had before: Dans l'éditeur, modifiez 'pick' en 'edit' sur la ou les lignes que vous souhaitez modifier. Enregistrez et quittez. Faites tous vos changements et ensuite validez le commit:
git |
git commit -a --amend ou git commit -a --amend --no-edit |
Ensuite, pour passer au commit suivant:
git |
git rebase --continue |
Jusqu'à ce que ce soit terminé.
4.19 Faire des diff entre différentes versions
Il est très facile avec git de faire des diff entre différentes versions. Par exemple, de la version HEAD à 2 versions précédentes :
git |
git diff HEAD~2 HEAD |
4.20 Modifier le texte du dernier commit
Pour voir l'état du dernier log, nous pouvons faire :
git |
git log -n 1 |
Pour modifier le texte de ce dernier log, nous allons utiliser l'option amend :
git |
git commit -a --amend |
Cela permet donc de changer juste le texte, il n'y aura pas de nouveau numéro commit. Seul le numéro d'identification changera.
4.21 Modifier la date du dernier commit
Il est possible de modifier la date du dernier commit:
git |
GIT_COMMITTER_DATE="`date`" git commit --amend --date "`date`" |
Ou sinon on peut spécifier la date en dur:
git |
GIT_COMMITTER_DATE="Fri Nov 17 12:00:00 CET 2014" git commit --amend --date "Fri Nov 17 12:00:00 CET 2014" |
4.22 Modifier l'autheur de commits
Lorsque l'on utilise plusieurs comptes Git sur une même machine, il arrive facilement de se tromper lorsque l'on clone un repository et que l'on ne met pas le bon username et adresse mail. Du coup lorsque l'on s'en rend compte, il est trop tard et il faut réécrire une partie d l'histoire pour remettre les bonnes informations:
4.23 Synchroniser qu'une partie d'un dépôt
Si vous souhaitez n'avoir qu'une partie d'un dépôt (également appelé sparse), vous allez devoir en premier lieu récupérer le dépôt dans son intégralité, puis lui spécifier que ce qui vous intéresse. Il ne restera alors que ce qui vous intéresse :
git clone ssh://deimos@git/var/cache/git/git_deimosfr . git config core.sparsecheckout true echo configs/puppet/ > .git/info/sparse-checkout git read-tree -m -u HEAD |
Ici dans le dépôt git_deimosfr, seul le dossier "configs/puppet/" ne m'intéresse. Vous pouvez ajoutez plusieurs dossiers dans le fichier ".git/info/sparse-checkout" ligne par ligne si vous souhaitez conserver plusieurs fichiers.
4.24 Utiliser un repository git externe dans un git
Si par exemple vous avez déjà un Git dans lequel vous voulez intégrer un autre Git, mais externe, il vous faudra utiliser les submodules. Pour ma part, j'ai un dossier puppet avec tous pleins de modules dedans. Une partie de ces modules n'a pas été créer par moi même et est maintenu par d'autres personnes ayant un Git. Je souhaite faire pointer certains modules vers les git externe. Voici comment précéder :
- J'ajoute la source externe à ce git :
git |
> git submodule add git://git.black.co.at/module-common configs/puppet/modules/common Cloning into configs/puppet/modules/common... remote: Counting objects: 423, done. remote: Compressing objects: 100% (298/298), done. remote: Total 423 (delta 155), reused 203 (delta 70) Receiving objects: 100% (423/423), 53.96 KiB, done. Resolving deltas: 100% (155/155), done. |
Il ne vous reste plus qu'a commiter et push si vous voulez rendre cette configuration valable sur le serveur.
- Maintenant prenons le cas d'un client qui fait un pull. Il va récupérer toute votre arborescence, mais pas les liens externes, pour cela, il devra exécuter les commandes suivantes :
git |
> git submodule init Submodule 'configs/puppet/modules/common' (git://git.black.co.at/module-common) registered for path 'configs/puppet/modules/common' > git submodule update Cloning into configs/puppet/modules/common... remote: Counting objects: 423, done. remote: Compressing objects: 100% (298/298), done. remote: Total 423 (delta 155), reused 203 (delta 70) Receiving objects: 100% (423/423), 53.96 KiB, done. Resolving deltas: 100% (155/155), done. Submodule path 'configs/puppet/modules/common': checked out 'ba28f3004d402c250ef3099f95a1ae13740b009f' |
Si lors d'un clone, vous souhaitez que les submodules soient également prit avec, vous devez rajouter l'option "--recursive". Exemple :
git |
git clone --recursive git://git.deimos.fr/git/git_deimosfr |
4.25 Les branches
4.25.1 Création d'une branche
Si par exemple, la version actuelle que vous avez sur git vous convient et que vous souhaitez la passer en stable, il vous faut utiliser les branches et alors créer une nouvelle branche en tant que branche de développement :
git |
git branch devel |
4.25.2 Envoyer une branche au serveur
Pour envoyer une branche au serveur :
git push origin <ma_branche> |
4.25.3 Lister des branches
Maintenant, la branche devel est créer, pour le vérifier :
git |
$ git branch -a devel * master |
L''*' signifie la branche courante (en cours d'utilisation).
Lorsque vous utilisez des branches distantes, vous pouvez les lister via cette option :
git |
$ git branch -r devel * master |
4.25.4 Changement de branche
Pour changer de branche, il suffit d'utiliser l'option checkout :
git |
$ git checkout devel Switched to branch "devel" |
Vérifions :
git |
$ git branch * devel master |
Si l'on veut repasser sur la branche précédente, nous pouvons à n'importe quel moment faire :
git |
$ git checkout -f master $ git branch * master devel |
L'option -f correspond à l'option --hard du reset qui permet de synchroniser en local les changements effectués sur le dépôt.
Si vous travaillez sur une branche distante, faites comme ceci par exemple :
git |
$ git checkout deimos/myproject -b master $ git branch * master devel |
4.25.5 Récupérer une branche depuis un serveur distant
Une fois que votre "pull" est fait, changez de branche comme ceci (vous pouvez la nommer différemment si vous le souhaitez) :
git |
git checkout -b <new_branch> origin/<new_remote_branch> |
- new_branch : le nom de ma nouvelle branche locale
- new_remote_branch : le nom de la nouvelle branche distante (côté serveur)
4.25.6 Supprimer une branche
Pour supprimer une branche, rien de plus simple :
git |
git branch -d devel |
Si celà fonctionne parfait, si vous souhaitez forcer en cas de problème, utiliser '-D' à la place de '-d'. Pour appliquer les changements sur le serveur remote :
git |
git push origin :devel |
Ceci supprimera la branche devel sur le serveur distant.
4.25.7 Merger des branches
L'option merge consiste à fusionner 2 branches complètement. Comme par exemple faire fusionner une branche devel avec une stable pour que la devel devienne stable :
git |
git checkout master git merge devel |
4.25.8 Rebase : Appliquer un patch sur plusieurs branches
Imaginons que nous avons travailler sur la branche devel mais qu'un bug en master a été découvert. Il est normal que si le master bénéficie du fix, la branche devel également. Nous allons donc nous placer dans la branche master, appliquer le patch, puis merger avec la branche devel le patch. On se place donc sur la branche devel, puis on exécute ces commandes :
git |
git checkout devel git rebase master |
Il est possible de le faire de façon interactive grâce à l’argument -i :
git rebase -i master |
4.25.9 Créer une branche lors d'une restauration
Vous pouvez créer une branche lors d'une restauration d'un fichier en vous plaçant préalablement dans la branche et en utilisant l'option -b :
git |
$ git checkout bfbfefa4d9116beff394ad29e2721b1ab6118df0 -b version_2 Switched to a new branch "version_2" |
4.26 Les Tags
Les tags sont très pratiques pour marquer une version bien spécifique. Par exemple, pour releaser une version 1.0, il est possible de créer un tag et de s'en resservir par la suite pour télécharger cette version particulière. On peut tagger ce que l'on veut, ça permet de se repérer facilement.
4.26.1 Créer un Tag
Pour créer un tag, c'est facile :
git |
git tag '1.0' |
Notes |
Il est possible de signer les tag via GPG |
4.26.2 Lister les tags
Pour lister les tags disponibles :
git |
git tag |
4.26.3 Supprimer un tag
Pour supprimer un tag:
git |
git tag -d v0.1 git push origin :refs/tags/v0.1 |
4.26.4 Envoyer les tags sur le serveur
Une fois les tags créés en local, il est possible de les pousser sur le serveur:
git push --tags |
4.27 Gérer les conflits
Il est possible de gérer les conflits de façon interactive :
git |
git mergetool |
4.28 Résolution automatique de conflits déjà vu
Il est possible de demander à git de résoudre automatiquement des problèmes sur lesquels il serait déjà tombé :
git |
git config --global rerere.enabled 1 |
4.29 Utiliser git pour trouver un bug
Imaginons qu’un bug sournois surgisse soudainement et vienne perturber la bonne marche de votre projet. Après quelques investigations dignes d’un fumeur de pipe anglais, vous découvrez que le vil dysfonctionnement n’est apparu que récemment. Il s’agit bien là d’une régression !
Certes, vous pourriez perdre du temps à corriger cette régression de la manière classique, en plongeant dans le code poussiéreux et plein de toiles d’araignées virtuelles, mais il serait bien plus simple de découvrir exactement à quelle moment elle est apparue, quel est le commit correspondant, pour avoir une idée très précise de sa cause.
C’est exactement ce que permet de faire git-bisect, d’une manière particulièrement intuitive. Indiquez lui un commit présentant le dysfonctionnement (HEAD), et un commit passé, n’importe lequel, qui ne la présente pas.
Git vous place alors dans votre historique de développement, pile au milieu entre les deux commit. Après vérification, vous indiquez à git si la régression apparaît ou pas. Et on recommence. Chaque fois, on divise par 2 l’intervalle des commits ayant potentiellement introduit le bug, jusqu’à débusquer le fautif. Génial, non ?
Passons à la pratique :
> git bisect start > git bisect bad <mauvaise_version> > git bisect good <bonne_version> Bisecting: 8 revisions left to test after this |
8 commits ont potentiellement introduit la régression. On indique si le commit actuel est correct ou pas :
git |
> git bisect good # ou git bisect bad Bisecting : 4 revisions left to test after this |
Et on continue, encore et encore, jusqu’à ce qu’on se trouve le mauvais commit à l’origine de ce temps perdu (mais ça aurait pu être pire) :
37b4745bf75e44638b9fe796c6dc97c1fa349e8e is first bad commit
A tout moment, vous pouvez obtenir un historique de votre parcours :
git |
git bisect log |
Si a un moment précis, vous souhaitez ne pas tester un commit en particulier, pour n’importe quelle raison, utilisez la commande :
git |
git bisect skip |
Tout ceci est bel et bon, mais il y a mieux (non ?! et si !). Supposions que vous soyez un fan de TDD. Vous disposez sûrement d’un script qui teste automatiquement si le code actuel est bon ou pas. Il est alors possible d’automatiser la recherche :
git |
git bisect start HEAD <bad_commit> -- git bisect run script |
Allez prendre un café, git travaille pour vous. Note : le script doit renvoyer 0 si le code est correct, 1 s’il est incorrect, et 125 s’il est intestable (git skip). Pendant la bisection, git créé une branche spéciale dédiée. N’oubliez pas de repasser sur votre branche de développement quand vous aurez débusqué la régression. Il est possible de le faire simplement en tapant :
git |
git bisect reset |
Voilà, avec tout ça, fini les fastidieuses recherches de régression.[1]
4.30 Connaître ligne par ligne les commits
Ceci est la solution de taper sur les doigts d'une personne qui a réaliser un commit. Entre autre, vous avez la possibilité de connaître ligne par ligne qui a écrit quoi :
4.31 Déplacer un dossier et son historique vers un autre repository
On peut parfois avoir besoin de recréer un autre repository vierge pour contenir le dossier d'un autre repository. Ca a été mon cas pour l'extension DeleteHistory de Mediawiki qui j'ai créer. Au début, j'avais un repository appelé 'mediawiki_extensions' ou j'avais 2 extensions dedans. Et puis avec le temps et les version de Mediawiki qui avançaient, il était préférable de faire une séparation par plugins tout en gardant l'historique. Je vais donc vous expliquer comment j'ai fais (j'ai suivit cette documentation[2]).
Nous allons travailler en local avec mon ancien repository que nous allons appeler oldrepo et mon nouveau repository newrepo (quelle originalité).
WARNING |
Faites un repository temporaire si vous disposez déjà de celui ci car il sera supprimé à la fin |
Nous allons donc cloner ce repo :
git |
git clone git://git.deimos.fr/oldrepo.git git clone git://git.deimos.fr/newrepo.git |
Puis nous allons filtrer sur le dossier qui nous intéresse, appelons le folder2keep :
git |
git filter-branch --subdirectory-filter folder2keep -- -- all |
Là, tous les fichiers et dossiers de folder2keep sont à la racine de notre repository. Si vous le souhaitez, vous pouvez créer un dossier et tout placer dedans, mais ceci n'est pas obligatoire :
mkdir new_directory/ git mv * new_directory/ |
Remplacez * par tous vos éléments que vous souhaitez placer dedans et commitez :
git |
git commit -m "Collected the data I need to move" |
Maintenant, nous allons aller dans notre fameux dossier et faire une référence locale entre les 2 :
cd ../newrepo/ git remote add oldrepo ../oldrepo/ |
Ensuite nous allons rappatrier les sources, créer la branche principale et merger le tout :
git fetch oldrepo git branch oldrepo remotes/oldrepo/master git merge oldrepo |
Maintenant nous allons transférer l'historique et faire un peu de ménage :
git |
git remote rm oldrepo git branch -d oldrepo git push origin master |
Et voilà, le repository temporaire (vieux) peut être maintenant supprimé.
4.32 Hooks
Il existe des hooks dans Git permettant de faire des pre ou post traitement. Voici un use case permettant d'effectuer une action (lancer une commande via ssh) lorsqu'un tag arrive sur le serveur. L'idée est de pouvoir en fonction d'un tag (exemple: prod ou preprod) déployer une nouvelle version d'un soft sur X serveurs. Voici la liste des options qui vont être nécessaire:
- Créer un hook dans le repository
- Créer un utilisateur dédié si vous utilisez Gitlab/GitHub/Gitolite
- Générer une clef privée SSH avec l'utilisateur 'git'
- Copier via ssh-copy-id vers les serveurs distant la clef de l'utilisateur 'git' (généralement www-data)
- Créer un script de déploiement et mettre les bons droits
- Cloner le repository sur tous les serveurs nécessaire
Vous devez modifier les environnement avec ceux que vous voulez pour que quand un tag arrivera avec le nom du tag en question, il y ai une action qui soit déclenchée derrière. Par exemple pour déployer sur les serveurs de prod, il faudra mettre un tag de type "prod-v1.0".
Switchez avec l'utilisateur git (ou celui qui a les droits) et copiez la clé publique vers tous les serveurs sur lesquelles vont devoir agir les tags:
su - git ssh-copy-id www-data@x.x.x.x |
Créez ensuite le script de déploiement:
Vous pouvez rajouter toutes les commandes que vous voulez. Il faut bien entendu copier ce script sur tous les serveurs cible et lui donner les droits d'exécution.
Créez ensuite le fichier de logs et mettez les bons droits:
touch /var/log/git-deploy-hook.log chow www-data. /var/log/git-deploy-hook.log chmod 755 /usr/bin/git_deploy.sh |
Clonez ensuite les repository avec les utilisateurs finaux:
cd /var/www git clone git@gitlab/cloned_repository.git chown -Rf www-data. /var/www/cloned_repository |
Et voilà, il ne reste plus qu'à pusher un tag sur un commit:
git tag -a prod-v1.0 -m 'production version 1.0' 9fceb02 git push --tags |
5 Utilitaires
5.1 Gitk
Gitk permet d'avoir une version graphique de l'état de votre git (breanches etc...).
6 FAQ
6.1 warning: You did not specify any refspecs to push, and the current remote
Si vous avez ce genre de message quand vous faites un push :
warning: You did not specify any refspecs to push, and the current remote warning: has not configured any push refspecs. The default action in this warning: case is to push all matching refspecs, that is, all branches warning: that exist both locally and remotely will be updated. This may warning: not necessarily be what you want to happen. warning: warning: You can specify what action you want to take in this case, and warning: avoid seeing this message again, by configuring 'push.default' to: warning: 'nothing' : Do not push anything warning: 'matching' : Push all matching branches (default) warning: 'tracking' : Push the current branch to whatever it is tracking warning: 'current' : Push the current branch
Lancez simplement cette commande en mettant ce qui vous corresponds le plus :
git |
git config --global push.default matching |
7 Références
- ^ http://www.miximum.fr/methodes-et-outils/79-debusquer-une-regression-avec-git-bisect
- ^ http://st-on-it.blogspot.fr/2010/01/how-to-move-folders-between-git.html
http://www.kernel.org/pub/software/scm/git/docs/user-manual.html
http://git.wiki.kernel.org/index.php/GitFaq
http://alexgirard.com/git-book/index.html
How To Install A Public Git Repository On A Debian Server
Git it
http://www.unixgarden.com/index.php/administration-systeme/git-les-mains-dans-le-cambouis
http://stackoverflow.com/questions/1811730/how-do-you-work-with-a-git-repository-within-another-repository
http://chrisjean.com/2009/04/20/git-submodules-adding-using-removing-and-updating/