6 points par GN⁺ 2023-11-03 | 1 commentaires | Partager sur WhatsApp
  • Même après des années d’usage, Git reste déroutant à cause de nombreux doublons et réemplois de termes, ce qui rend difficile de prévoir son comportement réel en se contentant de mémoriser les commandes
  • Des termes et symboles comme HEAD, heads, detached HEAD, ours/theirs, ../... changent de sens selon le contexte
  • index, staged et cached sont tous liés à .git/index, mais reset, revert et restore, malgré des noms proches, n’ont pas le même effet sur l’historique des commits, l’arbre de travail et les pointeurs de branche
  • Le fait que main suive un dépôt distant est distinct d’une remote-tracking branch comme origin/main, et checkout regroupe dans une seule commande le changement de branche et la restauration de fichiers
  • Pour comprendre Git, il faut regarder non seulement les commandes individuelles, mais aussi le modèle interne où s’articulent branches, références, commits, index et état du suivi distant

HEAD, branches et detached HEAD

  • Dans Git, les heads désignent les branches, qui sont stockées en interne dans le répertoire .git/refs/heads
    • Le glossaire officiel de Git distingue branch, qui inclut tous les commits au-dessus, et head, qui désigne le commit le plus récent, mais on peut aussi voir cela comme deux façons de regarder le même objet
  • HEAD pointe vers la branche courante et est stocké dans .git/HEAD
    • La formule « head est une branche, et HEAD est la branche courante » illustre bien à elle seule la confusion de la terminologie Git
  • L’état detached HEAD correspond au cas où HEAD ne pointe pas vers une branche, mais directement vers un identifiant de commit
    • Si l’on fait un checkout d’un tag, on peut se retrouver en detached HEAD, car un tag n’est pas une branche
    • Dans cet état, git pull ne fonctionne pas, et git push non plus sauf dans des cas particuliers
    • git commit, git merge, git rebase et git cherry-pick restent possibles, mais les nouveaux commits ne sont rattachés à aucune branche et peuvent devenir difficiles à retrouver
    • Créer une nouvelle branche ou revenir sur une branche existante permet de sortir de l’état detached HEAD

ours et theirs changent entre merge et rebase

  • Lors de la résolution de conflits, git checkout --ours file.txt choisit la version “ours”, mais la référence de merge et de rebase n’est pas la même
  • Avec merge, la branche courante est ours, et la branche que l’on fusionne est theirs
    • Exemple : après git checkout merge-into-ours, si l’on exécute git merge from-theirs, la branche courante est ours
  • Avec rebase, la branche courante est theirs, et la branche cible du rebase est ours
    • Exemple : après git checkout theirs, si l’on exécute git rebase ours, la branche cible devient ours
  • Cette différence est liée au fait que git rebase main consiste en interne à fusionner de façon répétée les commits de la branche courante sur une copie de la branche main
  • ourstheirs explique brièvement la terminologie ours et theirs
    • Dans VSCode, “current change” et “incoming change” peuvent prêter à la même confusion

Messages d’état distant et fast-forward

  • Your branch is up to date with 'origin/main' ne veut pas dire que la branche courante main reflète nécessairement le dernier état du dépôt distant
    • Cela signifie seulement que la branche locale est identique à origin/main tel qu’il était au moment du dernier git fetch ou git pull
    • Si le dernier fetch date d’il y a 5 jours, cela veut dire qu’elle est « à jour selon l’état d’il y a 5 jours »
  • Git enregistre dans le reflog des informations sur le moment du dernier fetch, mais le message d’état n’indique pas « à la date du dernier fetch »
  • can be fast-forwarded signifie que la branche distante est en avance sur la branche locale et qu’il n’y a aucun commit local supplémentaire, si bien qu’il suffit de déplacer le pointeur vers l’avant
    • Exemple : si main vaut A-B-C et origin/main vaut A-B-C-D-E, alors après git pull, les deux deviennent A-B-C-D-E
    • Dans ce cas, il ne peut pas y avoir de conflit de fusion
  • Si le fast-forward est impossible, c’est que les deux branches ont divergé
    • Exemple : si main contient un commit X absent de origin/main, tandis que origin/main contient D et E absents de main, le fast-forward n’est pas possible
    • Git indique alors que les deux branches ont divergé et précise le nombre de commits propres à chacune

