Reporter des commits vers un autre projet
Par Maxime Bréhin • Publié le 29 mai 2023 • 4 min

On a vu dans notre article sur le cherry-picking comment reporter des commits d’un endroit à un autre sans effort au sein d’un même projet ! Voyons ici comment effectuer le même type d’opération d’un projet vers un autre.

Quand est-ce utile ?

Il s’agit ici d’un besoin très particulier. Généralement avec une architecture comprenant un projet socle ou noyau et des projets déclinés depuis ce socle (des clones).

Architecture montrant deux projets déclinés depuis un projet noyau

Certaines évolutions ou correctifs réalisés sur l’un des projets doivent parfois être reportés sur les autres projets du groupe.

Docteur patch

Les patches Git servent à ça et sont réalisés en 2 étapes :

  • extraire les commits souhaités sous forme de fichiers (les patches) ;
  • appliquer ces patches sur un autre projet.

Schéma d'extraction et d'application des patches

Extraire

Pour créer les patches, on utilise la commande format-patch :

# Extrait les 3 commits jusqu'à "commit-1" inclus,
# chacun dans un fichier dédié dans le répertoire ../patches
git format-patch -k -o ../patches/ <commit-1> -3

Je conseille vivement l’emploi de l’option -o / --output-directory afin d’éviter tout risque d’ajout des fichiers de patches dans le projet actuel.

L’option -k / --keep-subject sert à conserver le message du commit extrait. Sans ça, le message serait préfixé par [PATCH].

Enfin, l’option numérique -3 permet d’indiquer le nombre de commits qu’on veut extraire jusqu’au commit désigné inclus.

Il existe différentes syntaxes, même si personnellement je préfère la syntaxe explicite utilisée ci-dessus. En voici d’autres que je trouve parfois utiles :

# Extrait tous les commits partant de commit-1 jusqu'à ma position actuelle (HEAD)
git format-patch -k <commit-1> -o ../patches/
# Extrait les 2 derniers commits
git format-patch -k -2 -o ../patches/
# Extrait l'intervalle entre commit-1 et commit-2 (commit-1 exclu)
git format-patch -k -o ../patches/ <commit-1>..<commit-2>

En pratique, on n’a pas besoin de regarder le contenu d’un patch. Mais pour satisfaire ta curiosité voici à quoi ça ressemble :

From 3ad32ae7dc3d701345e272f01346f6e2966cb5d9 Mon Sep 17 00:00:00 2001
From: Margaret Hamilton <margaret@learn.git>
Date: Tue, 28 Jul 2015 18:12:17 +0200
Subject: fix(copyright): extend copyright with start year

---
 index.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.html b/index.html
index 7c50df4..d3e23e2 100644
--- a/index.html
+++ b/index.html
@@ -11,7 +11,7 @@

   <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur atque, a commodi maiores assumenda doloribus quos, cum accusantium recusandae, inventore facere ex corporis aperiam eum molestias dolorum possimus aliquam quis!</p>

-  <footer>© 2014 My Company</footer>
+  <footer>© 2014–2015 My Company</footer>
   <script src="jquery.js"></script>
   <script src="demo.js"></script>
 </body>
--

On trouve une en-tête et les modifications (équivalent d’un git diff -p --stat). Le format servait originellement (et sert peut-être encore) à l’envoi des patches automatiquement par e-mail au format mbox (UNIX mailbox). Je vais éviter de rentrer dans les détails, mais si ça t’intéresse n’hésite pas à nous le dire en commentaire ! 👇

Appliquer les patches

Une fois extraits, on va appliquer ces patches dans les projets voulus. On a alors 2 façons de faire :

  • soit on applique les modifs dans la copie de travail, sans committer (on doit finaliser nous-même) : git apply --3way ../patches/* ;
  • soit on demande à appliquer les commits en mode “copier-coller” : git am --3way ../patches/*.

J’ai tendance à préférer la seconde méthode car elle s’apparente au cherry-picking, ce qui me va bien. À toi de voir ce qui te convient le mieux.

L’option --3way sert en cas de conflit, je t’explique ça un peu plus bas.

Des conflits ?

Comme pour le cherry-picking, on peut être confrontés à des conflits. La procédure est toujours la même : on arbitre et on continue / redonne la main.

Applying: …
error: patch failed: index.html:11
error: index.html: patch does not apply
Patch failed at …
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Dans ces moments-là, git status est ton meilleur ami et va te guider !

You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

Pour faire simple :

  • tu corriges ;
  • tu ajoutes au stage / à l’index ;
  • tu fais un git am --continue

Schéma de gestion des conflits lors de l'application des patches

L’option --3way permet de laisser les fichiers en situation de conflit dans la copie de travail pour que tu puisses faire ton arbitrage. Sans ça, il t’affichera le message d’erreur mais n’appliquera pas le patch problématique. Tu peux configurer Git pour qu’il l’effectue systématiquement :

git config --global am.threeWay true

Note que ce comportement s’applique également à la commande apply.

Utiliser le cherry-picking d’un projet vers un autre ?

À coup d’astuces, on peut utiliser le cherry-picking plutôt que les patches. Pour ça, il faut aller dans chaque projet concerné par le report, y charger la branche du projet noyau (oui je sais, ça peut sembler étrange, mais c’est possible !), et enfin effectuer le cherry-picking. Voilà comment faire :

  1. On définit un nouveau remote sur le projet enfant :
# Je le définis ici avec le nom `core`.
git remote add core <core-project-url>

2. On récupère le contenu de la branche qui nous intéresse :

git fetch core <nom-de-branche>
  1. On affiche l’historique de la branche si on n’a pas déjà les identifiants de commits à récupérer :
# Je logue ici les 20 derniers commits, à toi d'adapter selon ton besoin
git lg -20 core/<nom-de-branche>
  1. On se place sur la branche sur laquelle on souhaite appliquer les commits, ou on en crée une :
# À toi de choisir tes contraintes de nommage si tu décides
# comme moi de passer par une branche pour effectuer le report.
git switch --create backport/<nom-de-branche> <point-de-départ>
  1. On peut faire notre report via le cherry-picking :
git cherry-pick -x <ID-du-commit>

Schéma de report commits via cherry-picking

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 !