Git add hero đŸ€˜
Par Maxime BrĂ©hin ‱ PubliĂ© le 12 janvier 2022 ‱ 7 min

Lorsqu’on dĂ©bute avec Git, une des premiĂšres commandes qu’on utilise est git add. Elle est essentielle Ă  une bonne utilisation de Git puisqu’elle nous permet de prĂ©parer nos commits. Elle n’est pourtant jamais vraiment apprise, ce qui est regrettable car elle nous offre des options trĂšs pratiques !

Pour bien comprendre cet article, il est important d’avoir dĂ©jĂ  assimilĂ© la notion de zones Git. Et comme on aime faire les choses bien, on a Ă©crit un article Ă  ce sujet.

Vous préférez une vidéo ?

Si vous ĂȘtes du genre Ă  prĂ©fĂ©rer regarder que lire pour apprendre, on a pensĂ© Ă  vous :

Principe de fonctionnement

Lorsqu’on fait un git add 
 on demande Ă  Git d’enregistrer dans le stage les fichiers qu’on souhaitera plus tard valider (intĂ©gration au commit). En d’autres termes, on ajoute, modifie, supprime des fichiers dans notre copie de travail et on signale Ă  Git que tout ou partie de ces modifications est Ă  mettre dans un statut intermĂ©diaire (staged) avant d’ĂȘtre validĂ© sous la forme d’un commit.

On a donc la possibilitĂ© de prĂ©ciser des chemins complets ou partiels (globs) et ne prendre qu’une partie des modifications rĂ©alisĂ©es dans notre copie de travail (working directory) en vue d’un commit prochain.

Illustration de la commande "git add" : création des snapshots, enregistrement de leurs références dans le stage

Si la technique vous intéresse, voici ce qui se passe sous le capot :

  • Pour chaque fichier, un « instantané » du contenu est capturé :
    • il ne stocke pas les diffĂ©rences mais bien l’intĂ©gralitĂ© du fichier dans sa nouvelle version ;
    • ce fichier est optimisĂ© pour stockage sur le disque et appelĂ© blob (pour Binary Large Object) ;
    • le nom de ce fichier est une empreinte basĂ©e sur le contenu du fichier (hash de type SHA-1 ou SHA-2) ;
    • ce fichier est stockĂ© dans le rĂ©pertoire .git/objects.
  • Les rĂ©fĂ©rences aux fichiers sont enregistrĂ©es dans le stage (fichier .git/index).

Utilisation « classique »

Que vous soyez dans un terminal ou dans un Ă©diteur muni d’une interface Git, vous aurez accĂšs aux utilisations les plus frĂ©quentes de la commande add, Ă  savoir l’enregistrement des ajouts, modifications et suppressions des fichiers complets. Ceci Ă©quivaut en ligne de commande Ă  passer le ou les chemins de fichiers concernĂ©s :

git add file-1 file-2

On peut aller bien plus loin et gagner en temps, en confort, en qualitĂ© en s’aidant de quelques options. Remarquez que certaines options ne sont parfois disponibles qu’en ligne de commande. À vous de dĂ©terminer si votre Ă©diteur prĂ©fĂ©rĂ© sait gĂ©rer celles qui vous intĂ©ressent.

Notre sĂ©lection d’options utiles

  • <chemins> (ou -- <chemins> pour lever l’ambiguĂŻtĂ© avec le double tiret POSIX) ;
  • -u / --update, pour ne traiter que les fichiers dĂ©jĂ  versionnĂ©s ;
  • -A / --all / --no-ignore-removal, la totale : ajouts, modifications, suppressions ;
  • -f / --force pour forcer l’ajout de fichiers normalement ignorĂ©s ;
  • -p / --patch, pour ne stager qu’une partie des modifications des fichiers ;
  • -N / --intent-to-add pour permettre les diffs, le add -p et le commit -a des fichiers non-suivis.

Un petit tour des globs

Les (nombreuses) commandes Git capables de travailler avec des chemins de fichiers vous offrent la possibilitĂ© (en ligne de commande notamment) de passer des chemins exacts (fichiers ou rĂ©pertoires) voire des globs. Faisons un petit tour d’horizon de ces usages :