Syntaxe de sélection de commits : ^, ~, .., ...

  • HEAD^ et HEAD~ pointent tous deux vers le commit précédent d’un cran
    • HEAD^^^, HEAD~~~ et HEAD~3 désignent tous le commit situé trois niveaux plus haut
  • Pour un merge commit, c’est-à-dire un commit à plusieurs parents, HEAD^2 et HEAD^3 ont un sens différent
    • HEAD^ désigne le premier parent
    • HEAD^2 désigne le deuxième parent
    • HEAD^3 désigne le troisième parent
    • HEAD~3 signifie le parent du parent du parent, ce qui est différent de HEAD^3
  • git log main..test et git log main...test se comportent eux aussi différemment
    • main..test affiche les commits présents uniquement dans test
    • test..main affiche les commits présents uniquement dans main
    • main...test affiche les commits présents d’un côté ou de l’autre, mais pas communs aux deux
  • Dans git diff, .. et ... sont interprétés autrement que dans git log
    • git diff test..main compare en incluant à la fois les changements de test et ceux de main
    • git diff test...main compare l’ancêtre commun avec l’extrémité de main, et n’affiche donc qu’un diff unilatéral
  • Cette confusion autour de la notation par points est aussi abordée dans pain in dots

reference, refspec, tree-ish

  • Dans Git, reference est employé avec plusieurs sens
    • des branches et tags comme main ou v0.2
    • HEAD, qui pointe vers la branche courante
    • des expressions comme HEAD^^^, interprétées comme des identifiants de commit
  • La documentation Git appelle plus rigoureusement des expressions comme HEAD^^^ des “revision parameters”, mais dans l’usage courant elles peuvent être traitées comme des références
  • Une symbolic reference est une référence qui pointe vers une autre référence, comme HEAD
    • HEAD est une notion centrale, qui influence fortement le comportement des commandes de base de Git
  • La configuration des remotes dans .git/config contient des refspec
    • Exemple : +refs/heads/main:refs/remotes/origin/main
    • Dans la pratique, on utilise souvent telle quelle la valeur créée automatiquement par git clone ou git remote add
  • tree-ish désigne ce qui peut être utilisé comme THING dans git checkout THING .
    • un identifiant de commit
    • une référence de commit comme main, HEAD^^ ou v0.3.2
    • un sous-répertoire dans un commit, comme main:./docs
    • En pratique, on peut souvent le comprendre comme « un commit ou une référence vers un commit »

index, staged, cached

  • index, staged et cached sont tous liés à .git/index
    • .git/index est le fichier utilisé lorsque git add place des modifications en staging
  • Plusieurs formulations pour le même objet sont dispersées dans différentes commandes
    • git diff --cached
    • git rm --cached
    • git diff --staged
    • .git/index
  • En revanche, les options --index et --cached n’ont pas toujours exactement le même sens
    • Un article de Junio Hamano, mainteneur principal de Git, en explique les détails
  • Pour des raisons de performance, l’index répertorie aussi les untracked files, mais on ne considère généralement pas que la staging area inclut les untracked files

reset, revert, restore

  • reset, revert et restore sont des mots proches, mais ils n’agissent pas sur les mêmes éléments dans Git
  • git revert COMMIT crée sur la branche courante un nouveau commit qui annule les effets de COMMIT
    • Si COMMIT a ajouté 3 lignes, le nouveau commit supprimera ces 3 lignes
  • git reset --hard COMMIT ramène de force la branche courante à l’état du commit COMMIT
    • C’est une opération très dangereuse, car elle supprime les changements apparus après COMMIT
  • git restore --source=COMMIT PATH rétablit les fichiers de PATH dans l’état où ils étaient dans COMMIT
    • Cela ne modifie ni les autres fichiers ni l’historique des commits
  • Utilisés seuls, git reset --hard et git restore . ont souvent un comportement proche, mais git reset --hard COMMIT et git restore --source COMMIT . font en réalité des choses totalement différentes

