- Notes sur GIT
- Récupérer un repository distant
- Voir les branches
- Se positionner dans une branche
- Voir les modifications en cours
- Valider (committer) localement des modifications
- Envoyer les modifications sur la branche distante
- Récupérer les modifications provenant de la branche distante
- Créer une nouvelle branche
- Annuler des commits locaux
- Neutraliser un commit distant
- Supprimer une branche locale uniquement
- Supprimer une branche
- Nettoyer un repository
- Créer un tag
- Lister les tags
- Se positionner sur un tag
- Repartir d’un code taggué pour créer une nouvelle version
- Définition d’une politique de nommage des branches et des tags
- Voir les commits d’une branche
- Récupération de commits d’une autre branche
- Travailler avec une branche d’un autre repository
- Fournir une archive d’une version du code
Ce contenu est fourni sous licence CC BY-NC-SA 4.0
Notes sur GIT
Récupérer un repository distant
git clone <url>
Voir les branches
git branch -ar
Se positionner dans une branche
git checkout <nomBranche>
Voir les modifications en cours
git status
Valider (committer) localement des modifications
Valider toutes les modifications :
git commit -a -m '<mon message de commits>'
Valider certaines modifications :
git commit -a -m '<mon message de commits>' <fichier1> <fichier2> ...
Envoyer les modifications sur la branche distante
git push
Si on vient juste de créer la branche locale (ici, généralement,
git push -u origin <nomBranche>
# ou, de manière identique mais plus claire :
git push --set-upstream origin <nomBranche>
Envoyer toutes les modifications de toutes les branches locales vers les branches distantes correspondantes :
git push --all
Récupérer les modifications provenant de la branche distante
git pull
Créer une nouvelle branche
Le processus est le suivant :
- d’abord se positionner sur la branche depuis laquelle on souhaite diverger
- ensuite, créer la nouvelle branche
- faire nos modifications
- valider nos modifications locales
- envoyer sur le serveur distant les modifications locales
git checkout <nomBrancheSource>
git checkout -b <nomNouvelleBranche>
# faire les modifications... puis les valider localement :
git commit -a -m '<mon message de commits>'
git push --set-upstream origin <nomNouvelleBranche>
Annuler des commits locaux
Là, on se trouve dans le cas où l’on a fait un ou plusieurs commits sur la branche locale mais que ces commits n’ont pas encore été poussés vers le repo distant.
Deux approches possibles :
- je veux supprimer toute information, toute modification de ce commit
- je veux supprimer l’information de commit mais garder les modifications faites à mon code.
Le premier cas est “non mais là, j’ai merdé en fait. Tout ce que j’ai fait est à jeter. Heureusement, je n’ai rien poussé sur le repository principal, les collègues se moqueraient de moi”. L’idée est donc de supprimer toutes les modifications et revenir au code tel qu’il était avant ma modification.
git reset --hard HEAD^1
Le deuxième cas est “ha zut, je me suis trompé, j’aurais du ajouter/retirer tel fichier à mon commit” ou “ha zut, j’ai oublié de modifier tel fichier”.
git reset --mixed HEAD^1
Là, vous pouvez ensuite continuer vos modifications et validez votre commit.
Vous pouvez annuler plusieurs commits consécutifs mais toujours si vous ne les avez pas poussés sur le repo distant. Pour cela, identifier le numéro de commit précédent vers lequel revenir à l’aide de la commande git log puis l’indiquer. Exemple :
cedric@portable:~/GIT/blog.cedrictemple.net-test-git$ git log
commit 696625ea73bbb2e39400d9ed5ccb19b04b306da6 (HEAD -> master)
Author: Cedric Temple <email>
Date: Mon Dec 30 17:10:28 2019 +0100
test 2
commit 4ca8357b99af667cc8c31ec517e4ae01e5d405b0
Author: Cedric Temple <email>
Date: Mon Dec 30 17:10:09 2019 +0100
test 1
commit d248572d571d1746b808bf762909c623b77a6c74 (origin/master)
Author: Cedric Temple <email>
Date: Wed Dec 25 12:27:22 2019 +0100
Ajout de /etc/resolv.conf
commit 0a16f9172c038be6f8867b02257a581342058c9c
Author: Cedric Temple <email>
Date: Mon Dec 23 18:01:46 2019 +0100
...
...
...
cedric@portable:~/GIT/blog.cedrictemple.net-test-git$ git reset --mixed d248572d571d1746b808bf762909c623b77a6c74
Neutraliser un commit distant
Il n’est pas possible de supprimer un commit distant : en effet, rien ne dit que d’autres ne l’ont pas déjà récupéré, si on le supprime, les autres seront très perturbés. De ce fait, on va neutraliser un commit : on va créer un commit qui inverse un autre commit.
Pour cela, on utilise la commande git revert en passant en argument le numéro de commit que l’on veut neutraliser. Dès lors, GIT va automatiquement créer un nouveau commit inversant le commit précédent.
git revert ID_COMMIT
# exemple :
git revert 4ca8357b99af667cc8c31ec517e4ae01e5d405b0
Dès lors, une fois le commit poussé, l’historique GIT reflétera exactement l’histoire :
- un premier commit X a été fait
- d’autres commits ont (peut-être) été faits
- un autre commit X’ a été fait pour annuler le premier commit X
Ce n’est pas (totalement) magique : si d’autres commits ont modifié les lignes du commit que l’on veut neutraliser, un conflit va se créer. Il faut alors le résoudre manuellement.
Supprimer une branche locale uniquement
Si l’on veut effacer une branche locale uniquement, le processus est la suivant :
- d’abord, se positionner sur une autre branche
- ensuite, effacer la branche locale
git checkout master
git branch -D todelete
Supprimer une branche
Ici, on veut supprimer la branche nommée “todelete”. Le processus est le suivant :
- d’abord, se positionner sur une autre branche
- ensuite, effacer la branche locale
- enfin, pousser la référence vide (indiquée ici par le caractère ‘:’) sur le serveur.
git checkout master
git branch -D todelete
git push origin :todelete
Nettoyer un repository
Il est possible de nettoyer son repository pour virer toutes les données devenues inutiles. Exemple ici, avec un tout petit repository :
du -sh .
18M .
git gc
Décompte des objets: 28512, fait.
Delta compression using up to 8 threads.
Compression des objets: 100% (8548/8548), fait.
Écriture des objets: 100% (28512/28512), fait.
Total 28512 (delta 20391), reused 27529 (delta 19639)
du -sh .
9,5M .
Créer un tag
Un tag est une étiquette pour marque un commit. Généralement, on taggue lorsqu’on crée une version de son logiciel, de sa documentation, …
git tag <nomDuTag> -am 'message pour les humains'
# exemple :
git tag v1.1 -am 'ma toute nouvelle version v1.1 qui contient ...'
# puis, on doit pousser le tag sur le serveur :
git push --tags
# puis plus tard
git tag v1.2 -am 'ma toute nouvelle version v1.2 qui contient ...'
git push --tags
Lister les tags
git tag --list
Se positionner sur un tag
Pour retrouver le code que l’on a taggué, c’est simple :
# pour identifier le tag
git tag --list
# pour se positionner sur le tag
git checkout <nomTag>
# exemple :
git checkout v1.0
Là, vous êtes en mode “DETACHED HEAD”. Pas de panique : c’est pour vous indiquer que vous n’êtes pas sur une branche mais sur un tag. Du coup, vous ne pouvez pas pousser les modifications sur le serveur distant sans passer par la case “Repartir d’un code taggué pour créer une nouvelle version”.
Repartir d’un code taggué pour créer une nouvelle version
Prenons un exemple pour expliquer le cas d’usage récurrent. Généralement, on crée une version 2.3.8. On pense que cette version corrige tous les bugs de la version 2.3 et on avance sur la version 3.0. Là, après quelques semaines, un bug bloquant apparaît. Il faut le corriger rapidement, sans forcer tout le monde à migrer sur la version 3.0, qui d’ailleurs n’est pas encore prête. Cependant, il faut comprendre qu’un tag n’est pas une branche : on ne peut pas modifier le code d’un tag. Il faut donc créer une nouvelle branche basée sur le tag. Le processus est donc :
- identifier le tag
- se positionner sur ce tag
- créer une nouvelle branche
# pour identifier le tag
git tag --list
# pour se positionner sur le tag
git checkout <nomTag>
# exemple :
git checkout v2.3.8
# création de la nouvelle branche :
git branch -b 2.3.9-branch
Définition d’une politique de nommage des branches et des tags
Il est nécessaire de définir une politique de nommage des branches et des tags. En effet, si vous nommez de la même manière les tags et les branches, vous ne pouvez pas (simplement) vous positionner sur le tag v2.3.8 en utilisant :
git checkout v2.3.0
Vous allez avoir un conflit : une branche et un tag se nomme de la même maniètre (v2.3.0). Comment GIT sait s’il doit utiliser la branche ou le tag ? GIT, par défaut, se positionne sur la branche.
Il est alors nécessaire de définir comment nommer les choses en posant des règles simples comme par exemple :
- les branches se nomment
-develop où ne commence pas par le caractère 'v'. Notre branche va donc se nommer : - 2.3.8-develop
- les tags se nomment v
. Par exemple, notre tag va se nommer : - v2.3.8
Dès lors, il est plus facile d’identifier la branche ou le tag sans faire d’erreur.
Si jamais on se trompe et que l’on crée un tag qui a le même nom qu’une branche, il est quand même possible de se positionner sur le tag correspondant :
git checkout refs/tags/<nomDuTag>
# exemple si l'on a nommé le tag et la branche v2.3.0 :
# se positionner sur le tag :
git checkout refs/tags/v2.3.0
# se positionner sur la branche locale v2.3.0 :
git checkout refs/heads/v2.3.0
# se positionner sur la branche distante v2.3.0 :
git checkout refs/remotes/origin/v2.3.0
Voir les commits d’une branche
Pour voir les commits d’une branche, se positionner sur cette branche puis :
git log
On voit alors, pour chaque modification, 4 informations :
- l’identifiant du commit (un hash SHA)
- la date du commit
- le message de commit
Récupération de commits d’une autre branche
Lorsqu’on développe, on développe parfois en parallèle plusieurs versions. Donc on développe sur plusieurs branches en même temps. Cependant, on aimerait bien qu’une modification donnée soit reportée d’une branche à l’autre. Avec GIT, c’est faisable simplement. Imaginons le cas suivant pour l’exemple : j’ai deux branches 2.3.8-develop et 2.4.0-develop. J’ai fait un correctif sur la branche 2.3.8-develop que j’aimerai bien appliqué sur 2.4.0-develop. Le processus est le suivant :
- se positionner sur la branche 2.3.8-develop
- identifier la modification que l’on souhaite pousser sur la branche 2.4.0-develop grâce à git log
- se positionner sur la branche 2.4.0-develop
- récupérer le commit avec git cherry-pick
git checkout 2.3.8-develop
git log
# j'identifie mon commit en utilisant le hash SHA, par exemple : 84e2a37b46e604189a8bc18eb942775cb5a52987
git checkout 2.4.0-develop
git cherry-pick 84e2a37b46e604189a8bc18eb942775cb5a52987
# j'ai récupéré le commit, je peux le pousser sur le serveur distant :
git push
Bien entendu, cela fonctionne automagiquement lorsqu’il n’y a pas de conflit. S’il y a des conflits, il faut les résoudre manuellement.
Il est aussi possible d’enchaîner les git cherry-pick ou de donner une liste de commits.
Travailler avec une branche d’un autre repository
La grande force est d’être décentralisé. Il est possible de travailler sur des branches provenants d’autres repositories qui peuvent, ou non, provenir d’autres serveurs. Ce mode de fonctionnement est utilisé par GitHub de la façon suivante :
- on dispose d’un repository principal
- les développeurs clonent ce repository principal pour disposer chacun de leur propre repository
- pour chaque nouvelle modification (exemple : “ajout d’un bouton rouge dans écran 3”), un développeur crée une branche dédiée, dans leur propre repository
- ils committent et poussent (“push”) sur le serveur hébergeant leur propre repository
- ils font une demande de validation de modification (“pull-request”)
- si la demande est acceptée, elle arrive sur le repository principal
- régulièrement, ils resynchronisent leur propre repository avec le repository principal
Pour faire cela avec les commandes GIT :
# clone de mon repository personnel
git clone <serveurGit>/<cheminVersMonRepository> monLogiciel
# je me place dans le répertoire de mon logiciel
cd monLogiciel
# je crée une référence vers le repository principal
git remote add upstream <ServeurGitPrincipal>/<CheminVersRepositoryPrincipal>
# récupération des modifications
git fetch upstream
# je me place sur une branche upstream
git checkout upstream/branch-3.0
# je crée une nouvelle branche locale
git checkout -b branch-3.0
# je dis que cette branche doit exister sur mon serveur
git push --set-upstream origin branch-3.0
# je modifie mes fichiers locaux
...
# je valide mes modifications
git add ...
git commit -am 'mon message de commit'
# je pousse sur mon repository personnel de mon serveur
git push
# dès lors, je peux faire mon pull-request dans GitHub par exemple
Ensuite, une fois que le commit est validé, si je veux me remettre d’équerre avec le code source “officiel”, c’est à dire avec le code source du repository principal, qui peut avoir évoluer par ailleurs, car d’autres développeurs ont ajoutés leurs propres commits :
git fetch upstream
git checkout <la branche de mon repository local que je veux synchroniser>
git merge remote/<la branche du repository principal avec laquelle je veux me resynchroniser>
# Exemple : plus haut, on avait ajouter une modification à branch-3.0 ; donc là, on veut se resynchroniser avec cette branche
# on doit donc faire :
git fetch upstream
git checkout branch-3.0
git merge remote/branch-3.0
# et là, je peux faire mes nouvelles modifications
Fournir une archive d’une version du code
Il est souvent utile de fournir une archive (tar.gz ou zip) du code aux personnes qui vont l’utiliser. Pour cela, on utilise la commande ‘'’git archive’’’ :
# créer une archive depuis un tag
git archive --format=tar --prefix=monProjet-v2.3/ v2.3 | gzip > ../monProjet-v2.3.tar.gz
git archive --format=tar.gz --prefix=monProjet-v2.3/ v2.3 > ../monProjet-v2.3.tar.gz
# pas besoin d'indiquer le format, il est déduit du nom de fichier de sortie
git archive --prefix=monProjet-v2.3/ -o ../monProjet-v2.3.tar.gz v2.3
# créer une archive de la branche courante basée sur le dernier commit
git archive --prefix=monProjet-SNAPSHOT/ -o ../monProjet-SNAPSHOT.tar.gz HEAD
Ce contenu est fourni sous licence CC BY-NC-SA 4.0