Où commence une twig Git et quelle est sa longueur?

De temps en temps, on me demande sur quoi une twig donnée commence à git ou si un certain commit a été créé sur une twig spécifique. Le point final d’une twig est assez clair: c’est là que se trouve l’étiquette de twig. Mais – où a-t-il commencé? La réponse insignifiante serait: sur ce commit où nous avons créé cette twig. Mais pour autant que je sache maintenant, cette information est la raison pour laquelle je pose la question, perdue après les premiers commits.

Tant que nous connaissons le commit sur lequel nous sums branchés, nous pouvons dessiner le graphique pour le rendre clair:

A - B - C - - - - J [master] \ D - E - F - G [branch-A] \ H - - I [branch-B] 

J’ai créé branch-B à commit E , c’est donc le “départ”. Je le sais parce que je l’ai fait. Mais les autres peuvent-ils le reconnaître de la même manière? On pourrait dessiner le même graphique comme ça:

 A - B - C - - - - J [master] \ \ F - G [branch-A] \ / D - E \ H - I [branch-B] 

Donc, en regardant maintenant le graphique, quelle twig a commencé à E , laquelle à B ? Est-ce que commit D un membre des deux twigs ou peut-on décider clairement si elle appartient à branch-A ou branch-B?

Cela semble quelque peu philosophique, mais ce n’est vraiment pas le cas. Les superviseurs aiment parfois savoir quand une twig a été démarrée (cela marque généralement le début d’une tâche) et à quelle twig appartiennent certains changements (pour obtenir le but de certains changements – était-ce nécessaire pour le travail) et je aime savoir si git propose des infos (outils, commandes) ou des définitions pour répondre correctement à ces questions.

Dans Git, vous pourriez dire que chaque twig commence à la validation racine, ce qui serait littéralement vrai. Mais je suppose que ce n’est pas très utile pour vous. Au lieu de cela, vous pouvez définir “le début d’une twig” par rapport aux autres twigs. Une façon de le faire est d’utiliser

 git show-branch branch1 branch2 ... branchN 

et cela vous montrera le commit commun entre toutes les twigs spécifiées au bas de la sortie (s’il y a, en fait, un commit commun).

Voici un exemple de la documentation Linux Kernel Git pour show-branch

 $ git show-branch master fixes mhf * [master] Add 'git show-branch'. ! [fixes] Introduce "reset type" flag to "git reset" ! [mhf] Allow "+remote:local" refspec to cause --force when fetching. --- + [mhf] Allow "+remote:local" refspec to cause --force when fetching. + [mhf~1] Use git-octopus when pulling more than one heads. + [fixes] Introduce "reset type" flag to "git reset" + [mhf~2] "git fetch --force". + [mhf~3] Use .git/remote/origin, not .git/twigs/origin. + [mhf~4] Make "git pull" and "git fetch" default to origin + [mhf~5] Infamous 'octopus merge' + [mhf~6] Retire git-parse-remote. + [mhf~7] Multi-head fetch. + [mhf~8] Start adding the $GIT_DIR/remotes/ support. *++ [master] Add 'git show-branch'. 

Dans cet exemple, master est comparé aux fixes et mhf twigs mhf . Considérez cette sortie comme une table, chaque twig étant représentée par sa propre colonne et chaque validation obtenant sa propre ligne. Les twigs contenant un commit afficheront un + ou - dans leur colonne dans la ligne pour ce commit.

Tout en bas de la sortie, vous verrez que les 3 twigs partagent un commit commun ancêtre, et que c’est en fait la commande head de master :

 *++ [master] Add 'git show-branch'. 

Cela signifie que les fixes et mhf ont été branchés à partir de ce commit dans master .

Solutions alternatives

Bien sûr, ce n’est qu’une des manières possibles de déterminer un commit de base commun dans Git. D’autres moyens incluent git merge-base pour trouver des ancêtres communs, et git log --all --decorate --graph --oneline ou gitk --all pour visualiser les twigs et voir où elles divergent (bien qu’il y ait beaucoup de commits) cela devient difficile très rapidement).

Autres questions de l’affiche originale

En ce qui concerne ces questions, vous avez:

Est-ce que commit D un membre des deux twigs ou peut-on décider clairement si elle appartient à branch-A ou branch-B ?

D est membre des deux twigs, c’est un engagement d’ancêtre pour les deux.

Les superviseurs aiment parfois savoir quand une twig a été lancée (cela marque généralement le début d’une tâche)