track, remote-tracking branch, untracked files

  • Git emploie le mot track de trois manières différentes
    • Dans git status, Untracked files: désigne les fichiers que Git ne gère pas et qui ne font partie d’aucun commit
    • Une remote-tracking branch comme origin/main est une référence locale qui contient l’identifiant du commit pointé par main sur le remote origin au moment du dernier git pull ou git fetch
    • Une branche locale peut être configurée pour track une branche distante, comme dans “branch foo set up to track remote branch bar from origin”
  • Dire que main est une branche qui suit un remote et dire que origin/main est une remote-tracking branch, ce n’est pas la même chose
  • main est une vraie branche : on peut y créer des commits et y faire des merge
    • Si .git/config est configuré pour suivre le main distant, alors git pull et git push deviennent plus simples à utiliser
  • origin/main, malgré son nom de “remote-tracking branch”, n’est pas une branche ordinaire
    • On ne peut pas y commit directement
    • Elle n’est mise à jour que lorsque git pull ou git fetch rapatrie le dernier état du main distant

checkout, reflog, merge/rebase/cherry-pick

  • git checkout regroupe deux opérations différentes dans une seule commande
    • git checkout BRANCH change de branche
    • git checkout file.txt abandonne les modifications non indexées de file.txt
  • Pour réduire cette confusion, Git a séparé ces fonctions en git switch et git restore, mais l’ancienne commande checkout reste toujours disponible
  • reflog signifie reference log et enregistre ce qu’une référence pointait dans le passé
    • reference est un terme générique qui inclut branches, tags et HEAD
    • Le reflog peut aider à se sortir de situations Git très mauvaises, par exemple si l’on a supprimé par erreur une branche importante
  • merge, rebase et cherry-pick diffèrent par leur façon de combiner des commits
    • merge crée un nouveau commit qui fusionne deux branches
    • rebase copie un par un les commits de la branche courante au-dessus de la branche cible
    • cherry-pick ressemble à rebase, mais avec une syntaxe différente, et copie des commits sur la branche courante
  • git rebase --onto main otherbranch mybranch peut servir à déplacer seulement une partie des commits de mybranch au-dessus de main
    • Dans la structure d’exemple, cela peut s’utiliser si l’on veut rebaser seulement F et G sur main
    • La syntaxe, avec trois noms de branche, est difficile à mémoriser

commit et autres termes déroutants

  • Dans Git, commit s’emploie à la fois comme verbe et comme nom
    • Verbe : « faites des commit fréquents »
    • Nom : le commit le plus récent de main
  • Un commit Git peut se comprendre de trois façons
    • comme un snapshot de l’état courant de tous les fichiers
    • comme un diff par rapport au commit parent
    • comme une history de tous les commits précédents
  • Selon la commande, Git traite le commit différemment
    • git show traite un commit comme un diff
    • git log traite un commit comme de l’historique
    • git restore traite un commit comme un snapshot
  • blob, tree, origin, upstream, downstream, la relation entre fetch et pull, ainsi que stash, worktree, subtree et submodule font aussi partie des termes Git souvent source de confusion
  • Côté GitHub, “pull request”, “squash and merge” et “rebase and merge” sont aussi souvent cités comme terminologie déroutante

Les trois grands axes de confusion récurrents

  • Les termes Git particulièrement déroutants se résument surtout à trois points
    • head est une branche, et HEAD est la branche courante
    • “remote tracking branch” et “branch that tracks a remote” sont deux choses différentes
    • index, staged et cached désignent tous le même objet
  • L’article de 2012 the most confusing git terminology explique aussi comment la terminologie Git se rattache à celle de CVS et Subversion
  • La confusion terminologique dans Git ne concerne pas seulement quelques fonctions rares, mais revient dans l’ensemble de ses fonctionnalités centrales
  • En particulier, quand on parle de branches, le mot “tracking” est utilisé de plusieurs façons, ce qui rend la différence difficile à percevoir au premier abord
  • Certaines explications incluent des recoins peu utilisés de Git, avec la réserve qu’il peut donc subsister quelques erreurs

