Il est facile pour moi de comprendre comment les commits Git sont implémentés, mais il est difficile de comprendre comment les autres perçoivent les commits. J'ai donc posé quelques questions à d'autres sur Mastodon.
J'ai mené une enquête très peu scientifique demandant aux gens ce qu'ils pensent des commits Git : s'agit-il d'un instantané, d'une différence ou d'une liste de tous les commits précédents ? (Bien sûr, il est raisonnable de considérer cela comme les trois, mais je suis curieux de connaître le principal
Le résultat est :
Je suis surpris de voir à quel point les ratios sont proches pour les deux options dans Différence et Instantané. Les gens ont également soulevé des points intéressants mais contradictoires, comme
« Il me semble qu'un commit est une différence, mais je pense qu'il est en fait implémenté sous forme d'instantané » et
« Il me semble qu'un commit est un instantané, mais je pense cela se présente en fait sous la forme d’une différence ». Nous parlerons plus tard de la façon dont la soumission est réellement mise en œuvre.
Avant d’aller plus loin : qu’entend-on par « une différence » ou « un instantané » ?
La "différence" dont je parle est probablement assez évidente : la différence est ce que vous obtenez lorsque vous courez git show COMMIT_ID
. Par exemple, voici une correction de faute de frappe dans le projet rbspy :
diff --git a/src/ui/summary.rs b/src/ui/summary.rs index 5c4ff9c..3ce9b3b 100644 --- a/src/ui/summary.rs +++ b/src/ui/summary.rs @@ -160,7 +160,7 @@ mod tests { "; let mut buf: Vec = Vec::new(); -stats.write(&mut buf).expect("Callgrind write failed"); +stats.write(&mut buf).expect("summary write failed"); let actual = String::from_utf8(buf).expect("summary output not utf8"); assert_eq!(actual, expected, "Unexpected summary output"); }
Vous pouvez le voir sur GitHub : https://github.com/rbspy/rbspy/commit/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b
Par « instantané », j'entends « tous les fichiers que vous obtenez lorsque vous exécutez git checkout COMMIT_ID
».
Git fait généralement référence à la liste des fichiers soumis comme une « arborescence » (comme une « arborescence de répertoires »), et vous pouvez voir tous les fichiers soumis ci-dessus sur GitHub :
https://github.com/rbspy/rbspy/tree/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b (c'est /tree/
而不是 /commit/
)
Le conseil le plus courant que j'entends à propos de l'apprentissage de Git est probablement "apprenez simplement comment Git représente les choses en interne, et tout deviendra plus clair". J’aime évidemment beaucoup cette perspective (si vous avez passé du temps à lire ce blog, vous saurez que je l’adore
Mais pour apprendre Git, cela n’a pas fonctionné aussi bien que je l’avais espéré ! Normalement, je commencerais avec enthousiasme à expliquer "D'accord, donc Git
un commit est un instantané, il a un pointeur vers son commit parent, puis une branche est un pointeur vers le commit, alors...", mais j'essaie d'aider Les gens me diront qu'ils n'ont pas vraiment trouvé cette explication utile, qu'ils ne la comprennent toujours pas. J'ai donc examiné d'autres options.
Mais parlons d’abord de la mise en œuvre interne.
En interne, Git représente les commits sous forme d'instantanés (il stocke un "arbre" de la version actuelle de chaque fichier). Je suis dans un dépôt Git, où sont vos fichiers ? J'ai écrit à ce sujet dans , mais voici un aperçu très rapide du format interne.
Il s'agit d'une représentation de soumission :
$ git cat-file -p 24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b tree e197a79bef523842c91ee06fa19a51446975ec35 parent 26707359cdf0c2db66eb1216bf7ff00eac782f65 author Adam Jensen1672104452 -0500 committer Adam Jensen1672104890 -0500 Fix typo in expectation message
Et, lorsque nous regardons cet objet arborescent, nous voyons une liste de chaque fichier/sous-répertoire sous la racine du référentiel dans ce commit :
$ git cat-file -p e197a79bef523842c91ee06fa19a51446975ec35 040000 tree 2fcc102acd27df8f24ddc3867b6756ac554b33ef.cargo 040000 tree 7714769e97c483edb052ea14e7500735c04713eb.github 100644 blob ebb410eb8266a8d6fbde8a9ffaf5db54a5fc979a.gitignore 100644 blob fa1edfb73ce93054fe32d4eb35a5c4bee68c5bf5ARCHITECTURE.md 100644 blob 9c1883ee31f4fa8b6546a7226754cfc84ada5726CODE_OF_CONDUCT.md 100644 blob 9fac1017cb65883554f821914fac3fb713008a34CONTRIBUTORS.md 100644 blob b009175dbcbc186fb8066344c0e899c3104f43e5Cargo.lock 100644 blob 94b87cd2940697288e4f18530c5933f3110b405bCargo.toml
Cela signifie que la vérification d’un commit Git est toujours rapide : il est tout aussi facile pour Git de vérifier le commit d’hier que de vérifier un million de commits il y a un million. Git n'a jamais besoin de réappliquer 10 000 différences pour déterminer l'état actuel, car les validations ne sont jamais stockées sous forme de différences.
Je viens de mentionner qu'un commit Git est un instantané, mais quand quelqu'un dit "À mon avis, un commit est un instantané, mais je pense que c'est une différence dans l'implémentation"
, c'est aussi vrai ! Les commits Git
ne sont pas représentés sous la forme de différences auxquelles vous pourriez être habitué (ils ne sont pas stockés sur le disque en tant que différence par rapport au commit précédent), mais l'intuition de base est que si vous voulez faire un 10 000
fichier en ligne L'édition 500 fois, puis le stockage de 500 fichiers seront inefficaces.
Git dispose d'un moyen de stocker des fichiers sous forme de différences. C'est ce qu'on appelle un "packfile" et Git collectera périodiquement vos données dans un packfile pour économiser de l'espace disque. Git compresse également les données lorsque vous git clone
un référentiel.
Je n'ai pas assez d'espace ici pour expliquer en détail le fonctionnement des packfiles (« Unpacking Git packfiles » d'Aditya Mukerjee est mon article préféré pour expliquer comment ils fonctionnent). Cependant, je peux résumer brièvement ma compréhension du fonctionnement des deltas et de la manière dont ils diffèrent des diff ici :
Ce qui se passe réellement lorsque nous exécutons git show SOME_COMMIT
pour voir la différence d'un commit est un peu contre-intuitif. Ma compréhension est la suivante :
Ainsi, Git convertira les modifications en un instantané, puis calculera la différence. Cela semble un peu bizarre parce que cela commence par quelque chose comme une différence et se termine par autre chose comme une différence, mais la quantité de changement et la différence sont en réalité complètement différentes, donc c'est logique.
Cela dit, je pense que Git stocke les commits sous forme d'instantanés et le packfile n'est qu'un détail d'implémentation pour économiser de l'espace disque et accélérer le clonage. Je n'ai jamais eu besoin de savoir comment fonctionne packfile, mais cela m'aide à comprendre comment les instantanés Git sont validés sans occuper trop d'espace disque.
Je pense qu’une compréhension assez courante des « erreurs » de Git est la suivante :
Cette compréhension est bien sûr fausse (en réalité, les commits sont stockés sous forme d'instantanés et les différences sont calculées à partir de ces instantanés), mais cela me semble très utile et logique ! C'est un peu bizarre quand on pense aux commits de fusion, mais peut-être pourrions-nous dire que c'est juste la différence basée sur le premier commit parent du commit de fusion.
Je pense que ce malentendu est parfois très utile, et cela ne semble pas poser de problème pour une utilisation quotidienne de Git. J'aime vraiment le fait que les choses que nous utilisons le plus (différences) soient les éléments les plus fondamentaux - c'est très intuitif pour moi.
J'ai également réfléchi à d'autres compréhensions utiles mais « fausses » de Git, telles que :
Je pense qu'il existe une gamme de «mauvaises» compréhensions de Git qui ont beaucoup de sens, sont largement prises en charge par l'interface utilisateur de Git et ne posent pas de problèmes dans la plupart des cas. Mais cela peut devenir déroutant lorsque vous souhaitez annuler une modification ou que quelque chose ne va pas.
Même si je sais que les commits sont des instantanés dans Git, je les traite probablement comme des différences la plupart du temps parce que :
git show
donc c'est juste quelque chose que j'ai l'habitude de voir Mais je considère aussi parfois les commits comme des instantanés car :
git checkout COMMIT_ID
(l'idée de réappliquer 10 000 commits me stresse) Certaines réponses de Mastodon mentionnent également :
Quelques autres mots que les gens utilisent pour parler de commits qui pourraient être moins ambigus :
Il m'est difficile de comprendre les différentes compréhensions que les gens ont de Git. Ce qui est particulièrement délicat, c'est que, même si les « mauvaises » compréhensions sont souvent très utiles, les gens se méfient tellement des « mauvais » modèles mentaux qu'ils hésitent à partager leurs « fausses » idées, de peur qu'un interprète de Git ne se lève. Sortez et expliquez-leur pourquoi ils ont tort. (Ces interprètes Git
ont généralement de bonnes intentions, mais cela peut quand même avoir un impact négatif)
Merci à Marco Rogers, Marie Flanagan et à tout le monde chez Mastodon d'avoir parlé des commits Git avec moi.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!