Dans Git, vous pouvez réécrire l’historique de tout l’arbre de validation et de leurs twigs. Ainsi, lorsqu’une twig “démarre” n’est pas aussi bien définie que dans quelque chose comme TFS ou SVN. Vous pouvez rebase twigs à n’importe quel moment dans un arbre Git, même en le mettant avant la validation racine! Par conséquent, vous pouvez l’utiliser pour “démarrer” une tâche à n’importe quel moment dans l’arborescence souhaitée.

Ceci est un cas d’utilisation courant pour git rebase , pour synchroniser les twigs avec les dernières modifications d’une twig en amont, pour les pousser “en avant” le long du graphe de validation, comme si vous aviez “juste commencé” à travailler sur la twig, même bien que vous y ayez travaillé pendant un moment. Vous pourriez même repousser des twigs dans le temps le long du graphe de validation, si vous le vouliez (bien que vous ayez à résoudre de nombreux conflits, en fonction du contenu de la twig … ou peut-être que vous ne le feriez pas). Vous pouvez même insérer ou supprimer une twig au milieu de votre historique de développement (même si cela changerait probablement le nombre de commits). L’historique de la réécriture est l’une des principales caractéristiques de Git qui le rend si puissant et flexible.

C’est pourquoi les validations sont accompagnées à la fois d’une date de création (à l’origine de la validation de la validation) et d’une date de validation (la dernière validation de la validation dans l’arborescence de validation). Vous pouvez les considérer comme analogues à la création de date et heure et de date de dernière modification.

Les superviseurs aiment parfois savoir … à quelle twig appartiennent certains changements (pour obtenir le but de certains changements – était-ce nécessaire pour le travail).

Encore une fois, Git vous permettant de réécrire l’historique, vous pouvez (re) baser un ensemble de modifications sur n’importe quelle twig / commit du graphe de validation souhaité. git rebase vous permet littéralement de déplacer librement toute votre twig (bien que vous deviez peut-être résoudre les conflits au fur et à mesure, en fonction de l’emplacement de la twig et de ce qu’elle contient).

Cela étant dit, l’un des outils que vous pouvez utiliser dans Git pour déterminer les twigs ou les balises contenant un ensemble de modifications est le --contains :

 # Which twigs contains commit X? git branch --all --contains X # Which tags contains commit X? git tag --contains X 

L’avis de prime sur cette question demande,

Je serais intéressé de savoir si penser ou non aux twigs de Git car avoir un commit “débutant” défini autre que le commit racine est même logique?

C’est un peu le cas sauf:

  • le commit racine est “le premier commit accessible depuis la twig HEAD” (et n’oubliez pas qu’il peut y avoir plusieurs commit racine avec des twigs orphelines , utilisées par exemple dans GitHub pour les gh-pages )
  • Je préfère considérer le début d’une twig comme étant la validation d’ une autre twig à partir de laquelle cette twig a été créée (la réponse de tobib sans le ~1 ), ou (plus simple) l’ ancêtre commun .
    (également dans ” Trouver un sharepoint twigment avec Git? “, même si l’ OP mentionné n’est pas intéressé par les ancêtres communs ):

     git merge-base A master 

Cela signifie:

  • la première définition vous donne un commit fixe (qui pourrait ne jamais changer sauf en cas de filter-branch massive)
  • la deuxième définition vous donne un commit relatif (relatif à une autre twig) qui peut changer à tout moment (l’autre twig peut être supprimée)

Le second a plus de sens pour git, qui concerne la fusion et le rebasage entre les twigs.

Les superviseurs aiment parfois savoir quand une twig a été démarrée (cela marque généralement le début d’une tâche) et à quelle twig appartiennent certains changements (pour obtenir le but de certains changements – était-ce nécessaire pour le travail)?

Les twigs ne sont que le mauvais marqueur: à cause de la nature transitoire des twigs (qui peuvent être renommées / déplacées / rebasées / supprimées / …), vous ne pouvez pas imiter un “ensemble de modifications” ou une “activité” avec une twig, représenter une “tâche”.

C’est un problème XY : l’OP demande une solution tentée (où commence une twig) plutôt que le problème réel (ce qui pourrait être considéré comme une tâche dans Git).

