Git - Notes diverses

dim. 08 juil. 2018 by Marmotte

Git est un système de gestion de versions décentralisé. Le côté décentralisé implique que chaque client télécharge une copie complète du dépôt, la plupart des opérations étant donc réalisées directement sur la machine de l'utilisateur.

Cet article fait partie d'une série expliquant quelques fonctionnalités avancées que j'utilise régulièrement.

  1. Généralités et notations
  2. Quelques configurations
  3. Notes diverses
  4. Dépôts et répertoires de travail
  5. L'historique local
  6. Retravailler l'historique des commits
  7. Trouver le commit introduisant une régression

Annuler une résolution de conflit en cours

Lorsque l'on utilise des commandes comme git merge, git rebase ou git cherry-pick, le traitement peut s'arreter sur un conflit. Dans ce cas, on est obligé de résoudre le conflit pour pouvoir continuer.

Cependant, il arrive de se perdre dans la résolution du conflit, et je suis etonné du nombre de personnes qui committent en l'état, puis corrigent ensuite, soit par un nouveau commit, soit par un nouveau rebase, ou autre moyen.

Cette façon de procéder pose plusieurs problèmes, dont :

  • Une fois le traitement validé, il est plus difficile de retrouver les différents états précédents, et donc de corriger.
  • Si la fonctionnalité rerere est utilisée, une résolution de conflit erronée sera enregistrée.
  • Certains créent simplement un commit de correction, ce qui ajoute de la confusion dans l'historique du projet.
  • etc.

Il arrive même que, selon les actions effectuées, l'état du dépôt git ne soit pas modifié. Dans ce cas, git se croit toujours dans la résolution de conflit, ce qui peut poser problème lors de l'utilisation suivante de la commande échouée.

Lorsque l'on considère que la résolution de conflit est ratée, il est possible de l'annuler en totalité, pour pouvoir recommencer le traitement complet ensuite. Cela se fait au moyen du paramètre --abort des commandes concernées :

$ git merge --abort
$ git rebase --abort
$ git cherry-pick --abort

Historique des modifications par ligne d'un fichier

Lorsqu'une ligne de code parait étrange, on veut parfois savoir pourquoi elle a été écrite de cette manière. Pour cela, il est nécessaire de trouver le commit qui a ajouté la modification suspecte. Cependant, parcourir un à un tous les commits ayant modifié le fichier est inutilement long, beaucoup de modifications ne touchant probablement pas la ligne recherchée. Il est alors possible d'utiliser la commande git blame, qui affiche le contenu du fichier, chaque ligne étant préfixée par des informations du dernier commit l'ayant modifiée : hash, auteur et date.

$ git blame <fichier>
$ # Afficher l'historique depuis le commit <commit>
$ git blame <commit> <fichier>

N'appliquer qu'une partie d'un diff

Les commandes git add, git reset et git checkout permettent de basculer des modifications entre le répertoire de travail, l'index et le HEAD. Mais plutôt que prendre en compte toutes les modifications disponibles, il est possible de demander à git de découper le patch en blocs, et de décider un par un lesquels sont à conserver. Mieux, il est aussi possible d'éditer chaque morceau de patch à la volée, pour n'ajouter qu'une partie d'un bloc de modifications, ou corriger une erreur mineure sans devoir quitter le processus. L'édition du patch est cependant à réserver aux modifications très simples, puisqu'il est souvent plus facile de quitter la commande pour modifier le fichier, et relancer la commande, en cas de modification lourde, plutôt que de le faire directement dans le patch.

Cela est possible grâce au paramètre --patch (agbrégé -p). Pour chaque bloc, il faut alors saisir un caractère pour sélectionner l'action à effectuer. Voici les actions que j'utilise le plus souvent :

  • y : Accepter le bloc.
  • n : Refuser le bloc.
  • a : Accepter le bloc, et tous les suivants du même fichier.
  • d : Refuser le bloc, et tous les suivants du même fichier.
  • s : Découper le bloc proposé en plus petits blocs. Au lieu de découper avec un contexte de trois lignes, chaque sous-bloc est alors découpé à chaque ligne non modifiée.
  • e : Ouvrir le bloc dans un éditeur de texte, et modifier manuellement le patch.
  • q : Quitter le processus d'ajout/retrait des modifications en conservant les modifications déjà appliquées.
  • ? : Affiche l'aiderappelant à quelle action correspond chaque caractère.

Astuce : Lors de la création d'un nouveau fichier, celui-ci est inconnu de git, ce qui rend impossible l'utilisation de la commande git add -p pour ce fichier. Pour cela, il est alors nécessaire d'ajouter le chemin de ce fichier dans l'index, sans son contenu, au moyen de la commande git add -N. N'ajouter que certaines parties de ce fichier dans l'index devient ensuite possible avec la commande git add -p, en utilisant l'action e.

Vérifier ses commits avant de proposer ses modifications

Lors d'un gros développement, il n'est pas rare de faire erreurs, revoir le fonctionnement d'un élément, réimplémenter une méthode différemment, etc. On utilise alors les commandes git add -p, git rebase, etc. pour nettoyer et ordonner les commits afin de rendre l'historique du développement plus clair et simple à lire. Cependant, pendant ces opérations, il peut arriver d'inclure une modification non voulue dans un commit (souvent un fixup mal ciblé), ou de perdre une modification suite à une erreur de résolution de conflit (lors d'un changement d'ordre, par exemple).

Dans ce cas, avant de proposer le résultat aux autres développeurs, une vérification de l'historique peut être utile. La commande git show --reverse $(git merge-base <target-branch> HEAD).. affiche tous les nouveaux commits proposés, avec les différences apportées par chacun, permettant ainsi de vérifier que chacun ne contient pas de modification indésirable.

Note : Comme expliqué au paragraphe sur le passage de paramètres à un alias, pour définir un alias utilisant cette commande, il est nécessaire de le déclarer sous forme de fonction.