28 points par GN⁺ 2023-11-24 | 1 commentaires | Partager sur WhatsApp
  • Beaucoup de gens estiment que la manière dont fonctionnent les branches Git n’est pas intuitive
  • L’article explique l’écart entre le modèle intuitif courant des branches Git et la façon dont les branches sont réellement représentées en interne dans Git
  • Il montre que le modèle intuitif et le fonctionnement réel de Git sont en fait très étroitement liés
  • Il discute des limites du modèle intuitif et des raisons pour lesquelles il peut poser problème

Modèle intuitif des branches

  • Beaucoup de gens comparent les branches à « des branches d’un pommier ».
  • Dans Git, une branche n’a pas de notion de « parent », ce qui diffère de l’idée qu’elle serait dérivée de main.

Dans Git, une branche correspond à tout l’historique

  • Dans Git, une branche n’est pas simplement un commit séparé : elle inclut tout l’historique de tous les commits précédents.
  • À travers un dépôt d’exemple, l’article montre que main et mybranch ont tous deux 4 commits.

Les branches sont stockées sous forme d’ID de commit

  • En interne, dans Git, une branche est stockée sous la forme d’un petit fichier texte contenant un ID de commit.
  • Le commit le plus récent de chaque branche est enregistré dans ce fichier.
  • Comme il n’existe pas de relation parent-enfant entre les branches, Git ne connaît pas les relations entre elles.

L’intuition des gens n’est généralement pas si fausse

  • Dire que l’intuition des gens sur Git est « fausse » est quelque peu absurde.
  • Même un modèle « faux » peut en pratique être utile.

Rebase utilise lui aussi la notion « intuitive » de branche

  • Le rebase ne réapplique sur main que les commits de la branche « intuitive ».
  • Le résultat du rebase correspond au modèle intuitif.

Merge utilise lui aussi la notion « intuitive » de branche

  • Le merge ne copie pas les commits, mais il a besoin d’un commit de base partagé.
  • La merge base permet de retrouver le commit à partir duquel les branches du modèle intuitif ont divergé.

Les pull requests GitHub utilisent aussi cette idée intuitive

  • Quand on crée sur GitHub une pull request pour fusionner mybranch dans main, seuls les commits de la branche intuitive sont affichés.

L’intuition est utile, mais elle a ses limites

  • La définition intuitive d’une branche correspond bien au travail réel avec Git, mais Git ne perçoit pas différemment une branche issue de main.

Tronc et branches dérivées

  • Les gens perçoivent main et mybranch différemment, et cela influence leur manière d’utiliser Git.
  • Git ne fait pas la différence entre une branche qui serait une « dérivation » d’une autre et n’importe quelle autre branche.

Git peut faire un rebase « à l’envers »

  • Comme Git ne sait pas si une branche est une « dérivation » d’une autre, c’est à l’utilisateur de savoir quelle branche doit être rebasée et à quel moment.
  • git rebase main et le rebase inverse git rebase mybranch sont tous deux possibles. Même chose pour le merge

L’absence de hiérarchie entre les branches Git est un peu étrange

  • Dire que la branche main n’a rien de spécial vient du fait que Git ne reconnaît pas les relations entre les branches.
  • Il existe bien des relations entre les branches, mais Git n’en sait rien

L’interface des branches Git est elle aussi étrange

  • Si l’on veut ne voir que les commits « dérivés », la manière d’utiliser git log et git diff est différente.

Sur GitHub, la branche par défaut est spéciale

  • GitHub possède une « branche par défaut », qui joue un rôle particulier.

L’avis de GN⁺

L’idée la plus importante de cet article est de comprendre la différence entre l’intuition qu’ont les gens des branches Git et le fonctionnement réel de Git. Cet article aidera les ingénieurs logiciel débutants à mieux comprendre le concept de branche Git et à l’utiliser plus efficacement. Comprendre comment le modèle intuitif des branches Git correspond au travail réel, et comment Git ne gère pas les relations entre branches, est à la fois intéressant et instructif.

1 commentaires

 
GN⁺ 2023-11-24
Avis Hacker News
  • Une branche est un pointeur vers un commit, et ce pointeur est mis à jour chaque fois qu’un nouveau commit est créé. On peut considérer une branche comme un nom flottant, à la manière d’un tag. Comme le commit lui-même pointe vers son commit parent, une branche est en réalité une chaîne de commits liés avec un point d’entrée nommé. Quand on supprime une branche, il ne reste plus de libellé nommé, seulement la chaîne de commits associés.
  • Il est plus facile de comprendre la lignée des commits si on la voit comme des pointeurs qui regardent « en arrière » plutôt que « vers l’avant ». Une branche étant un ID de commit, on peut retrouver tout l’historique de cette branche en remontant les liens vers les parents. Le « point de branchement » est l’endroit où deux chaînes de commits se rejoignent, et un commit de fusion est particulier car il indique que deux historiques ont été réunis en un seul.
  • Dans des projets personnels, mes amis s’énervent parfois quand ils me voient manipuler les changements et les pointeurs de branche avec git reset --hard et git stash. Pour annuler une mauvaise fusion, j’utilise git reset --hard <dernier commit avant la fusion>, et pour appliquer sur la branche principale de petites modifications d’une branche locale, j’utilise git stash, puis je bascule sur la branche principale pour faire git stash apply.
  • Git n’a pas de notion selon laquelle « main est spéciale », mais des outils comme GitLab proposent des branches protégées pour réduire les erreurs. L’idée de branches « parentes » et « enfants » pourrait en fait être intéressante, et il faudrait prendre en charge plusieurs branches « parentes » pour les branches de support à long terme.
  • Lors d’une fusion, d’un rebase ou d’une pull request, il faut indiquer explicitement l’autre branche. Git ne sait pas quelle branche vous considérez comme branche de base. Il arrive qu’on veuille fusionner une branche de fonctionnalité dans une autre, donc il faut préciser clairement quelle branche doit être fusionnée dans quelle autre.
  • Même si l’intuition que les gens ont est techniquement en partie erronée, il y a de bonnes raisons pour lesquelles ils ont cette intuition.
  • Il existe un tutoriel interactif destiné aux personnes qui savent utiliser git add et git commit. Il aide à visualiser les branches pendant la lecture.
  • Si l’on se rappelle qu’en exécutant une commande Git on modifie « toujours » la branche courante, la syntaxe de Git devient « facile » à comprendre. Par exemple, git merge my-branch fusionne my-branch dans la branche courante, et git rebase my-branch rebase la branche courante sur my-branch.
  • Il serait utile qu’une branche (HEAD) ait une « queue » pointant vers le commit de base où cette branche a commencé. Comme les branches sont souvent rebasées, il arrive qu’on doive réfléchir à leur point de départ. Ce serait plus pratique si Git indiquait que le commit de base appartient à main.
  • Quand on envoie des « patchs » sur une mailing list, on peut inclure facultativement un commit de base. En effet, il n’est pas toujours clair si les changements reposent sur la dernière release, sur la branche principale de développement ou sur une branche d’intégration. Il faut aussi garder la base en tête quand on utilise git range-diff. Cet outil compare deux plages comme main..previous et main..current.
  • En relisant mes opinions personnelles sur les branches, j’ai réappris plusieurs choses que j’avais oubliées.