Aller au contenu principal
outilsGit au quotidien

Git au quotidien

Ce que ce chapitre apporte

  • Mettre un projet sous Git dès sa première ligne, avec un .gitignore utile.
  • Lire git status et savoir dans quelle zone se trouve chaque modification.
  • Composer des commits par intention plutôt que par moment de la journée.
  • Relire un historique, retrouver quand une ligne a changé et pourquoi.
  • Choisir la bonne opération d'annulation selon l'endroit où se trouve la modification.
  • Mettre de côté un travail en cours pour traiter une urgence.
  • Expliquer ce que calcule un diff.
Où on va
Le modèle de Git, ses trois zones, son graphe de commits et sa fusion, sont traités dans le bloc génie logiciel. Ce chapitre part de là où l'autre s'arrête : les gestes de la journée. Ouvrir un dépôt, voir où l'on en est, enregistrer proprement, relire l'histoire, et surtout annuler, opération que l'on cherche toujours dans l'urgence et qui n'a pas une réponse mais quatre, selon l'endroit où se trouve ce qu'on veut défaire.

Le premier jour

Trois commandes, une fois pour toutes.

Terminal3 commandes
$git init# crée le dépôt dans le dossier courant
$git config --global user.name "Léa D." # qui signe les commits
$git config --global user.email "lea@exemple.fr"

Puis un .gitignore, tout de suite, avant le premier commit. Ce qu'il contient : tout ce qui se régénère et tout ce qui ne regarde que vous.

# Produits de construction
__pycache__/
*.pyc
build/
dist/

# Dépendances installées
node_modules/
.venv/

# Réglages locaux et secrets
.env
.vscode/settings.json

# Bruit du système
.DS_Store
Un fichier déjà suivi n'est pas ignoré par un ajout au .gitignore
Le fichier .gitignore ne concerne que les fichiers que Git ne suit pas encore. Un fichier déjà commité continue d'être suivi, quoi qu'on écrive ensuite.
Il faut le retirer explicitement du suivi avec git rm --cached fichier, puis commiter ce retrait. Et s'il contenait un secret, le secret reste lisible dans l'historique : il est compromis, il faut le changer.

La boucle de la journée

Terminal6 commandes
$git status# où j'en suis
$git diff# ce que j'ai modifié et pas encore indexé
$git add rapport.py# je choisis ce qui entre dans le prochain commit
$git diff --staged# je relis ce que je m'apprête à enregistrer
$git commit -m "Corrige le calcul de la moyenne pondérée"
$git log --oneline -5# les cinq derniers commits

git status est la commande la plus utile de tout l'outil et la moins utilisée des débutants. Elle dit trois choses à la fois : sur quelle branche vous êtes, ce qui est modifié mais pas indexé, ce qui est indexé et attend le commit. La plupart des situations qui semblent inextricables se démêlent en la lisant.

Relire avant de commiter
git diff --staged montre exactement ce qui va entrer dans le commit. Trente secondes de relecture attrapent l'affichage de débogage oublié, le fichier de configuration personnel ajouté par mégarde, et la moitié du fichier collée deux fois.
C'est aussi le moment où l'on se rend compte que le commit mélange deux sujets, et qu'il vaut mieux en faire deux.

Une semaine de travail ordinaire donne un historique linéaire, où chaque commit correspond à une intention.

Historique du dépôtmain
30668daSquelette du projet et .gitignore
d595368Lecture du fichier de ventes
2b535dfCalcul du chiffre d'affaires par region
9c692a6Corrige le separateur decimal
36768e7Export du rapport en CSVHEADmainv0.1

Relire l'histoire

Un historique ne sert que si on sait l'interroger.

Terminal6 commandes
$git log --oneline --graph --all# l'arbre, en compact
$git log -p rapport.py# l'historique d'un seul fichier, avec les diffs
$git log -S "moyenne_ponderee" # les commits qui ajoutent ou retirent ce texte
$git show a1b2c3d# le contenu complet d'un commit
$git blame rapport.py# pour chaque ligne, le dernier commit qui l'a touchée
$git diff v0.1..HEAD# tout ce qui a changé depuis la version 0.1

