-
1. DĂ©marrage rapide
-
2. Les bases de Git
-
3. Les branches avec Git
-
4. Git sur le serveur
- 4.1 Protocoles
- 4.2 Installation de Git sur un serveur
- 4.3 Génération des clés publiques SSH
- 4.4 Mise en place du serveur
- 4.5 DĂ©mon (Daemon) Git
- 4.6 HTTP intelligent
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Git hébergé
- 4.10 Résumé
-
5. Git distribué
-
6. GitHub
-
7. Utilitaires Git
- 7.1 SĂ©lection des versions
- 7.2 Indexation interactive
- 7.3 Remisage et nettoyage
- 7.4 Signer votre travail
- 7.5 Recherche
- 7.6 RĂ©Ă©crire lâhistorique
- 7.7 Reset démystifié
- 7.8 Fusion avancée
- 7.9 Rerere
- 7.10 DĂ©boguer avec Git
- 7.11 Sous-modules
- 7.12 Empaquetage (bundling)
- 7.13 Replace
- 7.14 Stockage des identifiants
- 7.15 Résumé
-
8. Personnalisation de Git
- 8.1 Configuration de Git
- 8.2 Attributs Git
- 8.3 Crochets Git
- 8.4 Exemple de politique gérée par Git
- 8.5 Résumé
-
9. Git et les autres systĂšmes
- 9.1 Git comme client
- 9.2 Migration vers Git
- 9.3 Résumé
-
10. Les tripes de Git
- 10.1 Plomberie et porcelaine
- 10.2 Les objets de Git
- 10.3 Références Git
- 10.4 Fichiers groupés
- 10.5 La refspec
- 10.6 Les protocoles de transfert
- 10.7 Maintenance et récupération de données
- 10.8 Les variables dâenvironnement
- 10.9 Résumé
-
A1. Annexe A: Git dans dâautres environnements
- A1.1 Interfaces graphiques
- A1.2 Git dans Visual Studio
- A1.3 Git dans Visual Studio Code
- A1.4 Git dans IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git dans Sublime Text
- A1.6 Git dans Bash
- A1.7 Git dans Zsh
- A1.8 Git dans PowerShell
- A1.9 Résumé
-
A2. Annexe B: Embarquer Git dans vos applications
- A2.1 Git en ligne de commande
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Commandes Git
- A3.1 Installation et configuration
- A3.2 Obtention et création des projets
- A3.3 Capture dâinstantanĂ© basique
- A3.4 Création de branches et fusion
- A3.5 Partage et mise Ă jour de projets
- A3.6 Inspection et comparaison
- A3.7 DĂ©bogage
- A3.8 Patchs
- A3.9 Courriel
- A3.10 SystĂšmes externes
- A3.11 Administration
- A3.12 Commandes de plomberie
3.6 Les branches avec Git - Rebaser (Rebasing)
Rebaser (Rebasing)
Dans Git, il y a deux façons dâintĂ©grer les modifications dâune branche dans une autre : en fusionnant (merge
) et en rebasant (rebase
).
Dans ce chapitre, vous apprendrez la signification de rebaser, comment le faire, pourquoi câest un outil incroyable et dans quels cas il est dĂ©conseillĂ© de lâutiliser.
Les bases
Si vous revenez à un exemple précédent du chapitre Fusions (Merges), vous remarquerez que votre travail a divergé et que vous avez ajouté des commits sur deux branches différentes.
Comme nous lâavons dĂ©jĂ expliquĂ©, le moyen le plus simple pour intĂ©grer ces branches est la fusion via la commande merge
.
Cette commande rĂ©alise une fusion Ă trois branches entre les deux derniers instantanĂ©s (snapshots) de chaque branche (C3 et C4) et lâancĂȘtre commun le plus rĂ©cent (C2), crĂ©ant un nouvel instantanĂ© (et un commit).
Cependant, il existe un autre moyen : vous pouvez prendre le patch de la modification introduite en C4
et le réappliquer sur C3
.
Dans Git, cette action est appelée "rebaser" (rebasing).
Avec la commande rebase
, vous pouvez prendre toutes les modifications qui ont été validées sur une branche et les rejouer sur une autre.
Dans cet exemple, vous lanceriez les commandes suivantes :
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Cela fonctionne en cherchant lâancĂȘtre commun le plus rĂ©cent des deux branches (celle sur laquelle vous vous trouvez et celle sur laquelle vous rebasez), en rĂ©cupĂ©rant toutes les diffĂ©rences introduites par chaque commit de la branche courante, en les sauvant dans des fichiers temporaires, en rĂ©initialisant la branche courante sur le mĂȘme commit que la branche de destination et en appliquant finalement chaque modification dans le mĂȘme ordre.
C4
sur C3
Ă ce moment, vous pouvez retourner sur la branche master
et réaliser une fusion en avance rapide (fast-forward merge).
$ git checkout master
$ git merge experiment
master
Ă prĂ©sent, lâinstantanĂ© pointĂ© par C4'
est exactement le mĂȘme que celui pointĂ© par C5
dans lâexemple de fusion.
Il nây a pas de diffĂ©rence entre les rĂ©sultats des deux types dâintĂ©gration, mais rebaser rend lâhistorique plus clair.
Si vous examinez le journal de la branche rebasĂ©e, elle est devenue linĂ©aire : toutes les modifications apparaissent en sĂ©rie mĂȘme si elles ont eu lieu en parallĂšle.
Vous aurez souvent Ă faire cela pour vous assurer que vos commits sâappliquent proprement sur une branche distante â par exemple, sur un projet oĂč vous souhaitez contribuer mais que vous ne maintenez pas.
Dans ce cas, vous réaliseriez votre travail dans une branche puis vous rebaseriez votre travail sur origin/master
quand vous ĂȘtes prĂȘt Ă soumettre vos patchs au projet principal.
De cette maniĂšre, le mainteneur nâa pas Ă rĂ©aliser de travail dâintĂ©gration â juste une avance rapide ou simplement une application propre.
Il faut noter que lâinstantanĂ© pointĂ© par le commit final, quâil soit le dernier des commits dâune opĂ©ration de rebasage ou le commit final issu dâune fusion, sont en fait le mĂȘme instantanĂ© â câest juste que lâhistorique est diffĂ©rent. Rebaser rejoue les modifications dâune ligne de commits sur une autre dans lâordre dâapparition, alors que la fusion joint et fusionne les deux tĂȘtes.
Rebases plus intéressants
Vous pouvez aussi faire rejouer votre rebasage sur autre chose quâune branche.
Prenez un historique tel que Un historique avec deux branches thĂ©matiques qui sortent lâune de lâautre par exemple.
Vous avez créé une branche thématique (server
) pour ajouter des fonctionnalités cÎté serveur à votre projet et avez réalisé un commit.
Ensuite, vous avez créé une branche pour ajouter des modifications cÎté client (client
) et avez validé plusieurs fois.
Finalement, vous avez rebasculé sur la branche server
et avez réalisé quelques commits supplémentaires.
Supposons que vous dĂ©cidez que vous souhaitez fusionner vos modifications du cĂŽtĂ© client dans votre ligne principale pour une publication (release) mais vous souhaitez retenir les modifications de la partie serveur jusquâĂ ce quâelles soient un peu mieux testĂ©es.
Vous pouvez récupérer les modifications du cÎté client qui ne sont pas sur le serveur (C8
et C9
) et les rejouer sur la branche master
en utilisant lâoption --onto
de git rebase
 :