1 commentaires

 
GN⁺ 2023-11-03
Avis de Hacker News
  • J’ai eu du mal à mémoriser la plupart de ces termes, mais ce qui a tout fait s’emboîter, c’est de comprendre que beaucoup de choses dans Git sont au final des pointeurs vers des commits.
    Les noms de branches, HEAD et les tags sont tous des pointeurs. HEAD pointe vers le commit que l’on est en train de regarder, et un nom de branche comme my-feature pointe vers le dernier commit de cette branche. Si l’on fait git checkout -b my-feature depuis main, alors main, my-feature et HEAD pointent tous vers le dernier commit de main.
    Chaque fois que l’on commit sur my-branch, HEAD et my-branch se déplacent pour pointer vers le nouveau commit. Un detached HEAD signifie qu’aucune branche ne pointe vers le commit vers lequel HEAD pointe. La différence entre un tag et une branche, c’est qu’un tag pointe vers un commit précis et ne bouge pas.
    Une autre chose qui m’a dérouté, c’est que beaucoup de commandes semblent incohérentes parce que Git suppose des arguments par défaut. git checkout file.txt équivaut à git checkout HEAD -- file.txt, et quand on est sur my-branch, git rebase main équivaut à git rebase main my-branch. La seconde forme peut toutefois être exécutée depuis une autre branche.
    Enfin, quand tout part de travers, la commande qui permet de sortir de n’importe quel état bizarre, c’est git reflog. Elle montre tous les commits vers lesquels HEAD a pointé. Cela dit, c’est agréable de voir Git reconnaître cette confusion et créer des commandes avec une interface moins surprenante et plus simple.
    • Pour utiliser le nom que Git donne à ce concept, les « pointeurs de commits » sont des refs. C’est de là que vient le nom reflog.
      Autre point auquel beaucoup d’utilisateurs de Git ne pensent pas forcément : les refs de branches ou de tags distants récupérés sont aussi toutes des refs, et on peut donc les référencer partout où un nom de ref est accepté.
      Par exemple, si l’on veut dire « peu m’importe ce qu’il y a dans cette branche, ne fais ni fast-forward, ni merge, ni rebase, écrase simplement la branche locale avec le contenu distant », on peut faire ceci : git checkout foo, git reset --hard origin/foo.
    • Plus exactement, le pointeur HEAD ne pointe pas directement vers un commit ; en général, il pointe vers un pointeur de branche comme my-branch, et ce pointeur de branche pointe vers le commit. L’état detached HEAD est l’exception.
      Quand on commit sur my-branch, HEAD et my-branch ne se déplacent pas tous les deux vers le nouveau commit : HEAD continue de pointer vers le pointeur my-branch, et seul my-branch se déplace pour pointer vers le nouveau commit. Bien sûr, si l’on suit HEAD puis my-branch jusqu’au commit, on arrive bien au nouveau commit.
      Un detached HEAD est un état dans lequel HEAD pointe directement vers un commit au lieu d’un pointeur de branche. Même en detached HEAD, HEAD et le pointeur de branche peuvent tous deux pointer vers le commit le plus récent. Avec git log --decorate, au lieu du classique (HEAD -> my-branch), on voit alors généralement quelque chose comme (HEAD, my-branch) sur le commit le plus récent.
    • J’aime bien cette explication. Cela dit, un point encore plus déroutant qu’on rencontre parfois est qu’il existe deux sortes de tags. La plupart des tags sont, comme indiqué, des tags légers qui pointent vers un objet commit, rien de plus.
      En revanche, les tags annotés peuvent contenir un message, un timestamp, un SHA, etc. Ce sont de véritables objets Git et ils se comportent assez comme des objets commit, mais ils référencent généralement tout de même un autre objet Git, le plus souvent un commit.
    • Si, comme moi, vous êtes du genre excessivement prudent, c’est aussi une bonne idée de lancer git rev-parse HEAD et de copier la chaîne obtenue dans un endroit sûr avant de commencer une procédure délicate avec beaucoup de conflits ou plusieurs déplacements.
      On a alors l’assurance d’avoir marqué précisément le dernier état dont on est sûr qu’il était correct. On peut généralement revenir au dernier état correct, et non à un point déjà situé plusieurs étapes dans le chaos. À condition, bien sûr, de ne pas avoir utilisé sans précaution des choses comme git gc ou git filter-branch.
    • Cette explication montre aussi bien que la structure en arbre interne de Git gouverne tout.
      Cela sonne comme une blague, mais ce n’en est pas vraiment une : dire qu’il faut vraiment un diplôme d’informatique pour utiliser Git n’est pas complètement faux. Je suis d’accord pour dire que les arguments par défaut de Git produisent une expérience utilisateur très incohérente.
      git checkout ., git reset --hard/soft, git cleanup ont tous l’air très similaires, mais en réalité ils sont très différents. Cela dit, je préfère toujours Git à Perforce ou SVN. À une époque, j’aimais un peu plus Mercurial, mais je ne sais pas si ce serait encore le cas aujourd’hui.
      git reflog est roi et montre la vérité.
  • Cela fait environ dix ans que Git a « gagné » la guerre des systèmes de gestion de versions grâce à une énième mode technologique sans fondement, et pourtant on bute encore sur des tâches qui devraient être triviales.
    Je me souviens avoir lu que Linus Torvalds avait été surpris que les gens commencent à utiliser Git directement sans ajouter par-dessus une couche plus conviviale. Si le secteur était capable d’introspection, il reconnaîtrait que choisir Git a été une erreur et passerait à un autre système de gestion de versions. Au lieu de gaspiller un temps fou dans un outil qui ne fait que stocker du texte.
    • Je ne vois pas Git comme un outil de stockage de texte. C’est un outil qui coordonne de manière assez fine et fiable les changements apportés par plusieurs personnes au même « texte », sans bloquer la progression de qui que ce soit. Essayez de faire ça avec Word, la différence est énorme.
      Bien sûr, il pourrait être meilleur. Mais la haine que reçoit Git me surprend toujours. Git est un outil remarquable, et il est très en avance sur des outils destinés aux non-développeurs comme Word ou Google Docs.
      C’est peut-être parce que je suis assez vieux pour me souvenir de l’époque où Subversion régnait.
    • Tout à fait d’accord.
      Chaque semaine, je vois des collègues se perdre à cause de Git, des demandes d’aide en ligne, et encore des articles comme celui-ci qui tentent de réexpliquer quelque chose qui devrait être simple.
      Je ne me souviens pas avoir vu, en codant, des gens perdre des heures à défaire le bazar qu’ils avaient créé avec SVN, TFVC, Perforce, etc.
      Les outils existent pour rendre la vie plus facile. S’ils n’y arrivent pas, ils ne valent pas le temps qu’on y consacre.
    • En 2013 encore, il n’était pas du tout évident que Git allait gagner. Il y avait beaucoup d’utilisateurs de darcs, Mercurial, svn, et même cvs, et les outils de l’époque avaient généralement des plugins pour les prendre tous en charge.