git log -S mérite d'être connue : elle répond à « quand cette fonction est-elle apparue » ou « qui a supprimé cet appel », questions auxquelles une recherche dans le code actuel ne peut pas répondre, puisque justement le code a disparu.

git blame a mauvaise réputation à cause de son nom. Son usage réel n'est pas de désigner un coupable mais de retrouver le commit qui a introduit une ligne, donc son message, donc la raison.

Ce que calcule un diff

Un diff n'est pas une comparaison ligne à ligne, sinon l'insertion d'une ligne au début décalerait tout et rendrait les deux fichiers entièrement différents. L'outil cherche la plus longue sous-suite commune aux deux versions : ce qui reste, de part et d'autre, est ce qui a été supprimé et ajouté.

main.py
Sortie
>_ Prêt à exécuter…

Deux lignes ajoutées, aucune supprimée, cinq lignes communes reconnues malgré le décalage. C'est ce calcul qui permet à Git de fusionner deux branches sans rien demander tant qu'elles ne touchent pas aux mêmes endroits.

Annuler : quatre situations, quatre réponses

C'est le sujet où l'on se trompe, parce que la question « comment annuler » n'a de sens qu'accompagnée de « annuler quoi, et est-ce déjà publié ».

Ce qu'on veut défaireOù c'estCommande
Une modification du fichier, pas encore indexéerépertoire de travailgit restore fichier
Une modification indexée, pas encore commitéeindexgit restore --staged fichier
Le dernier commit, non publiédépôt localgit commit --amend
Un commit déjà publiédépôt distantgit revert a1b2c3d

Les deux premières lignes concernent du travail en cours et ne touchent pas à l'historique. Les deux dernières sont d'une autre nature.

git commit --amend remplace le dernier commit par un nouveau, avec une nouvelle empreinte. Utile pour rattraper un message mal écrit ou un fichier oublié, à condition que personne n'ait récupéré le commit d'origine.

git revert ne supprime rien : il crée un commit qui applique l'inverse de celui qu'on désigne. L'histoire garde trace de l'erreur et de sa correction, ce qui est exactement ce qu'on veut sur une branche partagée.

Historique du dépôtmain
d9a473fLecture du fichier de ventes
c120799Calcul du chiffre d'affaires
06d6bfbPasse les montants en centimesintroduit le defaut
2bf95f1Export du rapport
2af0f90Annule le passage en centimesHEADmaingit revert
reset --hard
git reset --hard déplace la branche en arrière et écrase le répertoire de travail. Tout ce qui n'était pas commité est perdu, sans confirmation.
Sur une branche partagée, le résultat est pire : votre historique et celui du dépôt distant divergent, et la remise en état demande une intervention pour tout le monde. Sur une branche partagée, on annule avec revert.
Presque rien n'est vraiment perdu
git reflog liste tous les endroits où HEAD est passé, y compris ceux qu'aucune branche ne référence plus. Un commit « perdu » par un mauvais reset s'y retrouve, et git checkout sur son empreinte le ramène.
Cela ne vaut que pour ce qui a été commité au moins une fois. Un travail jamais commité, lui, ne laisse aucune trace.

Mettre de côté

Une urgence arrive alors que le travail en cours n'est pas en état d'être commité.

Terminal4 commandes
$git stash# met de côté les modifications, revient à un dépôt propre
$git switch main
# ... on corrige l'urgence, on commite, on livre ...
$git switch ma-fonctionnalite
$git stash pop# récupère le travail mis de côté

Le réflexe alternatif consiste à commiter un état provisoire, avec un message explicite, puis à le corriger plus tard par --amend ou à le réorganiser avant publication. Les deux se valent tant que le résultat publié reste propre. Ce qui ne se fait pas, c'est de laisser un commit wip dans l'historique commun.

Exercices type

git status montre un fichier à la fois « modifié » et « indexé ». Comment est-ce possible ?

Parce que ce sont deux zones distinctes, et que le fichier a été modifié après avoir été indexé.

L'index contient l'état du fichier au moment du git add. Le répertoire de travail contient l'état actuel. Si vous avez modifié le fichier depuis, les deux diffèrent, et Git le signale dans les deux rubriques.

