Annuler un pull
En voyant le titre de cet article, tu t’es probablement dit : “Mais pourquoi diantre aurais-je besoin d’annuler une synchronisation ?”
C’est vrai que ça semble absurde en premier lieu, sauf quand on connait bien Git et qu’on sait à quel point une mauvaise configuration de pull peut dégrader notre historique. On peut également trouver d’autres raisons à la marge.
J’espère donc que tu n’auras pas à réaliser ce genre de manipulation, mais si jamais l’occasion se présente, voilà comment t’en sortir avec une toute petite commande :
git reset --keep <nom-de-branche>@{1}
Cet article n’a pas vocation à expliquer le fonctionnement de la commande reset dans le détail. Si tu veux creuser ce sujet, tu peux lire cet autre article : « Git reset : rien ne se perd, tout se transforme ». En une phrase, la commande reset
permet de nous repositionner (notre localisation actuelle HEAD et l’étiquette de la branche courante) sur un commit choisi.
Cet article fait partie de notre série pour savoir annuler, défaire et corriger.
Avant propos : bien configurer le pull
Si tu constates que ton historique est dégradé suite à un pull, c’est très certainement que toi ou quelqu’un dans ton équipe n’a pas configuré correctement le mode de synchronisation. L’idée est, selon moi, de considérer ta branche et sa réciproque sur le serveur comme une seule et même branche (même si Git les gère à la base comme 2 branches “distinctes”). Partant de ce postulat, on mettra toujours à jour nos nouveautés (commits) locales par dessus le travail récupéré depuis le distant. Du point de vue technique, ça revient à dire à Git de synchroniser le pull en mode rebase
et avec l’option merges
. Pour bien comprendre tout ceci, je t’encourage vivement à lire notre article « Configurer le pull en mode “rebase” » qui décrit tout cela en détail.
Revenir “avant” le pull
Revenons-en au sujet de cet article : tu viens de faire un pull et le résultat ne te convient pas, tu veux le défaire. Pour ça, il faut comprendre qu’un pull est la somme de 2 opérations consécutives :
- La récupération des nouveautés depuis le serveur (fetch) ;
- L’application de ces nouveautés sur notre branche locale (merge ou rebase selon la configuration).
Quand on parle de défaire un pull, on parle d’annuler cette seconde opération. Si tu as lu notre série « Annuler, défaire, corriger », tu as dû voir les articles explicant comment annuler une fusion et comment annuler un rebase. C’est exactement ce qu’on doit faire, et la commande est la même dans les 2 cas :
git reset --keep <branche-courante>@{1}
Un exemple illustré
Prenons un exemple et voyons les étapes successives. Partons d’une branche dev
locale avec un commit d0
initial déjà partagé, et 2 nouveaux commits locaux d1
et d2
.
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'dev'} } }%% gitGraph commit id: "d0" commit id: "d1" commit id: "d2" tag: "dev"
Un collègue a partagé les commits d3
et d4
. La première étape implicite du pull, le fetch, produit l’historique
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'dev'} } }%% gitGraph commit id: "d0" commit id: "d1" commit id: "d2" tag: "dev" branch "origin/dev" checkout "origin/dev" commit id: "d3" commit id: "d4" tag: "origin/dev"
Ensuite, selon ta configuration, cette branche origin/dev
sera soit fusionnée dans dev
, soit ta branche locale dev
sera mise à jour (“rebasée”) par dessus origin/dev
Résultat du pull en mode fusion (rebase = false
)
L’historique produit une “bosse” dans l’historique et un nouveau commit. Mais surtout, l’étiquette de la branche locale dev
n’est déplacée qu’une fois en fin d’opération pour pointer sur le commit de fusion.
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'dev'} } }%% gitGraph commit id: "d0" commit id: "d1" commit id: "d2" branch "origin/dev" checkout "origin/dev" commit id: "d3" commit id: "d4" tag: "origin/dev" checkout "dev" merge "origin/dev" tag: "dev"
Résultat du pull en mode rebase (rebase = merges
)
L’historique montre nos commits locaux d1
et d2
rejoués par dessus le nouvel état de la branche origin/dev
. On a donc les commits d1’
et d2’
qui suivent le commit d4
. L’étiquette de la branche dev
n’est déplacée là aussi qu’une fois en fin d’opération.
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'dev'} } }%% gitGraph commit id: "d0" commit id: "d3" commit id: "d4" tag: "origin/dev" commit id: "d1’" commit id: "d2’" tag: "dev"
Reset et le reflog pour annuler
L’annulation dans les 2 cas se fera à l’aide de la commande reset et de la syntaxe de reflog nom-de-branche@{1}
qui indique le dernier emplacement de l’étiquette de branche avant modification de l’historique :
git reset --keep dev@{1}
Une fois la commande exécutée, on retrouve notre l’historique d’avant changement de la branche dev
locale :
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { 'commitLabelFontSize': '16px' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'dev'} } }%% gitGraph commit id: "d0" commit id: "d1" commit id: "d2" tag: "dev" branch "origin/dev" checkout "origin/dev" commit id: "d3" commit id: "d4" tag: "origin/dev"
Si maintenant tu changes ta configuration (par exemple: git config --global pull.rebase merges
) et que tu relances la commande pull
ton historique se remettra à jour en accord avec cette configuration.
Note au passage que si ton annulation était mal venue ou mal faite, tu peux annuler cette annulation avec la même commande git reset --keep dev@{1}
.
Te voilà désormais équipé·e pour annuler un pull 👍 !
Vous pouvez aussi regarder le programme de notre formation "Comprendre Git" ou nous poser vos questions sur notre forum discord.