Stratégies de fusion
- Leçon 9
- 40 min
- Module 3 : Collaborer par la revue de code
Leçon 9 sur 15 · L'essentiel de Git et du développement en équipeStratégies de fusion
1. Comprendre un dépôt
2. Travailler avec les branches et les dépôts distants
3. Collaborer par la revue de code
4. Résoudre et récupérer
5. Mises en production et correctifs urgents
Ce que vous saurez faire
Choisissez comment intégrer une demande de tirage (pull request) à main et sachez ce qui arrive à ses commits (enregistrements). Après cette leçon, vous pourrez expliquer l’historique conservé ou réécrit par un commit de fusion, un squash et un rebase.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Comment réviser et fusionner une demande de tirage, dans Les étapes d’une demande de tirage : 8. Fusionner et Lire et réviser les différences : deux points ou trois points.
- Ce qu’est un commit de fusion avec deux parents, dans Synchroniser de façon réfléchie : les deux personnes créent un commit en même temps.
Ce qu’il vous faut sur votre ordinateur
-
Le dossier
~/projects/nextia-learning-labs. Vous l’avez cloné dans Les étapes d’une demande de tirage : préparer le laboratoire d’exercice. S’il manque, faites l’étape 2 de cette section. -
Réinitialisez le laboratoire pratique pour obtenir les mêmes résultats que dans cette leçon. Dans
~/projects, exécutez :Fenêtre de terminal sh nextia-learning-labs/C14/setup.sh m03 --reset -
Allez ensuite dans
~/projects/c14-lab/ticket-notes.git statusafficheOn branch main(sur la branche main) etworking tree clean(répertoire de travail propre). La brancheadd-faq, que vous fusionnerez de trois façons, se trouve dans votre clone. Le script l’a créée. -
La fusion, le squash et le rebase de cette leçon créent de nouveaux commits. Vos nouvelles empreintes diffèrent de celles présentées ici. Les empreintes des commits de préparation, comme
6b53600, sont les mêmes.
Une branche, trois façons de l’intégrer à main
Section intitulée « Une branche, trois façons de l’intégrer à main »La branche add-faq d’Amira ajoute un nouveau fichier, faq.txt, en deux commits. Elle a créé la branche à partir de e058b3f. Ensuite, Sam a ajouté un commit à main :
git log --oneline --graph main add-faq* 2e9e37c (HEAD -> main, origin/main, origin/HEAD) Add the on-call number| * 6c3828f (origin/add-faq, add-faq) Add a network question to the FAQ| * 6b53600 Add FAQ|/* e058b3f Add ticket 103main et add-faq ont divergé : chacune possède un commit que l’autre n’a pas. Un service hébergé propose trois façons d’intégrer add-faq à main. Toutes donnent les mêmes fichiers. Elles donnent des historiques différents.
- Commit de fusion. Git crée un nouveau commit avec deux parents : le dernier commit de
mainet le dernier commit de la branche. Les commits de la branche conservent leurs empreintes et font désormais partie demain. Vous avez fait cela avecgit merge --no-ffdans la première leçon de ce module. - Squash. Un squash (regroupement de commits) combine tous les changements d’une branche en un nouveau commit sur la branche cible. Le nouveau commit a un seul parent. Les commits de la branche ne font pas partie de
main. - Rebase. Un rebase (rebasage) copie les commits d’une branche sur un nouveau commit de départ, un à la fois. Chaque copie est un nouveau commit avec une nouvelle empreinte. Ensuite,
mainavance jusqu’à la dernière copie par avance rapide (fast-forward).
Pourquoi un rebase change-t-il les empreintes? L’empreinte d’un commit est calculée à partir de tout son contenu, y compris l’empreinte de son parent. Une copie avec un nouveau parent est un commit différent. Elle reçoit donc une empreinte différente, même si ses changements sont identiques.
Exemple détaillé : essayer les trois méthodes côte à côte
Section intitulée « Exemple détaillé : essayer les trois méthodes côte à côte »Amira veut voir chaque résultat sans modifier main ni add-faq. Elle utilise donc des branches temporaires : de nouvelles étiquettes de branche qu’elle supprime après le test. Une branche ne coûte presque rien, et les vraies branches ne bougent pas.
1. Commit de fusion
Section intitulée « 1. Commit de fusion »Elle crée une copie de main appelée try-merge et y fusionne add-faq :
git switch -c try-merge maingit merge --no-ff --no-edit add-faqgit log --oneline --graph -5Switched to a new branch 'try-merge'Merge made by the 'ort' strategy. faq.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 faq.txt* 47b3604 (HEAD -> try-merge) Merge branch 'add-faq' into try-merge|\| * 6c3828f (origin/add-faq, add-faq) Add a network question to the FAQ| * 6b53600 Add FAQ* | 2e9e37c (origin/main, origin/HEAD, main) Add the on-call number|/* e058b3f Add ticket 103git switch -c try-merge main crée la nouvelle branche à partir de main, et non de sa branche actuelle. Le message indique into try-merge (dans try-merge) parce que la cible n’est pas main. Sur main, il indique seulement Merge branch 'add-faq' (fusionner la branche add-faq).
L’historique conserve tout : les deux commits de la FAQ, leurs empreintes d’origine et le point de jonction de la branche.
2. Squash
Section intitulée « 2. Squash »Elle crée une deuxième copie de main. git merge --squash place tous les changements de add-faq dans la zone de préparation, sans créer de commit :
git switch -c try-squash maingit merge --squash add-faqgit status --shortSwitched to a new branch 'try-squash'Automatic merge went well; stopped before committing as requestedSquash commit -- not updating HEADA faq.txtElle enregistre les changements dans un seul commit, avec un message pour toute la demande de tirage :
git commit -m "Add an FAQ with two questions"git log --oneline --graph -3[try-squash edddd6f] Add an FAQ with two questions 1 file changed, 5 insertions(+) create mode 100644 faq.txt* edddd6f (HEAD -> try-squash) Add an FAQ with two questions* 2e9e37c (origin/main, origin/HEAD, main) Add the on-call number* e058b3f Add ticket 103L’historique forme une ligne droite. edddd6f contient les changements des deux commits de la FAQ, mais a un seul parent, 2e9e37c. Les commits 6b53600 et 6c3828f ne font pas partie de cet historique. Leurs messages distincts n’y figurent plus.
3. Rebase
Section intitulée « 3. Rebase »D’abord, Amira note les empreintes des commits de la branche :
git log --oneline main..add-faq6c3828f Add a network question to the FAQ6b53600 Add FAQCette fois, elle crée une copie de add-faq, car un rebase modifie la branche actuelle. Elle effectue ensuite un rebase de la copie sur main :
git switch -c try-rebase add-faqgit rebase maingit log --oneline --graph -4Switched to a new branch 'try-rebase'Successfully rebased and updated refs/heads/try-rebase.* da418c8 (HEAD -> try-rebase) Add a network question to the FAQ* c8c08d3 Add FAQ* 2e9e37c (origin/main, origin/HEAD, main) Add the on-call number* e058b3f Add ticket 103Les deux commits ont les mêmes messages et les mêmes changements. Mais ils partent maintenant de 2e9e37c et ont de nouvelles empreintes : c8c08d3 et da418c8. Les originaux existent toujours sur add-faq :
git log --oneline main..add-faqgit log --oneline main..try-rebase6c3828f Add a network question to the FAQ6b53600 Add FAQda418c8 Add a network question to the FAQc8c08d3 Add FAQPour terminer, la branche cible avance jusqu’aux copies. Amira crée une copie de main appelée try-ff et fusionne avec --ff-only. Cette option fusionne seulement si Git peut effectuer une avance rapide. Sinon, Git s’arrête sans rien changer.
git switch -c try-ff maingit merge --ff-only try-rebaseSwitched to a new branch 'try-ff'Updating 2e9e37c..da418c8Fast-forward faq.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 faq.txtL’avance rapide a fonctionné, car après le rebase, les copies partent du dernier commit de main. Aucun commit de fusion n’a été créé.
4. Mêmes fichiers, historique différent
Section intitulée « 4. Mêmes fichiers, historique différent »Les trois résultats contiennent les mêmes fichiers. git diff --stat entre ces résultats n’affiche rien :
git diff --stat try-merge try-squashgit diff --stat try-merge try-ffL’absence de sortie signifie qu’il n’y a aucune différence. Seul l’historique diffère.
5. Supprimer les branches temporaires
Section intitulée « 5. Supprimer les branches temporaires »Amira retourne sur main. git branch -d refuse de supprimer try-merge, car son commit de fusion n’est pas dans main :
git switch maingit branch -d try-mergeSwitched to branch 'main'Your branch is up to date with 'origin/main'.error: the branch 'try-merge' is not fully mergedhint: If you are sure you want to delete it, run 'git branch -D try-merge'hint: Disable this message with "git config set advice.forceDeleteBranch false"Ces branches servent aux tests, alors elle les supprime avec -D. -D supprime une branche même si ses commits ne sont fusionnés nulle part :
git branch -D try-merge try-squash try-rebase try-ffDeleted branch try-merge (was 47b3604).Deleted branch try-squash (was edddd6f).Deleted branch try-rebase (was da418c8).Deleted branch try-ff (was da418c8).main et add-faq n’ont pas changé pendant le test.
Comparer les trois méthodes
Section intitulée « Comparer les trois méthodes »| Commit de fusion | Squash | Rebase, puis avance rapide | |
|---|---|---|---|
| Nouveaux commits sur main | 1 commit de fusion | 1 commit | 1 copie pour chaque commit de la branche |
| Commits de la branche | Conservés, avec les mêmes empreintes | Absents de main. Leurs changements sont dans le nouveau commit. |
Réécrits : copies avec de nouvelles empreintes |
| Forme de l’historique | Montre le départ et la jonction de la branche | Une ligne droite | Une ligne droite |
| Messages distincts des commits | Conservés | Remplacés par un seul message | Conservés |
| Bouton sur GitHub | Create a merge commit (créer un commit de fusion) | Squash and merge (regrouper et fusionner) | Rebase and merge (rebaser et fusionner) |
| Quand les équipes l’utilisent | Quand chaque commit compte et que l’équipe veut voir chaque demande de tirage comme un ensemble | Quand la branche contient beaucoup de petits commits ou de corrections, et qu’un commit par demande de tirage est plus clair | Quand l’équipe veut un historique droit et que chaque commit est bien organisé |
Aucune méthode n’est toujours la bonne. Beaucoup d’équipes choisissent une méthode pour toutes leurs demandes de tirage afin de garder un historique cohérent. Vérifiez quelle méthode votre équipe utilise avant de fusionner.
Exercice guidé : comparer vous-même les méthodes
Section intitulée « Exercice guidé : comparer vous-même les méthodes »Utilisez des branches temporaires. N’exécutez pas ces commandes directement sur main ni sur add-faq.
-
Dans
~/projects/c14-lab/ticket-notes, exécutezgit status. La sortie afficheOn branch mainetworking tree clean. -
Exécutez
git log --oneline main..add-faq. Notez les deux empreintes :6c3828fet6b53600. -
Essayez un commit de fusion :
Fenêtre de terminal git switch -c try-merge maingit merge --no-ff --no-edit add-faq -
Exécutez
git log --oneline --graph -5. Le dernier commit a deux parents, et6b53600et6c3828ffigurent dans le graphe. -
Essayez un squash :
Fenêtre de terminal git switch -c try-squash maingit merge --squash add-faqgit commit -m "Add an FAQ with two questions" -
Exécutez
git log --oneline --graph -3. L’historique forme une ligne droite. Les commits de la FAQ n’y figurent pas. -
Essayez un rebase sur une copie de la branche :
Fenêtre de terminal git switch -c try-rebase add-faqgit rebase mainLa sortie se termine par
Successfully rebased and updated refs/heads/try-rebase.(rebasage et mise à jour de refs/heads/try-rebase réussis). -
Exécutez
git log --oneline main..try-rebase. Comparez les empreintes avec celles de l’étape 2. Les messages sont identiques. Les empreintes diffèrent. -
Faites avancer une copie de
mainjusqu’aux commits rebasés par avance rapide :Fenêtre de terminal git switch -c try-ff maingit merge --ff-only try-rebaseLa sortie contient
Fast-forward(avance rapide). -
Comparez les fichiers des résultats :
Fenêtre de terminal git diff --stat try-merge try-squashgit diff --stat try-merge try-ffIl n’y a aucune sortie : les fichiers sont identiques.
-
Passez à
mainet supprimez les branches temporaires :Fenêtre de terminal git switch maingit branch -D try-merge try-squash try-rebase try-ff -
Exécutez
git log --oneline --graph --all -6.mainest toujours à2e9e37c, etadd-faqest toujours à6c3828f.
À vous : une branche avec un commit sans rapport
Section intitulée « À vous : une branche avec un commit sans rapport »La demande de tirage (pull request) de Sam dans la dernière leçon, add-ticket-104 → main, contient deux commits (enregistrements des changements) : e34484f Add ticket 104 et 5a15d07 Improve README wording.
D’abord, faites une prédiction pour chaque méthode :
- Combien de nouveaux commits
mainreçoit-elle? - Les empreintes
e34484fet5a15d07apparaissent-elles dans l’historique demain? - Une personne qui consulte
mainvoit-elle encore que la modification du README était un commit distinct?
Vérifiez ensuite vos prédictions avec des branches temporaires. Utilisez origin/add-ticket-104 comme branche à fusionner, et git log --oneline main..<throwaway branch> pour voir le résultat. Supprimez les branches temporaires à la fin.
Afficher une réponse modèle
git switch -c try-merge maingit merge --no-ff --no-edit origin/add-ticket-104git log --oneline main..try-merge5c41513 Merge remote-tracking branch 'origin/add-ticket-104' into try-merge5a15d07 Improve README wordinge34484f Add ticket 104git switch -c try-squash maingit merge --squash origin/add-ticket-104git commit -m "Add ticket 104"git log --oneline main..try-squashgit show --stat HEAD37a47a9 Add ticket 104 README.md | 2 +- scratch.txt | 2 ++ tickets.txt | 1 + 3 files changed, 4 insertions(+), 1 deletion(-)git switch -c try-rebase origin/add-ticket-104git rebase maingit log --oneline main..try-rebase9586a0f Improve README wording651a164 Add ticket 104Exécutez ensuite git switch main et git branch -D try-merge try-squash try-rebase.
| Nouveaux commits | e34484f et 5a15d07 dans main? |
Modification du README visible comme commit distinct? | |
|---|---|---|---|
| Commit de fusion | 3 : les deux commits et le commit de fusion | Oui | Oui |
| Écrasement | 1 | Non | Non. Elle est cachée dans « Add billet 104 » (ajouter le billet 104), avec scratch.txt. |
| Rebasage | 2 copies | Non : des copies avec de nouvelles empreintes | Oui |
Le résultat de l’écrasement est dangereux ici. Un écrasement ne supprime ni les modifications sans rapport ni les fichiers ajoutés par erreur. Il les cache dans un seul commit dont le message ne les mentionne pas. Une méthode de fusion ne remplace jamais une révision : corrigez d’abord la demande de tirage, comme dans la dernière leçon. Fusionnez-la ensuite.
Erreur courante
Après une fusion par écrasement, git branch -d refuse
Symptôme : Amira fusionne add-faq dans main par écrasement, pousse main et supprime la branche distante avec git push origin --delete add-faq. Elle supprime ensuite sa branche locale :
git branch -d add-faqerror: the branch 'add-faq' is not fully mergedhint: If you are sure you want to delete it, run 'git branch -D add-faq'hint: Disable this message with "git config set advice.forceDeleteBranch false"Cause : -d vérifie si les commits de la branche sont dans main. Après un écrasement, ils n’y sont pas : main contient un nouveau commit avec les mêmes modifications. Cependant, 6b53600 et 6c3828f ne font pas partie de son historique. Git ne peut pas voir que les modifications sont identiques, alors il refuse. git log --oneline main..add-faq affiche encore les deux commits.
Solution :
-
Vérifiez que le contenu est dans
main. Comparez le fichier dans les deux branches :Fenêtre de terminal git diff add-faq main -- faq.txtSi rien ne s’affiche,
faq.txtest identique dans les deux branches. Comparez seulement les fichiers que la branche a modifiés.mainpeut contenir d’autres nouvelles modifications absentes de la branche. -
Si rien ne s’affiche, supprimez la branche avec
git branch -D add-faq. -
Si un résultat s’affiche, arrêtez. Une partie du travail de la branche n’est pas dans
main. Trouvez pourquoi avant de supprimer quoi que ce soit.
Erreur courante
Après un rebasage, git push est rejeté
Symptôme : Amira rebase la branche add-faq elle-même sur main. Elle avait déjà poussé la branche. Maintenant, git status et git push affichent :
On branch add-faqYour branch and 'origin/add-faq' have diverged,and have 3 and 2 different commits each, respectively.To /Users/amira/projects/c14-lab/team-remote.git ! [rejected] add-faq -> add-faq (non-fast-forward)error: failed to push some refs to '/Users/amira/projects/c14-lab/team-remote.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. If you want to integrate the remote changes,hint: use 'git pull' before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.Cause : la branche distante contient encore les anciens commits, 6b53600 et 6c3828f. Sa branche contient plutôt les copies. Une poussée normale doit conserver tous les commits présents sur la branche distante, alors Git la refuse. Ne suivez pas le conseil affiché ici. Une récupération avec fusion réunirait les anciens commits et les copies, et chaque modification apparaîtrait deux fois.
Solution : déterminez d’abord si d’autres personnes utilisent la branche.
-
Si d’autres personnes utilisent la branche, ou s’il s’agit de
main: ne forcez pas la poussée. Consultez l’équipe. Habituellement, vous faites une fusion plutôt qu’un rebasage. -
Si vous êtes la seule personne à utiliser la branche : remplacez la branche distante avec
--force-with-lease:Fenêtre de terminal git push --force-with-leaseSortie To /Users/amira/projects/c14-lab/team-remote.git+ 6c3828f...68770b4 add-faq -> add-faq (forced update)
--force-with-lease remplace la branche distante seulement si elle est encore dans l’état de votre dernière récupération. Si un collègue a poussé vers la branche depuis, Git refuse, et son commit reste intact :
To /Users/amira/projects/c14-lab/team-remote.git ! [rejected] add-faq -> add-faq (stale info)error: failed to push some refs to '/Users/amira/projects/c14-lab/team-remote.git'Récupérez alors les changements et examinez son commit avant de faire autre chose. N’utilisez jamais --force-with-lease ou --force sur main.
Vérifiez votre compréhension
Faites une prédiction : Sam a add-faq dans son clone, avec 6b53600 et 6c3828f. Amira rebase add-faq et force la poussée. Sam ajoute un commit à sa branche add-faq et exécute git push. Que se passe-t-il, et quelle règle Amira a-t-elle enfreinte?
Afficher la réponse
La poussée de Sam est rejetée. La branche distante add-faq contient maintenant les copies issues du rebasage. La branche de Sam contient encore les anciens commits, donc sa poussée n’est pas une avance rapide. S’il récupère les changements et les fusionne, son historique contient les anciens commits et les copies : les mêmes modifications deux fois. Amira a enfreint cette règle : ne rebasez pas des commits que d’autres personnes ont déjà.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Vérification de fin : expliquez quel historique chaque méthode de fusion conserve ou réécrit. Utilisez add-faq comme exemple.
Afficher la réponse
- Commit de fusion : conserve tout l’historique.
6b53600et6c3828ffont maintenant partie demainavec les mêmes empreintes. Un nouveau commit de fusion avec deux parents indique où la branche a été réunie. - Écrasement : conserve les modifications, mais pas les commits de la branche.
mainreçoit un nouveau commit avec un parent et un message.6b53600et6c3828f, ainsi que leurs messages distincts, ne sont pas dansmain. - Rebasage : réécrit les commits de la branche. Git crée des copies avec les mêmes modifications et messages, mais de nouvelles empreintes, après la version la plus récente de
main. Ensuite,mainavance par une avance rapide, et aucun commit de fusion n’est créé.
Dans les trois cas, les fichiers dans main sont identiques.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Un commit de fusion conserve chaque commit de la branche et son empreinte, et ajoute un commit avec deux parents.
- Un écrasement réunit toutes les modifications d’une branche dans un nouveau commit. Les commits de la branche ne font pas partie de
main. - Un rebasage copie les commits de la branche sur un nouveau point de départ. Les copies ont de nouvelles empreintes.
- Les trois méthodes donnent les mêmes fichiers. Elles donnent des historiques différents.
- Ne rebasez pas des commits que d’autres personnes ont déjà. Utilisez
--force-with-leaseseulement sur une branche que vous seul utilisez, et jamais surmain. - Après une fusion par écrasement,
git branch -drefuse. Vérifiez le contenu avecgit diff, puis utilisez-D.
C’est la fin du module 3. Vous pouvez maintenant faire passer une modification par une demande de tirage et réviser une demande. Vous pouvez aussi choisir comment l’intégrer à main. Dans le module 4, deux personnes modifient la même ligne, et vous résolvez le conflit.
Leçon suivante : Les conflits, étape par étape