J’aimerais qu’il existe encore une telle diversité. La monoculture Git signifie aussi que toute tentative de remplacer Git doit d’abord réimplémenter Git. On se retrouve donc presque éternellement liés à Git, comme à l’ASCII ou à la touche Scroll Lock.

  • Git a gagné pour de bonnes raisons, et il était clairement meilleur que ce qui existait avant. Aujourd’hui, critiquer Git peut être à la mode, comme pour jQuery, mais à son arrivée c’était une nette amélioration.
  • J’ai récemment écrit un article sur un problème similaire, et sur les raisons pour lesquelles nous pourrions rester coincés avec Git.
    La principale inquiétude est que GitHub soit tellement bon que nous resterons attachés à Git, à moins que GitHub ne découple Git du moteur de gestion de texte et n’autorise d’autres moteurs.
    https://trolololo.xyz/github
    https://news.ycombinator.com/item?id=38098109
  • La terminologie de Git est un cas d’école : beaucoup de termes ont du sens une fois qu’on comprend déjà le fonctionnement, mais ils n’en ont presque aucun si l’on ne connaît pas les détails d’implémentation ou si on les met à côté d’autres termes.
    Ce sont des termes qui relèvent d’une abstraction fuyante.
    • C’est en partie vrai, mais c’est aussi parce que différentes parties de Git ont été créées par différentes personnes qui les ont nommées séparément. Linus appelait à l’origine cela le cache, ce qui était le terme le plus informatique.
      Ensuite, Junio l’a apparemment renommé index, un terme plus spécifique aux systèmes de gestion de versions distribués, et la plupart des utilisateurs l’ont appelé staging area. Désormais, cette évolution terminologique est fossilisée dans l’UI et les entrailles de Git.
    • Ce n’est pas que ça ait du sens, c’est juste un nommage bâclé. Trouver de bons noms demande des efforts, et les premiers développeurs de Git ne les ont pas fournis.
    • Si l’on imagine devoir connaître les entrailles du système d’exploitation, de l’IDE ou du compilateur pour une utilisation de base, c’est en fait une mauvaise conception d’API.
  • « Your branch is up to date with origin/master » est le plus gros mensonge que j’entends tous les jours. Du coup, je fais simplement pull à chaque fois.
    • Strictement parlant, c’est vrai. origin/master ne désigne pas le master qui se trouve sur origin, mais le commit vers lequel pointait le dernier origin/master connu. Cette valeur est mise à jour lors d’un fetch, et pull effectue automatiquement un fetch.
      Si tu fais ls -l .git/refs/remotes/origin/master, tu verras que origin/master n’est qu’un fichier sur ton système. Tu peux aussi voir quand il a changé. Il ne se met pas à jour magiquement.
      S’il y a des changements quand tu fais git fetch origin, l’horodatage et le contenu changent. Tu peux le vérifier avec cat .git/refs/remotes/origin/master.
      Les bases de Git sont très simples : on peut implémenter les structures de données centrales et quelques opérations en une journée. C’est vraiment précieux de le savoir. D’une manière ou d’une autre, Git a réussi à construire une interface utilisateur très complexe au-dessus d’un noyau plutôt simple.
    • Ce message signifie simplement que Git ne va pas se connecter à Internet ou au dépôt distant pour vérifier les mises à jour à moins qu’on ne le lui demande explicitement. Je trouve cette propriété souhaitable en soi. Mais le message pourrait être plus clair.
    • Il aurait dû dire quelque chose comme : « Your branch is up to date with the local copy of origin/master, last updated on XXXX-XX-XX:XX:XX:XX ».
    • Je supprime ma branche locale master. En général, soit je ne peux pas pousser vers master, soit je ne veux de toute façon pas travailler directement sur master. Dans ce cas, il n’y a aucune raison de garder une branche locale master modifiable.
      Il suffit donc de mettre à jour origin/master, et fetch suffit pour ça.
    • git fetch && git pull
      J’attends avec impatience de prendre ma retraite dans 3 ans et de me débarrasser de ce tas de déchets cassé.
  • J’utilise Git depuis presque ses tout débuts, et ce n’est que cette semaine que j’ai découvert porcelain. J’ai un projet qui parse la sortie de git status, et ajouter le flag --porcelain aide vraiment. Il produit une sortie plus concise et plus facile à parser par programme.
    En me demandant combien d’autres commandes avaient une sortie plus lisible par machine, j’ai fini par consulter la page « Git Internals - Plumbing and Porcelain » de git-scm. En résumé, Git a été écrit au départ moins comme un système de gestion de versions sophistiqué que comme une boîte à outils pour gérer la gestion de versions.
    Beaucoup de personnes qui utilisaient Git dès le début ont appris les tâches de gestion de versions avec ces commandes de bas niveau, puis ont transmis ces workflows à d’autres. C’est la couche plumbing. Plus tard, Git a créé une couche plus polie, appelée porcelain.
    Je ne sais pas encore parfaitement quelles commandes appartiennent à quelle couche, mais cela m’a aidé à comprendre les nouveaux workflows recommandés ces dernières années. Cela me permet aussi de mieux juger quand je change mon propre workflow.
    https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Po...
    • C’est un signe clair que Linus était satisfait de la structure interne, le plumbing, mais estimait que l’UI, le porcelain, devait être retravaillée. À l’origine, tout le porcelain était constitué de scripts appelant le plumbing.
      Malheureusement, pour rendre cette UI plus rapide, ils l’ont codée en dur, sans l’améliorer.
  • Julia a une très bonne voix d’écriture. Elle écrit des choses très utiles même pour des développeurs chevronnés, tout en gardant un ton chaleureux et non excluant pour les personnes qui viennent d’arriver dans le domaine.
  • Je crois que je souffre du « syndrome de Stockholm de Git ». Ces termes ne me semblent pas si déroutants que ça, mais c’est sans doute en grande partie parce que je suis profondément lié à Git depuis environ 2011, sans avoir vraiment appris beaucoup d’autres systèmes.
    Avec le recul, quand j’ai commencé, ça devait être extrêmement déroutant, mais je pense que je faisais semblant que non pour avoir l’air cool.
    • C’est en voyant la CLI du système de gestion de versions Fossil que j’ai compris que j’avais le syndrome de Stockholm. Elle était tellement intuitive.
    • Avant Git, j’utilisais cvs et svn, et comparé à svn, Git était beaucoup plus facile à comprendre.
    • Le seul autre système de gestion de versions que j’aie utilisé est Plastic SCM, et syndrome de Stockholm ou pas, Git est bien meilleur.
  • À voir aussi : la staging area de Git est littéralement un terme sur lequel tout le monde est d’accord.