Pour ce faire (représentant une tâche), vous pouvez utiliser:

  • tags : ils sont immuables (une fois associés à un commit, ce commit n’est plus censé être déplacé / rebasé), et tout commit entre deux tags bien nommés peut représenter une activité.
  • des git notes à un commit pour mémoriser à quel “item de travail” ledit commit a été créé (contrairement aux tags, les notes peuvent être réécrites si le commit est modifié ou rebasé).
  • hooks (pour associer un commit à un object “externe” comme un “work item”, basé sur le message de validation). C’est ce que fait le pont Git-RTC – IBM Rational Team Concert – avec un hook de pré-réception . Le point est le suivant: le début d’une twig ne reflète pas toujours le début d’une tâche, mais simplement la continuation d’un historique. qui peut changer, et qui séquence devrait représenter un ensemble logique de changements.

Peut-être que vous posez la mauvaise question. IMO, il n’est pas logique de demander où une twig démarre car une twig donnée inclut toutes les modifications apscopes à chaque fichier (c’est-à-dire depuis la validation initiale).

Par contre, demander où deux twigs ont divergé est certainement une question valable. En fait, cela semble être exactement ce que vous voulez savoir. En d’autres termes, vous ne voulez pas vraiment connaître les informations sur une seule twig. Au lieu de cela, vous voulez connaître certaines informations sur la comparaison de deux twigs.

Un peu de recherche a permis de trouver la page de manuel de gitrevisions qui décrit en détail les références à des commits et à des plages de commits spécifiques. En particulier,

Pour exclure les commits accessibles depuis un commit, une notation préfixe ^ est utilisée. Par exemple, ^ r1 r2 signifie des commits accessibles depuis r2 mais exclut ceux accessibles depuis r1.

Cette opération définie apparaît si souvent qu’il existe un raccourci pour cela. Lorsque vous avez deux commits r1 et r2 (nommés selon la syntaxe expliquée dans SPECIFYING REVISIONS ci-dessus), vous pouvez demander des commits accessibles depuis r2, à l’exception de ceux accessibles depuis r1 par ^ r1 r2. .r2.

Donc, en utilisant l’exemple de votre question, vous pouvez obtenir les commits où branch-A diverge de master avec

 git log master..branch-A 

Je pense que c’est probablement une bonne opportunité pour l’éducation. git n’enregistre pas vraiment le sharepoint départ d’une twig. A moins que le repositionnement pour cette twig ne contienne toujours l’enregistrement de création, il n’y a aucun moyen de déterminer où il a démarré, et si la twig a fusionné n’importe où, il peut en avoir plusieurs, ainsi que de nombreux points différents où il pourrait avoir été créé et a commencé à diverger de sa source d’origine.

Il pourrait être judicieux de poser une contre-question dans de tels cas – pourquoi avez-vous besoin de savoir d’où vient-il, ou est-ce que c’est important de quelle manière? Il est possible que cela ne soit pas forcément important – la plupart des raisons sont probablement liées au stream de travail spécifique que votre équipe a adopté et tente d’appliquer, et peuvent indiquer des domaines dans lesquels votre stream de travail pourrait être amélioré. Une amélioration consisterait peut-être à déterminer quelles sont les “bonnes” questions à poser – par exemple, plutôt que “où la branch-B twig-t-elle à”, peut-être “quelles twigs contiennent ou non les correctifs / nouvelles fonctionnalités introduits par branch-B “…

Je ne suis pas sûr qu’une réponse complètement satisfaisante à cette question existe vraiment …

Il y a deux préoccupations distinctes ici. À partir de votre exemple,

 A - B - C - - - - J [master] \ \ F - G [branch-A] \ / D - E \ H - I [branch-B] 

[…] Les superviseurs aiment parfois savoir quand une twig a été démarrée (cela marque généralement le début d’une tâche) et à quelle twig appartiennent certains changements (pour obtenir le but de certaines modifications – était-ce nécessaire pour le travail)

deux observations factuelles avant d’arriver à la viande:

Première constatation: ce que votre superviseur veut savoir, c’est la correspondance entre les commits et certains enregistrements de workorder-ish externes: qu’est-ce qui valide l’adresse bug-43289 ou featureB? Pourquoi strcat utilisation de longmsg.c dans longmsg.c ? Qui va payer les vingt heures entre votre coup de pouce précédent et celui-ci? Les noms de twig eux-mêmes n’ont pas d’importance ici, ce qui importe, c’est la relation entre les commits et les enregistrements administratifs externes.