# Fichiers
git add file-1 file-2 director-1/file-3
# RĂ©pertoires
git add director-1/ directory-2/sub-directory
# Le célÚbre « prend tout dans le dossier courant »
git add .
# Les globs ou motifs de chemins (ici : tous les fichiers dans le
# répertoire courant dont le nom commence par « file » et tous les
# fichiers dans tous les sous-répertoires ayant pour extension « .js »)
git add file* **/*.js
# Et bien évidemment on peut mélanger les syntaxes
git add file-1 dir-1/ *.md

Ces syntaxes sont utilisables pour la partie fichiers de l’appel, qui suit les options de git add.

Tout sauf les nouveautés

Vous souhaiterez parfois n’intĂ©grer que les modifications et suppressions, donc tout sauf les fichiers nouvellement crĂ©Ă©s (qui ne sont pas encore versionnĂ©s). Pour ça, vous avez l’option -u / --update :

Tout, partout !

Vous voudrez souvent prendre l’intĂ©gralitĂ© du travail en cours : ajouts, suppressions et modifications. On utilise en gĂ©nĂ©ral git add ., mais si vous avez tendance Ă  vous balader dans vos rĂ©pertoires, ce . ne dĂ©signant que le rĂ©pertoire courant et ses sous-rĂ©pertoires, vous pourriez manquer les rĂ©pertoires parents.

C’est lĂ  tout l’intĂ©rĂȘt d’employer plutĂŽt git add --all / -A :

Cesse de m’ignorer !

Si les fichiers que vous souhaitez ajouter font partie des fichiers habituellement ignorĂ©s par Git (voir le fonctionnement du .gitignore), vous devrez alors forcer leur ajout avec l’option --force / -f. Une fois ces fichiers versionnĂ©s, leurs modifications ultĂ©rieures apparaitront au mĂȘme titre que pour les autres fichiers. Pour cesser de les versionner tout en les conservant sur disque, vous aurez besoin d’un git rm --cached par exemple (notez qu’ils existeront toujours dans les commits qui les contenaient).

Pour illustrer cela, prenons le cas de figure rĂ©current des logs. GĂ©nĂ©ralement les fichiers de logs sont placĂ©s dans un rĂ©pertoire applicatif dĂ©diĂ©, ici log/, et on ne souhaite pas historiser les logs dans Git, ça n’a aucun intĂ©rĂȘt. On va alors indiquer dans le fichier .gitignore qu’on ne souhaite pas versionner les fichiers contenus dans ce rĂ©pertoire :

# On recourt Ă  une astuce pour ajouter rapidement
# en fin de fichier la ligne « log/* ».
echo 'log/*' >> .gitignore

Si votre stack technique ne crĂ©e pas automatiquement son dossier de logs (et refuse de se lancer s’il n’existe pas), vous devrez vous assurer qu’il existe d’office, ce qui suppose que Git versionne le rĂ©pertoire. Sauf que Git ne gĂšre pas des rĂ©pertoires, seulement des fichiers. Pour palier Ă  ça, l’astuce classique consiste Ă  crĂ©er un fichier vide .keep ou .git-keep dans le rĂ©pertoire ciblé :

# Crée un fichier vide « .keep » dans le répertoire « log ».
touch log/.keep

Le problĂšme, c’est que ce fichier est ignorĂ©, il ne nous est pas proposĂ© Ă  l’ajout lors d’un git status et git add refusera de l’ajouter — mais nous dira que nous pouvons forcer l’ajout :

git add --force log/.keep

Et voilà, le tour est joué !

L’ajout chirurgical : « Docteur Patch »

S’il y a une option qu’on affectionne particuliĂšrement, c’est bien le git add --patch ! Elle nous permet de choisir, au sein d’un fichier, les modifications qu’on souhaite stager. C’est parfait quand on s’aperçoit qu’on a travaillĂ© sur plusieurs aspects et qu’on souhaite dĂ©couper tout ça pour obtenir plus de clartĂ© dans notre historique de commits.

Si on l’utilise sans paramĂštre supplĂ©mentaire, les fichiers nous seront proposĂ©s un Ă  un avec un Ă©ventuel prĂ©-dĂ©coupage des fragments de modifications (appelĂ©s hunks) quand l’espacement entre ces blocs est assez consĂ©quent.

On peut là aussi passer des chemins ou globs pour ne proposer des découpes que sur les fichiers qui nous intéressent.

Lorsqu’on a lancĂ© la commande, un assistant s’ouvre et affiche ce qui ressemble Ă  un git diff. Cet affichage nous indique le nombre de fragments qu’il a dĂ©coupĂ©s dans le fichier (dans l’exemple qui suit (1/2) signifie qu’on traite le premier bloc sur 2). Il nous pose ensuite une question avec tout plein de lettres entre crochets derriĂšre, qui n’est pas le truc le plus clair du monde :

(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?

Heureusement, on peut renseigner le point d’interrogation ? puis valider pour obtenir davantage de prĂ©cisions :

y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

Notez que l’option s (split) n’est disponible que lorsque le fragment affichĂ© permet d’ĂȘtre redĂ©coupĂ© facilement (quand il existe des lignes intactes entre les modifications du fragment courant).

Pour un usage courant, on utilisera principalement les options y (pour valider le fragment) et n (pour l’inverse). Parfois le s quand la dĂ©coupe n’est pas assez fine Ă  notre goĂ»t.

Il existe Ă©galement une option e (edit) pour permettre la modification manuelle du fragment, ce qui revient Ă  gĂ©rer Ă  la main les + et - d’un diff. C’est tentant si on se trouve face Ă  un fragment sans sĂ©paration, donc sans option s disponible, et qu’on souhaite dĂ©couper quand mĂȘme. Attention tout de mĂȘme, il est difficile de construire manuellement un fragment appropriĂ©, et ça donne souvent des rĂ©sultats inattendus. Mieux vaut alors reprendre votre fichier, retirer temporairement la partie du fragment que vous ne souhaitez pas ajouter, faire votre git add 
 puis remettre la portion retirĂ©e dans le fichier.

Voici un exemple d’utilisation qui produit 3 commits Ă  partir d’un unique fichier modifiĂ© en plusieurs endroits, chaque commit Ă©tant dĂ©diĂ© Ă  un sujet prĂ©cis.

L’intention d’ajout

C’est l’ajout indĂ©cis, « à la normande » : je veux ajouter, mais pas vraiment ! đŸ˜” ??

Il s’agit ici de l’option --intent-to-add ou -N.
Elle s’avĂšre pratique lorsqu’on souhaite visualiser l’ensemble des diffĂ©rences stagĂ©es, y compris les nouveaux fichiers. En d’autres termes : sans ajout ou intention d’ajout, la commande git diff ne nous permettra pas de voir le contenus des nouveaux fichiers. MĂȘme chose d’ailleurs pour le git add -p.

D’autre part elle permet la prise en considĂ©ration des nouveaux fichiers (non suivi/untracked) en cas d’utilisation du raccourci de commande git commit -a 
 (qui Ă©quivaut Ă  un git add -u + git commit).

L’ajout interactif, la fausse bonne idĂ©e

Si vous avez ouvert un jour la documentation de git add (pour les curieux·ses : git help add), peut-ĂȘtre avez vous dĂ©couvert l’option -i / --interactive. Peut-ĂȘtre mĂȘme avez-vous osĂ© ouvrir cette boite de Pandore et dĂ©couvert le dĂ©mon qui s’y cachait 👿.

Contrairement Ă  ce qu’on pourrait croire d’elle, cette option ne nous simplifie pas la tĂąche d’ajout en nous fournissant une interface sensationnelle. Bien au contraire, elle nous plonge dans une interface particuliĂšrement dĂ©routante.

Une petite démo vaut mieux que des mots :

Bien préparer ses commits

Vous l’aurez compris, la commande git add ne nous contraint pas Ă  un simple ajout en masse des modifications, et c’est tant mieux ! Cela nous permet de prĂ©parer au mieux des commits atomiques đŸ€Ż, c’est-Ă -dire traitant d’un sujet le plus prĂ©cisemment possible, et, par consĂ©quent, de produire un historique clair et utile.

Si vous voulez creuser plus ce sujet, nous vous recommandons de parcourir notre sĂ©rie d’articles sur l’utilisation de Git pour assister et automatiser la qualitĂ© des commits dans nos projets.

Tu veux aller plus loin et maĂźtriser pleinement les fondamentaux de Git ou ĂȘtre accompagnĂ© pour garantir la qualitĂ© de tes projets grĂące Ă  une bonne mise en place de Git ? On peut t’aider ou te former, il suffit de nous dĂ©crire ton besoin !