Le commit n'enregistrera que la version indexée. Pour enregistrer la modification récente, il faut refaire un git add.

Vous avez commité un fichier de configuration contenant un mot de passe. Que faire, dans quel ordre ?

D'abord changer le mot de passe. Le dépôt a pu être poussé, cloné, sauvegardé : le secret doit être considéré comme compromis, et c'est la seule action qui règle vraiment le problème.

Ensuite retirer le fichier du suivi : git rm --cached config.env, l'ajouter au .gitignore, commiter.

Enfin, seulement si nécessaire, réécrire l'historique pour effacer les anciennes versions. C'est une opération lourde qui change toutes les empreintes et oblige chacun à reprendre son dépôt. Elle ne dispense pas de la première étape.

Quelle différence entre revert et reset ?

revert ajoute un commit qui applique l'inverse d'un commit existant. L'historique s'allonge, rien n'est réécrit, et tout le monde peut récupérer la correction normalement.

reset déplace le pointeur de branche en arrière. Les commits qui suivaient ne sont plus atteignables par la branche : l'historique est réécrit.

Le critère de choix est la publication. Sur une branche partagée, revert. Sur un travail local que personne n'a vu, reset est légitime et souvent plus propre.

Pourquoi un diff n'est-il pas une simple comparaison ligne par ligne ?

Parce qu'une seule ligne insérée en tête décalerait toutes les suivantes, et la comparaison position par position déclarerait le fichier entièrement modifié.

L'algorithme cherche donc la plus longue sous-suite commune aux deux versions. Ce qui en fait partie est inchangé, le reste se répartit en suppressions et en ajouts.

C'est ce qui rend les diffs lisibles, et c'est la même notion qui permet à une fusion de savoir que deux modifications portent sur des endroits différents.

Un commit a été poussé avec un message erroné. Peut-on le corriger ?

Techniquement oui, par git commit --amend suivi d'une poussée forcée. Pratiquement, cela réécrit l'historique commun : quiconque avait récupéré l'ancien commit se retrouve avec une histoire divergente, et la remise en ordre coûte du temps à tout le monde.

Sur une branche partagée, on laisse le message tel quel, quitte à préciser dans le commit suivant.

Sur une branche personnelle que personne d'autre n'utilise, la correction est sans risque.

À quoi sert git stash qu'un commit provisoire ne ferait pas ?

À rien que le commit provisoire ne fasse, et c'est le point : les deux sont acceptables.

stash a l'avantage de ne rien laisser dans l'historique, ce qui évite d'avoir à nettoyer ensuite. Il a l'inconvénient d'être une pile invisible dans git log, facile à oublier, et non partagée entre machines.

Le commit provisoire a l'avantage d'être visible et sauvegardé. Il oblige à ne pas le publier tel quel.

La méthode

  1. git init et .gitignore avant la première ligne de code, pas après.
  2. Lis git status avant toute opération, et relis-le après quand tu doutes.
  3. Relis git diff --staged avant chaque commit.
  4. Un commit par intention. Si le message contient « et », il y a probablement deux commits.
  5. Avant d'annuler, situe : travail, index, commit local, commit publié. La commande en découle.
  6. revert sur ce qui est publié, jamais reset.
  7. En cas de panique, git reflog avant toute autre manipulation.

En résumé

  • Le .gitignore ne s'applique qu'aux fichiers pas encore suivis.
  • git status situe chaque modification dans l'une des trois zones.
  • Un commit se compose par intention, pas par fin de journée.
  • git log -S retrouve les commits qui ont ajouté ou retiré un texte, même disparu.
  • Un diff repose sur la plus longue sous-suite commune entre les deux versions.
  • Annuler dépend de l'endroit : restore, restore --staged, --amend, revert.
  • revert ajoute un commit inverse ; reset réécrit l'historique.
  • git reflog retrouve des commits qu'aucune branche ne référence plus.
  • Un secret commité est compromis : on le change, on ne se contente pas de l'effacer.

Et ensuite ? Tout cela vaut pour un dépôt personnel. À plusieurs, il faut décider qui pousse quoi, comment on relit, et comment on tranche un conflit. C'est le chapitre suivant.

Git au quotidien | Plateforme ETS