Deuxième constatation: que la branch-A ou la branch-B soit publiée en premier (via la fusion, la rebase ou la poussée par exemple), le travail dans les commits D et E doit aller droit et ne pas être dupliqué par une opération ultérieure. Il n’y a aucune différence entre ce qui était courant quand ces commits ont été faits. Les noms de twig ne comptent pas non plus. Ce qui compte, ce sont les relations de commits entre eux via le graphe d’ascendance.


Donc, ma réponse est que, dans la mesure où une histoire est concernée, les noms de twigs n’ont aucune importance. Ce sont des balises de commodité indiquant quel est le commit actuel pour un but spécifique à ce repo, rien de plus. Si vous voulez un moniker utile dans la ligne d’object de message de fusion-commit par défaut, git branch some-useful-name le conseil avant de fusionner et fusionne cela. Ils sont les mêmes engagements de toute façon.

Lier n’importe quel nom de twig que le développeur avait extrait au moment de la validation avec un enregistrement externe – ou quelque chose du tout – fait partie intégrante du territoire «tout va bien tant que tout fonctionne». Ne le fais pas Même avec l’usage restreint courant dans la plupart des VCS, votre DE-{FG,HI} se produira plus tôt et plus tard, alors vos conventions de dénomination de twig devront être adaptées pour gérer cela, et quelque chose de plus compliqué apparaîtra,. . .

Pourquoi s’embêter? Placez le (s) numéro (s) de rapport invitant le travail dans un slogan au bas de vos messages de validation et faites-le avec lui. git log --grep (et git en général) est extrêmement rapide pour une bonne raison.

Même un hook de préparation assez flexible pour insérer des slogans comme ceci est sortingvial:

 branch=`git symbolic-ref -q --short HEAD` # branch name if any workorder=`git config branch.${branch:+$branch.}x-workorder` # specific or default config tagline="Acme-workorder-id: ${workorder:-***no workorder supplied***}" sed -i "/^ *Acme-workorder-id:/d; \$a$tagline" "$1" 

et voici la boucle de hook de pré-réception de base pour quand vous devez inspecter chaque commit:

 while read old new ref; do # for each pushed ref while read commit junk; do # check for bad commits # test here, eg git show -s $commit | grep -q '^ *Acme-workorder-id: ' \ || { rc=1; echo commit $commit has no workorder associated; } # end of this test done < 

Le projet kernel utilise des slogans comme celui-ci pour la signature du copyright et l'enregistrement de la révision du code. Il ne pouvait vraiment pas être beaucoup plus simple ou plus robuste.

Notez que j'ai fait quelques manipulations après c & p pour dé-specialiser de vrais scripts. Avertissement de clavier à édition

D’un sharepoint vue philosophique, la question de l’histoire d’une twig ne peut être résolue au sens global. Cependant, le reflog suivi l’historique de chaque twig dans ce référentiel particulier .

Ainsi, si vous disposez d’un référentiel central unique vers lequel tout le monde pousse, vous pouvez utiliser son reflog pour suivre ces informations (plus de détails dans cette question ). Premièrement, sur ce repository central, assurez-vous que le renvoi est enregistré et ne sera jamais nettoyé:

 $ git config core.logAllRefUpdates true $ git config gc.reflogExpire never 

Ensuite, vous pouvez lancer git reflog pour inspecter l’historique de la twig.

Exemple

J’ai reproduit votre exemple de graphe de validation avec quelques poussées dans un référentiel de test. Maintenant je peux faire ce genre de chose:

 $ git log --graph --all --oneline --decorate * 64c393b (branch-b) commit I * feebd2f commit H | * 3b9dbb6 (branch-a) commit G | * 18835df commit F |/ * d3840ca commit E * b25fd0b commit D | * 8648b54 (master) commit J | * 676e263 commit C |/ * 17af0d2 commit B * bdbfd6a commit A $ git reflog --date=local master branch-a branch-b 64c393b branch-b@{Sun Oct 11 21:45:03 2015}: push 3b9dbb6 branch-a@{Sun Oct 11 21:45:17 2015}: push 18835df branch-a@{Sun Oct 11 21:43:32 2015}: push 8648b54 master@{Sun Oct 11 21:42:09 2015}: push 17af0d2 master@{Sun Oct 11 21:41:29 2015}: push bdbfd6a master@{Sun Oct 11 21:40:58 2015}: push 

Donc, vous pouvez voir que dans mon exemple, lorsque la branch-a été créée pour la première fois, elle a été déclenchée avec commit F , et qu’une deuxième poussée vers le serveur central le faisait avancer pour valider G Alors que lorsque branch-b est apparu pour la première fois, il a été mis sur commit, et il n’a pas encore vu de mise à jour.