https://news.ycombinator.com/item?id=28143078
« Your branch is up to date with ‘origin/main’ » est en fait un peu trompeur. On peut comprendre que la branche main est à jour, mais en réalité ce n’est pas le cas.
Cela dit, inutile d’y aller trop fort sur le « techniquement parlant ». Le problème des deux généraux ne s’applique-t-il pas ici ? Si l’on veut vraiment être pointilleux, on ne peut pas savoir si c’est « à jour » à cet instant précis. Même si l’on fait un fetch avant status, c’est pareil.
Alors, quelle serait une attente raisonnable ? Simplement que la ref dans la base d’objets de ma machine était identique, lors de la dernière vérification, à la ref d’une autre base d’objets lointaine.
Un simple appel à status ne peut pas faire un fetch réseau, ni rappeler à chaque fois les principes de base de l’outil qu’on utilise. C’est en tout cas mon avis. Pourtant, étonnamment, beaucoup de commentaires de la réponse StackOverflow semblent estimer que ces deux choses sont nécessaires.
Une formulation comme « is up to date with the origin’s main as of your last fetch 5 days ago » serait plus raisonnable, car elle indique seulement depuis combien de temps le dernier fetch a eu lieu.
[1] https://stackoverflow.com/questions/27828404/why-does-git-st...
Autre point : je pensais que ORIG_HEAD avait un rapport avec la « head d’origin », comme FETCH_HEAD. Mais non. Cette « pseudoref » semble servir de point de sauvegarde avant des réécritures plus complexes comme un rebase, et elle a été implémentée avant l’existence du reflog. Cela veut probablement dire « original head ».

  • Je suis plutôt bien disposé envers Git, mais je suis d’accord avec l’auteur sur la formulation de ce message. En revanche, je ne suis pas d’accord avec l’alternative proposée.
    « Your branch is up to date with ‘origin/main’ » est techniquement correct, mais l’expression up to date, lue rapidement, suggère deux choses : que main est identique à origin/main, et que origin/main est lui aussi à jour, c’est-à-dire qu’il a été actualisé pendant la commande ou qu’il est maintenu automatiquement à jour et que la synchronisation récente a réussi.
    Ce n’est pas un signal lisible par une machine qui doit être vrai pendant un cycle CPU, mais un message d’état affiché à l’utilisateur. Même sans invoquer la théorie des réseaux, cette interprétation est raisonnable.
    « Up to date » évoque davantage le fait d’avoir rattrapé quelque chose dans le temps que dans l’espace. La position d’une branche locale relève plutôt de l’espace — « où pointe cette branche ? » — tandis que l’état d’une ref distante relève plutôt du temps — « quand ai-je mis à jour la ref distante pour la dernière fois ? ». Bien sûr, c’est très subjectif et les deux peuvent se comprendre dans les deux sens.
    De meilleures formulations pourraient être « Your branch matches origin/main », « Your branch’s head is the same as origin/main », ou « Your branch is pointing to the same commit as origin/main ». C’est un compromis entre clarté et verbosité. On pourrait aussi y ajouter entre parenthèses, comme le propose l’auteur, « remote ref last updated 5 days ago ».
  • Je ne suis pas d’accord avec l’idée qu’un appel à status fasse un fetch réseau serait agaçant. À mon avis, les opérations réseau doivent être explicites, pas implicites. Quand je fais git status, je veux connaître l’état de mon dépôt sur le système de fichiers.
    Si je veux savoir ce qui se passe sur un autre remote, je peux faire moi-même un fetch, puis comparer.
  • Ce à quoi on s’attend, c’est d’obtenir les informations à jour au moment où la commande status est lancée. Il peut y avoir une condition de concurrence pendant que les informations sont affichées dans le terminal, mais la fenêtre temporelle est très réduite.
    En pratique, cela signifie généralement qu’on veut un fetch. Le problème des deux généraux ne s’applique qu’au côté distant. Si le remote ne reçoit pas confirmation que le client a bien reçu les données, il peut continuer à les retransmettre pendant un moment. Ce n’est pas le problème du client.
    Si le client ne reçoit pas les données du serveur, le bon comportement serait probablement de rester bloqué puis d’afficher une erreur après un timeout, ce que fetch fait sans doute déjà aujourd’hui.
  • L’étape la plus précieuse pour apprendre Git est d’avoir le bon modèle mental. Tout est graphe.
    Cet outil est souvent partagé, et il convient très bien ici aussi. Il visualise le modèle interne de Git et l’effet de plusieurs commandes.
    https://learngitbranching.js.org/
    Les 10 minutes passées avec cet outil m’ont appris davantage que mes quelque 10 années d’expérience avec Git auparavant. Je le recommande vivement.
  • Je ne veux pas en rajouter, mais le fait que les gens de Bitbucket aient inventé le terme pull request pose aussi problème.
    « pull » désigne l’action de fusionner des changements distants dans un dépôt local. Ce que l’utilisateur demande réellement, c’est que le serveur fusionne ses changements distants dans une branche. Le terme « merge request » de GitLab est le bon.
    J’ai déjà vu quelqu’un faire un fetch et voir des commits disparaître. Ils avaient été masqués parce que le dépôt était revenu en arrière dans le temps. Git cache ce genre de détails dans status, et si l’on n’utilise pas git-prompt ou powerline-shell, on travaille dans le noir.
    • L’idée d’origine, encore présente dans git-request-pull(1), était que nous ayons tous chacun notre dépôt Git quelque part comme kernel.org/git ou redhat.com/git, et que nous demandions par e-mail à d’autres de pull depuis des dépôts situés sur des serveurs, organisations et domaines différents.
      GitHub s’est inspiré de la commande request-pull de Git, mais l’a réinterprétée comme une fusion d’un dépôt GitHub vers un autre dépôt GitHub.
    • Du point de vue du serveur, l’utilisateur est le remote et le serveur est le local. L’utilisateur demande donc au serveur de pull depuis chez lui.
      En tout cas, c’est ainsi que je l’ai toujours lu.
    • Le livre Git explique l’origine de ce terme et pourquoi il a du sens.
      En résumé, ce n’est pas une requête faite à un serveur, mais à une autre personne. On demande à quelqu’un de pull sa branche pour examiner les changements que l’on souhaite contribuer au projet.

[0] https://git-scm.com/book/en/v2/Distributed-Git-Contributing-...

  • « pull request » a été popularisé par GitHub, et je trouve que c’est un terme correct. La distinction entre le fait de pull depuis un autre dépôt ou de faire une « merge request » au sein du même dépôt n’est pas vraiment intéressante. « merge request » donne l’impression d’être un terme propre à GitLab.
  • C’est ce que j’ai toujours eu le plus de mal à comprendre. Il me semble évident qu’il devrait s’agir d’une push request ou d’une merge request, et non d’une pull request.