Cédric Temple

Administrateur système en logiciels libres

GIT, gestionnaire de version décentralisé

2019-04-28 » notes pour plus tard

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, doit être remplacé par le nom de la branche locale pour avoir des noms identiques entre branche locale et branche distante) :

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 :

  1. d’abord se positionner sur la branche depuis laquelle on souhaite diverger
  2. ensuite, créer la nouvelle branche
  3. faire nos modifications
  4. valider nos modifications locales
  5. 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>

Supprimer une branche locale uniquement

Si l’on veut effacer une branche locale uniquement, le processus est la suivant :

  1. d’abord, se positionner sur une autre branche
  2. 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 :

  1. d’abord, se positionner sur une autre branche
  2. ensuite, effacer la branche locale
  3. 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 parfois utile de nettoyer son repository pour virer toutes les données 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 :

  1. identifier le tag
  2. se positionner sur ce tag
  3. 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 :

  1. l’identifiant du commit (un hash SHA)
  2. la date du commit
  3. 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 :

  1. se positionner sur la branche 2.3.8-develop
  2. identifier la modification que l’on souhaite pousser sur la branche 2.4.0-develop grâce à git log
  3. se positionner sur la branche 2.4.0-develop
  4. 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
© Cédric Temple - - Powered by Jekyll.