Mises en garde

Cela montre seulement l’histoire comme il a été poussé au repo central . Si, par exemple, un collègue a démarré la branch-A lors de la validation A , mais l’a ensuite rebaptisé sur la validation B avant de la pousser, cette information ne serait pas reflétée dans le renvoi du référentiel central.

Cela ne fournit pas non plus un enregistrement définitif de l’ endroit où une twig a commencé . Nous ne pouvons pas vraiment dire avec certitude quelle twig “possède” commet D et E qui a été créée par Master. Est-ce qu’ils ont été créés sur la branch-a puis repris par la branch-b , ou l’inverse?

Les deux twigs apparaissaient initialement dans le référentiel central contenant ces commits, et le reflog nous indique quelle twig a été reflog premier dans le référentiel central. Cependant, ces commits ont peut-être été “passés” entre plusieurs référentiels d’utilisateurs finaux, via format-patch , etc. Donc, même si nous soaps quel est le pointeur de twig responsable de leur transfert vers le serveur central, nous ne connaissons pas leur origine ultime .

Comme l’explique @cupcake, il n’y a pas de sharepoint départ d’une twig. Vous ne pouvez vérifier que la première fois qu’une twig a touché une autre. C’est probablement ce que vous voulez dans la plupart des cas. @ code-guru a déjà expliqué la syntaxe pour faire référence aux plages de commits.

Rassembler le tout: Cette commande affiche le premier commit avant le premier commit qui était dans branch-A mais pas dans master :

git show `git rev-list branch-A ^master --topo-order | tail -n 1`~1

Quelques détails architecturaux

Git stocke les révisions dans un référentiel sous la forme d’une série de validations. Ces validations contiennent un lien vers des informations sur les modifications apscopes aux fichiers depuis la dernière validation et, surtout, un lien vers la validation précédente. En termes généraux, l’historique de validation d’une twig est une liste liée par un lien, de la révision la plus récente jusqu’à la racine du référentiel. L’état du référentiel à chaque validation est celui qui est associé à tous les commits antérieurs à la racine.

Alors, quelle est la tête? Et c’est quoi une twig?

HEAD est un pointeur spécial sur le dernier commit dans la twig actuellement active. Chaque twig, y compris le master 1 , est également un pointeur vers la dernière révision de son histoire.

Clair comme de la boue? Examinons un exemple en utilisant une image du livre Pro Git , qui, espérons-le, clarifiera un peu les choses. 2

Arbre Git Simple

Dans ce diagramme, nous avons un référentiel relativement simple avec 4 commits. 98ca9 est la racine. Il y a deux twigs, master et testing. La twig maître est à commit f30ab alors que la twig testing est à 87ab2 . Nous travaillons actuellement dans la twig principale, de sorte que HEAD pointe vers la twig principale. L’historique des twigs de notre référentiel est (du plus récent au plus ancien):

 testing: 87ab2 -> f30ab -> 34ac2 -> 98ca9 master: f30ab -> 34ac2 -> 98ca9 

À partir de cela, nous pouvons voir que les deux twigs sont les mêmes à partir de f30ab , donc nous pouvons également dire que les tests étaient des twigs à ce commit.

Le livre Pro Git contient beaucoup plus de détails et vaut vraiment la peine d’être lu.

Maintenant, nous pouvons aborder–

La question spécifique

Fancify le diagramme que nous obtenons:

Envie d'un petit bonbon en buvant du thé.

Est-ce que commit D est un membre des deux twigs ou peut-on décider clairement si elle appartient à branch-A ou branch-B?

Connaissant ce que nous soaps maintenant, nous pouvons voir que commit D est un membre des deux chaînes menant des pointeurs de twig à la racine. On peut donc dire que D est membre des deux twigs.

Quelle twig a commencé à E, laquelle à B?

Les twigs A et B proviennent de la twig principale en B et divergent les unes des autres en E. Git lui-même ne distingue pas quelle twig possède E. En son sein, les twigs ne sont que la chaîne des engagements du plus récent au dernier. le plus ancien se termine à la racine.


1 Anecdote: La twig principale n’est qu’une twig ordinaire. Ce n’est pas différent de toute autre twig.

2 Le livre Pro Git est sous licence avec la licence Creative Commons Atsortingbution-NonCommercial-ShareAlike 3.0 Unported.