$ git rebase --onto master server client
Cela signifie en substance "Extraire la branche client, dĂ©terminer les patchs depuis lâancĂȘtre commun des branches client
et server
puis les rejouer sur master
".
Câest assez complexe, mais le rĂ©sultat est assez impressionnant.
Maintenant, vous pouvez faire une avance rapide sur votre branche master
(cf. Avance rapide sur votre branche master
pour inclure les modifications de la branche client):
$ git checkout master
$ git merge client
master
pour inclure les modifications de la branche clientSupposons que vous décidiez de tirer (pull) votre branche server
aussi.
Vous pouvez rebaser la branche server
sur la branche master
sans avoir Ă lâextraire avant en utilisant git rebase [branchedebase] [branchethematique]
â qui extrait la branche thĂ©matique (dans notre cas, server
) pour vous et la rejoue sur la branche de base (master
)Â :
$ git rebase master server
Cette commande rejoue les modifications de server
sur le sommet de la branche master
, comme indiqué dans Rebasage de la branche server sur le sommet de la branche master
.
master
Vous pouvez ensuite faire une avance rapide sur la branche de base (master
)Â :
$ git checkout master
$ git merge server
Vous pouvez effacer les branches client
et server
une fois que tout le travail est intĂ©grĂ© et que vous nâen avez plus besoin, Ă©liminant tout lâhistorique de ce processus, comme visible sur Historique final des commits :
$ git branch -d client
$ git branch -d server
Les dangers du rebasage
Ah⊠mais les joies de rebaser ne viennent pas sans leurs contreparties, qui peuvent ĂȘtre rĂ©sumĂ©es en une ligne :
Ne rebasez jamais des commits qui ont déjà été poussés sur un dépÎt public.
Si vous suivez ce conseil, tout ira bien. Sinon, de nombreuses personnes vont vous haïr et vous serez méprisé par vos amis et votre famille.
Quand vous rebasez des données, vous abandonnez les commits existants et vous en créez de nouveaux qui sont similaires mais différents.
Si vous poussez des commits quelque part, que dâautres les tirent et se basent dessus pour travailler, et quâaprĂšs coup, vous rĂ©Ă©crivez ces commits Ă lâaide de git rebase
et les poussez à nouveau, vos collaborateurs devront re-fusionner leur travail et les choses peuvent rapidement devenir trÚs désordonnées quand vous essaierez de tirer leur travail dans votre dépÎt.
Examinons un exemple expliquant comment rebaser un travail déjà publié sur un dépÎt public peut générer des gros problÚmes. Supposons que vous clonez un dépÎt depuis un serveur central et réalisez quelques travaux dessus. Votre historique de commits ressemble à ceci :
Ă prĂ©sent, une autre personne travaille et inclut une fusion, puis elle pousse ce travail sur le serveur central. Vous le rĂ©cupĂ©rez et vous fusionnez la nouvelle branche distante dans votre copie, ce qui donne lâhistorique suivant :
Ensuite, la personne qui a poussé le travail que vous venez de fusionner décide de faire marche arriÚre et de rebaser son travail.
Elle lance un git push --force
pour forcer lâĂ©crasement de lâhistorique sur le serveur.
Vous récupérez alors les données du serveur, qui vous amÚnent les nouveaux commits.
Vous ĂȘtes dĂ©sormais tous les deux dans le pĂ©trin.
Si vous faites un git pull
, vous allez créer un commit de fusion incluant les deux historiques et votre dépÎt ressemblera à ça :
Si vous lancez git log
lorsque votre historique ressemble Ă ceci, vous verrez deux commits qui ont la mĂȘme date dâauteur et les mĂȘmes messages, ce qui est dĂ©routant.
De plus, si vous poussez cet historique sur le serveur, vous réintroduirez tous ces commits rebasés sur le serveur central, ce qui va encore plus dérouter les autres développeurs.
Câest plutĂŽt logique de prĂ©sumer que lâautre dĂ©veloppeur ne souhaite pas voir apparaĂźtre C4
et C6
dans lâhistorique.
Câest la raison pour laquelle il avait effectuĂ© un rebasage initialement.
Rebaser quand vous rebasez
Si vous vous retrouvez effectivement dans une situation telle que celle-ci, Git dispose dâautres fonctions magiques qui peuvent vous aider. Si quelquâun de votre Ă©quipe pousse de force des changements qui Ă©crasent des travaux sur lesquels vous vous ĂȘtes basĂ©s, votre dĂ©fi est de dĂ©terminer ce qui est Ă vous et ce qui a Ă©tĂ© rĂ©Ă©crit.
Il se trouve quâen plus de lâempreinte SHA du commit, Git calcule aussi une empreinte qui est uniquement basĂ©e sur le patch introduit avec le commit. Ceci est appelĂ© un "identifiant de patch" (patch-id).
Si vous tirez des travaux qui ont été réécrits et les rebasez au-dessus des nouveaux commits de votre collÚgue, Git peut souvent déterminer ceux qui sont uniquement les vÎtres et les réappliquer au sommet de votre nouvelle branche.
Par exemple, dans le scĂ©nario prĂ©cĂ©dent, si au lieu de fusionner quand nous Ă©tions Ă lâĂ©tape Quelquâun pousse des commits rebasĂ©s, en abandonnant les commits sur lesquels vous avez fondĂ© votre travail nous exĂ©cutons la commande git rebase teamone/master
, Git va :
-
DĂ©terminer quels travaux sont uniques Ă notre branche (C2, C3, C4, C6, C7)
-
DĂ©terminer ceux qui ne sont pas des commits de fusion (C2, C3, C4)
-
DĂ©terminer ceux qui nâont pas Ă©tĂ© rĂ©Ă©crits dans la branche de destination (uniquement C2 et C3 puisque C4 est le mĂȘme patch que C4')
-
Appliquer ces commits au sommet de
teamone/master
Ainsi, au lieu du rĂ©sultat que nous avons observĂ© au chapitre Vous fusionnez le mĂȘme travail une nouvelle fois dans un nouveau commit de fusion, nous aurions pu finir avec quelque chose qui ressemblerait davantage Ă Rebaser au-dessus de travaux rebasĂ©s puis que lâon a poussĂ© en forçant.
Cela fonctionne seulement si les commits C4 et C4' de votre collĂšgue correspondent presque exactement aux mĂȘmes modifications. Autrement, le rebasage ne sera pas capable de dĂ©terminer quâil sâagit dâun doublon et va ajouter un autre patch similaire Ă C4 (ce qui Ă©chouera probablement puisque les changements sont au moins partiellement dĂ©jĂ prĂ©sents).
Vous pouvez également simplifier tout cela en lançant un git pull --rebase
au lieu dâun git pull
normal.
Vous pouvez encore le faire manuellement Ă lâaide dâun git fetch
suivi dâun git rebase team1/master
dans le cas présent.
Si vous utilisez git pull
et voulez faire de --rebase
le traitement par défaut, vous pouvez changer la valeur du paramÚtre de configuration pull.rebase
par git config --global pull.rebase true
.
Si vous considĂ©rez le fait de rebaser comme un moyen de nettoyer et rĂ©arranger des commits avant de les pousser et si vous vous en tenez Ă ne rebaser que des commits qui nâont jamais Ă©tĂ© publiĂ©s, tout ira bien. Si vous tentez de rebaser des commits dĂ©jĂ publiĂ©s sur lesquels les gens ont dĂ©jĂ basĂ© leur travail, vous allez au devant de gros problĂšmes et votre Ă©quipe vous en tiendra rigueur.
Si vous ou lâun de vos collĂšgues y trouve cependant une quelconque nĂ©cessitĂ©, assurez-vous que tout le monde sache lancer un git pull --rebase
pour essayer de rendre les choses un peu plus faciles.
Rebaser ou Fusionner
Maintenant que vous avez vu concrĂštement ce que signifient rebaser et fusionner, vous devez vous demander ce quâil est prĂ©fĂ©rable dâutiliser. Avant de pouvoir rĂ©pondre Ă cela, revenons quelque peu en arriĂšre et parlons un peu de ce que signifie un historique.
On peut voir lâhistorique des commits de votre dĂ©pĂŽt comme un enregistrement de ce quâil sâest rĂ©ellement passĂ©. Il sâagit dâun document historique qui a une valeur en tant que tel et ne doit pas ĂȘtre altĂ©rĂ©. Sous cet angle, modifier lâhistorique des commits est presque blasphĂ©matoire puisque vous mentez sur ce quâil sâest rĂ©ellement passĂ©. Dans ce cas, que faire dans le cas dâune sĂ©rie de commits de fusions dĂ©sordonnĂ©s ? Cela reflĂšte ce quâil sâest passĂ© et le dĂ©pĂŽt devrait le conserver pour la postĂ©ritĂ©.
Le point de vue inverse consiste Ă considĂ©rer que lâhistorique des commits est le reflet de la façon dont votre projet a Ă©tĂ© construit. Vous ne publieriez jamais le premier brouillon dâun livre et le manuel de maintenance de votre projet mĂ©rite une rĂ©vision attentive. Ceci constitue le camp de ceux qui utilisent des outils tels que le rebasage et les branches filtrĂ©es pour raconter une histoire de la meilleure des maniĂšres pour les futurs lecteurs.
DĂ©sormais, nous espĂ©rons que vous comprenez quâil nâest pas si simple de rĂ©pondre Ă la question portant sur le meilleur outil entre fusion et rebasage. Git est un outil puissant et vous permet beaucoup de manipulations sur et avec votre historique mais chaque Ă©quipe et chaque projet sont diffĂ©rents. Maintenant que vous savez comment fonctionnent ces deux outils, câest Ă vous de dĂ©cider lequel correspond le mieux Ă votre situation en particulier.
De maniĂšre gĂ©nĂ©rale, la maniĂšre de profiter au mieux des deux mondes consiste Ă rebaser des modifications locales que vous avez effectuĂ©es mais qui nâont pas encore Ă©tĂ© partagĂ©es avant de les pousser de maniĂšre Ă obtenir un historique propre mais sans jamais rebaser quoi que ce soit que vous ayez dĂ©jĂ poussĂ© quelque part.