J'ai un référentiel avec les branches master et A et beaucoup d'activités de fusion entre les deux. Comment puis-je trouver la validation dans mon référentiel lorsque la branche A a été créée sur la base du maître?
Mon référentiel ressemble à ceci:
-- X -- A -- B -- C -- D -- F (master)
\ / \ /
\ / \ /
G -- H -- I -- J (branch A)
Je cherche la révision A, ce qui n'est pas ce qui git merge-base (--all)
se trouve.
Réponses:
Je cherchais la même chose, et j'ai trouvé cette question. Merci de le demander!
Cependant, j'ai trouvé que les réponses que je vois ici ne semblent pas tout à fait donner la réponse que vous avez demandée (ou que je cherchais) - elles semblent donner le
G
commit, au lieu duA
commit.J'ai donc créé l'arborescence suivante (lettres attribuées dans l'ordre chronologique), afin de pouvoir tester les choses:
Cela semble un peu différent du vôtre, car je voulais m'assurer d'avoir (en référence à ce graphique, pas le vôtre) B, mais pas A (et pas D ou E). Voici les lettres attachées aux préfixes SHA et aux messages de commit (mon dépôt peut être cloné à partir d' ici , si cela intéresse quelqu'un):
Ainsi, l' objectif: trouver B . Voici trois façons que j'ai trouvées, après un peu de bricolage:
1. visuellement, avec gitk:
Vous devriez voir visuellement un arbre comme celui-ci (vu du maître):
ou ici (vu du sujet):
dans les deux cas, j'ai sélectionné le commit qui se trouve
B
dans mon graphique. Une fois que vous avez cliqué dessus, son SHA complet est présenté dans un champ de saisie de texte juste en dessous du graphique.2. visuellement, mais depuis le terminal:
git log --graph --oneline --all
(Édition / note latérale: l'ajout
--decorate
peut également être intéressant; il ajoute une indication des noms de branche, des balises, etc. Ne pas ajouter cela à la ligne de commande ci-dessus car la sortie ci-dessous ne reflète pas son utilisation.)qui montre (en supposant
git config --global color.ui auto
):Ou, en texte simple:
dans les deux cas, nous considérons la validation 6aafd7f comme le point commun le plus bas, c'est-
B
à- dire dans mon graphique ouA
dans le vôtre.3. Avec la magie des coquillages:
Vous ne spécifiez pas dans votre question si vous vouliez quelque chose comme ce qui précède, ou une seule commande qui ne vous donnera qu'une seule révision, et rien d'autre. Eh bien, voici ce dernier:
Que vous pouvez également mettre dans votre ~ / .gitconfig en tant que (note: le tiret arrière est important; merci Brian pour avoir attiré l'attention sur cela) :
Ce qui pourrait être fait via la ligne de commande suivante (compliquée par des guillemets):
Note:
zsh
pourrait tout aussi bien avoir étébash
, mais lesh
fera pas le travail - la<()
syntaxe n'existe pas dans la vanillesh
. (Merci encore, @conny, de m'en avoir fait part dans un commentaire sur une autre réponse sur cette page!)Remarque: autre version de ce qui précède:
Merci à liori pour avoir souligné que ce qui précède pourrait tomber lors de la comparaison de branches identiques, et trouver une autre forme de diff qui supprime la forme sed du mélange et rend cela "plus sûr" (c'est-à-dire qu'il renvoie un résultat (à savoir, le commit le plus récent) même lorsque vous comparez master à master):
En tant que ligne .git-config:
De la coque:
Donc, dans mon arbre de test (qui n'était pas disponible pendant un certain temps, désolé; il est de retour), cela fonctionne maintenant à la fois sur le maître et le sujet (en donnant les commits G et B, respectivement). Merci encore, liori, pour la forme alternative.
C'est donc ce que j'ai [et liori] avec. Il semble fonctionner pour moi. Il autorise également quelques alias supplémentaires qui pourraient s'avérer utiles:
Heureux git-ing!
la source
diff
est si-then-else et le mode d' effacement a changé / supprimé les lignes de sa sortie au lieu de compter sur le contexte ayant assez grand, par.:diff --old-line-format='' --new-line-format='' <(git rev-list …) <(git rev-list …)|head -1
.git log -n1 --format=format:%H $(git log --reverse --format=format:%H master..topic | head -1)~
fonctionnera aussi, je pensegit merge-base --fork-point ...
de donner le commit B (commit6aafd7f
) pour cet arbre? J'étais occupé avec d'autres trucs quand tu as posté ça, et ça sonnait bien, mais je l'ai finalement juste essayé, et je ne le fais pas fonctionner ... Je reçois quelque chose de plus récent, ou juste un échec silencieux (pas d'erreur message, mais l' état de la sortie 1), en essayant des arguments tels quegit co master; git merge-base --fork-point topic
,git co topic; git merge-base --fork-point master
,git merge-base --fork-point topic master
(soit pour la caisse), etc. y at - il quelque chose que je fais mal ou manquant?--fork-point
est basé sur le reflog, donc cela ne fonctionnera que si vous avez fait les changements localement. Même alors, les entrées de reflog pourraient avoir expiré. C'est utile mais pas fiable du tout.Vous cherchez peut-être
git merge-base
:la source
--all
option "git merge-base
"git merge-base
cinq heures après la publication de ma réponse (probablement en réponse à ma réponse). Néanmoins, je laisserai cette réponse telle quelle car elle peut toujours être utile pour quelqu'un d'autre qui trouve cette question via la recherche.Je l'ai utilisé
git rev-list
pour ce genre de chose. Par exemple, (notez les 3 points)va cracher le point de branchement. Maintenant, ce n'est pas parfait; puisque vous avez fusionné le maître dans la branche A plusieurs fois, cela divisera quelques points de branche possibles (en gros, le point de branche d'origine, puis chaque point auquel vous avez fusionné le maître dans la branche A). Cependant, cela devrait au moins réduire les possibilités.
J'ai ajouté cette commande à mes alias en
~/.gitconfig
tant que:donc je peux l'appeler comme:
la source
G
au lieu deA
, selon le graphique de la question d'origine.) J'ai une réponse qui obtientA
, que je publierai actuellement.git checkout topic
puis l'exécutertopic
à la place debranch-a
), il répertorie648ca357b946939654da12aaf2dc072763f3caee
et37ad15952db5c065679d7fc31838369712f0b338
- les deux37ad159
et648ca35
sont dans l'ancêtre des branches actuelles (cette dernière étant la TÊTE actuelle detopic
), mais ce n'est pas non plus le point avant que la ramification ne se produise. Obtenez-vous quelque chose de différent?Si vous aimez les commandes laconiques,
Voici une explication.
La commande suivante vous donne la liste de toutes les validations dans master qui se sont produites après la création de nom_branche
Puisque vous ne vous souciez que du premier de ces commits, vous voulez la dernière ligne de la sortie:
Le parent du premier commit qui n'est pas un ancêtre de "nom_branche" est, par définition, dans "nom_branche" et est dans "maître" car il est l'ancêtre de quelque chose dans "maître". Vous avez donc le premier commit dans les deux branches.
La commande
est juste un moyen d'afficher la référence de validation parent. Vous pourriez utiliser
ou peu importe.
PS: Je ne suis pas d'accord avec l'argument selon lequel l'ordre des ancêtres n'est pas pertinent. Cela dépend de ce que vous voulez. Par exemple, dans ce cas
il est parfaitement logique de sortir C2 en tant que commit de "branchement". C'est à ce moment que le développeur s'est éloigné de "maître". Lorsqu'il s'est ramifié, la branche "B" n'a même pas été fusionnée dans sa branche! C'est ce que donne la solution dans ce post.
Si ce que vous voulez est le dernier commit C tel que tous les chemins depuis l'origine jusqu'au dernier commit sur la branche "A" passent par C, alors vous voulez ignorer l'ordre d'ascendance. C'est purement topologique et vous donne une idée de depuis quand vous avez deux versions du code en même temps. C'est à ce moment que vous opteriez pour des approches basées sur la fusion, et cela retournera C1 dans mon exemple.
la source
git rev-list commit^^!
peut être simplifié commegit rev-parse commit^
git rev-list --first-parent ^branch_name master
pargit rev-list --first-parent branch_name ^master
car si la branche master est 0 validée devant l'autre branche (qui peut lui être rapidement transmise), aucune sortie ne sera créée. Avec ma solution, aucune sortie n'est créée si le maître est strictement en avance (c'est-à-dire que la branche a été complètement fusionnée), c'est ce que je veux.git rev-list --first-parent ^topic master
ne vous ramènera au premier commit qu'après la dernière fusion demaster
intotopic
(si cela existe même).Étant donné que tant de réponses dans ce fil ne donnent pas la réponse à la question, voici un résumé des résultats de chaque solution, ainsi que le script que j'ai utilisé pour répliquer le référentiel donné dans la question.
Le journal
En créant un dépôt avec la structure donnée, nous obtenons le journal git de:
Mon seul ajout, c'est la balise qui rend explicite le point auquel nous avons créé la branche et donc le commit que nous souhaitons trouver.
La solution qui fonctionne
La seule solution qui fonctionne est celle fournie par lindes renvoie correctement
A
:Comme le souligne Charles Bailey , cette solution est très fragile.
Si vous
branch_A
enmaster
puis fusionnermaster
enbranch_A
sans intervenir commits alors la solution de lindes ne vous donne le plus récent en premier Divergance .Cela signifie que pour mon flux de travail, je pense que je vais devoir m'en tenir au balisage du point de branchement des branches de longue durée, car je ne peux pas garantir qu'elles pourront être trouvées de manière fiable plus tard.
Tout cela se résume vraiment à l'
git
absence de ce qu'onhg
appelle des branches nommées . Le blogueur jhw appelle ces lignées vs familles dans son article Why I Like Mercurial More Than Git et son article de suivi More On Mercurial vs. Git (with Graphs!) . Je recommanderais aux gens de les lire pour voir pourquoi certains convertis mercuriels manquent de ne pas avoir nommé de branches dansgit
.Les solutions qui ne fonctionnent pas
La solution apportée par mipadi renvoie deux réponses,
I
etC
:La solution apportée par Greg Hewgill retour
I
La solution apportée par Karl revient
X
:Le scénario
Je doute que la version git fasse beaucoup de différence, mais:
Merci à Charles Bailey de m'avoir montré une manière plus compacte de scripter le référentiel d'exemples.
la source
diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
. Merci d'avoir fourni des instructions pour créer leEn général, ce n'est pas possible. Dans un historique de branche, une branche et fusion avant qu'une branche nommée ait été branchée et une branche intermédiaire de deux branches nommées se ressemblent.
Dans git, les branches ne sont que les noms actuels des conseils de sections de l'histoire. Ils n'ont pas vraiment d'identité forte.
Ce n'est généralement pas un gros problème car la base de fusion (voir la réponse de Greg Hewgill) de deux validations est généralement beaucoup plus utile, donnant la validation la plus récente partagée par les deux branches.
Une solution reposant sur l'ordre des parents d'un commit ne fonctionnera évidemment pas dans les situations où une branche a été complètement intégrée à un moment donné de son histoire.
Cette technique échoue également si une fusion d'intégration a été effectuée avec les parents inversés (par exemple, une branche temporaire a été utilisée pour effectuer une fusion de test dans le maître, puis avancée rapidement dans la branche de fonctionnalité pour continuer).
la source
git
avait un équivalent àhg
« s nommé branches, il ferait la gestion des branches d'entretien à long vécu de façon beaucoup plus facile.Que diriez-vous de quelque chose comme
la source
diverges = !bash -c 'git rev-parse $(diff <(git log --pretty=oneline ${1}) <(git log --pretty=oneline ${2}) | tail -1 | cut -c 3-42)^'
(sans fichiers temporaires)G
au lieu deA
, selon le graphique de la question d'origine.) Je pense que j'ai trouvé une réponse, cependant, que je publierai actuellement.diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
je manque sûrement quelque chose, mais à mon avis, tous les problèmes ci-dessus sont causés parce que nous essayons toujours de trouver le point de branchement remontant dans l'histoire, et cela provoque toutes sortes de problèmes en raison des combinaisons de fusion disponibles.
Au lieu de cela, j'ai suivi une approche différente, basée sur le fait que les deux branches partagent beaucoup d'histoire, exactement toute l'histoire avant la ramification est la même à 100%, donc au lieu de revenir en arrière, ma proposition est d'aller de l'avant (à partir du 1er commit), à la recherche de la 1ère différence dans les deux branches. Le point de branchement sera, simplement, le parent de la première différence trouvée.
En pratique:
Et ça résout tous mes cas habituels. Bien sûr, il y a des frontières non couvertes mais ... ciao :-)
la source
comm --nocheck-order -1 -2 <(git rev-list --reverse --topo-order topic) <(git rev-list --reverse --topo-order master) | head -1
J'ai récemment eu également besoin de résoudre ce problème et j'ai fini par écrire un script Ruby pour cela: https://github.com/vaneyckt/git-find-branching-point
la source
Après beaucoup de recherches et de discussions, il est clair qu'il n'y a pas de solution miracle qui fonctionnerait dans toutes les situations, du moins pas dans la version actuelle de Git.
C'est pourquoi j'ai écrit quelques patchs qui ajoutent le concept d'une
tail
branche. Chaque fois qu'une branche est créée, un pointeur vers le point d'origine est également créé, latail
réf. Cette référence est mise à jour chaque fois que la branche est rebasée.Pour connaître le point de branchement de la branche devel, tout ce que vous avez à faire est d'utiliser
devel@{tail}
, c'est tout.https://github.com/felipec/git/commits/fc/tail
la source
Voici une version améliorée de ma réponse précédente réponse précédente . Il s'appuie sur les messages de validation des fusions pour trouver où la branche a été créée pour la première fois.
Il fonctionne sur tous les référentiels mentionnés ici, et j'en ai même traité quelques-uns difficiles qui sont apparus sur la liste de diffusion . J'ai également écrit des tests pour cela.
la source
La commande suivante révélera le SHA1 du Commit A
git merge-base --fork-point A
la source
Il me semble que j'obtiens de la joie
La dernière ligne que vous obtenez est le premier commit sur la branche, il s'agit donc d'obtenir le parent de cela. Donc
Semble fonctionner pour moi et n'a pas besoin de diff et ainsi de suite (ce qui est utile car nous n'avons pas cette version de diff)
Correction: cela ne fonctionne pas si vous êtes sur la branche principale, mais je le fais dans un script, donc c'est moins un problème
la source
Pour trouver des validations à partir du point de branchement, vous pouvez utiliser ceci.
la source
Parfois, c'est effectivement impossible (à quelques exceptions près où vous pourriez avoir la chance d'avoir des données supplémentaires) et les solutions ici ne fonctionneront pas.
Git ne conserve pas l'historique des références (qui inclut les branches). Il stocke uniquement la position actuelle de chaque branche (la tête). Cela signifie que vous pouvez perdre un peu d'historique de branche dans git au fil du temps. Chaque fois que vous branchez par exemple, vous perdez immédiatement la branche d'origine. Une succursale ne fait que:
Vous pouvez supposer que le premier engagé est la branche. Cela a tendance à être le cas, mais ce n'est pas toujours le cas. Rien ne vous empêche de vous engager dans l'une ou l'autre branche en premier après l'opération ci-dessus. De plus, les horodatages git ne sont pas garantis pour être fiables. Ce n'est que lorsque vous vous engagez à la fois qu'ils deviennent vraiment des branches structurellement.
Alors que dans les diagrammes, nous avons tendance à numéroter les validations de manière conceptuelle, git n'a pas de véritable concept stable de séquence lorsque l'arbre de validation se branche. Dans ce cas, vous pouvez supposer que les nombres (indiquant l'ordre) sont déterminés par l'horodatage (il peut être amusant de voir comment une interface utilisateur git gère les choses lorsque vous définissez tous les horodatages de la même manière).
C'est ce qu'un humain attend conceptuellement:
Voici ce que vous obtenez réellement:
Vous supposeriez que B1 soit la branche d'origine, mais cela pourrait en fait être simplement une branche morte (quelqu'un a vérifié -b mais ne s'y est jamais engagé). Ce n'est que lorsque vous vous engagez à la fois que vous obtenez une structure de branche légitime dans git:
Vous savez toujours que C1 est venu avant C2 et C3 mais vous ne savez jamais de manière fiable si C2 est venu avant C3 ou C3 est venu avant C2 (parce que vous pouvez régler l'heure sur votre poste de travail sur n'importe quoi par exemple). B1 et B2 sont également trompeurs car vous ne pouvez pas savoir quelle branche est arrivée en premier. Dans bien des cas, vous pouvez faire une très bonne estimation, généralement précise. C'est un peu comme une piste de course. Toutes choses étant généralement égales aux voitures, vous pouvez supposer qu'une voiture qui arrive dans un tour derrière a commencé un tour derrière. Nous avons également des conventions qui sont très fiables, par exemple, master représentera presque toujours les branches les plus anciennes, bien que j'ai malheureusement vu des cas où même ce n'est pas le cas.
L'exemple donné ici est un exemple préservant l'historique:
Le vrai ici est également trompeur parce que nous, les humains, le lisons de gauche à droite, de la racine à la feuille (réf). Git ne fait pas ça. Où nous faisons (A-> B) dans nos têtes git fait (A <-B ou B-> A). Il le lit de ref à root. Les références peuvent être n'importe où mais ont tendance à être des feuilles, au moins pour les branches actives. Une référence pointe vers un commit et les commits ne contiennent qu'un like pour leurs parents, pas pour leurs enfants. Lorsqu'un commit est un commit de fusion, il aura plusieurs parents. Le premier parent est toujours le commit d'origine qui a été fusionné. Les autres parents sont toujours des validations qui ont été fusionnées dans la validation d'origine.
Ce n'est pas une représentation très efficace, mais plutôt une expression de tous les chemins que git peut emprunter à chaque référence (B1 et B2).
Le stockage interne de Git ressemble plus à ceci (pas que A en tant que parent apparaisse deux fois):
Si vous sauvegardez un commit git brut, vous verrez zéro ou plusieurs champs parents. S'il y a zéro, cela signifie qu'il n'y a pas de parent et que la validation est une racine (vous pouvez en fait avoir plusieurs racines). S'il y en a un, cela signifie qu'il n'y a pas eu de fusion et que ce n'est pas un commit root. S'il y en a plusieurs, cela signifie que la validation est le résultat d'une fusion et que tous les parents après la première sont des validations de fusion.
Lorsque les deux frappent A, leur chaîne sera la même, avant cela, leur chaîne sera entièrement différente. Le premier commit que deux autres commits ont en commun est l'ancêtre commun et d'où ils ont divergé. il pourrait y avoir une certaine confusion ici entre les termes commit, branch et ref. Vous pouvez en fait fusionner un commit. C'est ce que fait vraiment la fusion. Une référence pointe simplement vers un commit et une branche n'est rien d'autre qu'une référence dans le dossier .git / refs / heads, l'emplacement du dossier est ce qui détermine qu'une référence est une branche plutôt que quelque chose d'autre comme une balise.
Là où vous perdez l'histoire, c'est que la fusion fera l'une des deux choses selon les circonstances.
Considérer:
Dans ce cas, une fusion dans l'une ou l'autre direction créera un nouveau commit avec le premier parent comme commit pointé par la branche extraite actuelle et le second parent comme commit à l'extrémité de la branche que vous avez fusionné dans votre branche actuelle. Il doit créer un nouveau commit car les deux branches ont des changements depuis leur ancêtre commun qui doivent être combinés.
À ce stade, D (B1) a maintenant les deux ensembles de changements des deux branches (lui-même et B2). Cependant, la deuxième branche n'a pas les changements de B1. Si vous fusionnez les modifications de B1 à B2 afin qu'elles soient synchronisées, vous pouvez vous attendre à quelque chose qui ressemble à ceci (vous pouvez cependant forcer git merge à le faire comme ceci avec --no-ff):
Vous obtiendrez cela même si B1 a des commits supplémentaires. Tant qu'il n'y aura pas de changements dans B2 que B1 n'a pas, les deux branches seront fusionnées. Il fait une avance rapide qui ressemble à un rebase (les rebases mangent ou linéarisent également l'historique), sauf contrairement à un rebase car une seule branche a un ensemble de changements, il n'est pas nécessaire d'appliquer un ensemble de changements d'une branche en plus de celui d'une autre.
Si vous cessez de travailler sur B1, alors les choses sont très bien pour préserver l'histoire à long terme. Seul B1 (qui pourrait être maître) avancera généralement, donc l'emplacement de B2 dans l'histoire de B2 représente avec succès le point de fusion avec B1. C'est ce que git attend de vous, pour dériver B de A, alors vous pouvez fusionner A en B autant que vous le souhaitez à mesure que les modifications s'accumulent, mais lors de la fusion de B en A, il n'est pas prévu que vous travailliez sur B et plus loin . Si vous continuez à travailler sur votre branche après l'avoir rapidement fusionnée dans la branche sur laquelle vous travailliez, puis effacez l'historique précédent de B à chaque fois. Vous créez vraiment une nouvelle branche à chaque fois après une avance rapide sur la source, puis sur la branche.
1 à 3 et 5 à 8 sont des branches structurelles qui s'affichent si vous suivez l'historique pour 4 ou 9. Il n'y a aucun moyen de savoir à laquelle de ces branches structurelles non nommées et non référencées appartiennent les branches nommées et références comme fin de la structure. Vous pourriez supposer à partir de ce dessin que 0 à 4 appartient à B1 et 4 à 9 appartient à B2 mais à part 4 et 9 ne pouvait pas savoir quelle branche appartient à quelle branche, je l'ai simplement dessinée d'une manière qui donne la illusion de cela. 0 pourrait appartenir à B2 et 5 pourrait appartenir à B1. Il y a 16 possibilités différentes dans ce cas dont la branche nommée à laquelle chacune des branches structurelles pourrait appartenir.
Il existe un certain nombre de stratégies Git qui fonctionnent autour de cela. Vous pouvez forcer git merge à ne jamais avancer rapidement et toujours créer une branche de fusion. Une horrible façon de préserver l'historique des branches est d'utiliser des balises et / ou des branches (les balises sont vraiment recommandées) selon une convention de votre choix. Je ne recommanderais vraiment pas un commit vide factice dans la branche dans laquelle vous fusionnez. Une convention très courante consiste à ne pas fusionner dans une branche d'intégration tant que vous ne voulez pas vraiment fermer votre branche. C'est une pratique que les gens devraient essayer d'adhérer, sinon vous travaillez sur le point d'avoir des succursales. Cependant, dans le monde réel, l'idéal n'est pas toujours pratique, ce qui signifie que faire la bonne chose n'est pas viable dans toutes les situations. Si ce que vous '
la source
man git-reflog
et la partie sur les dates: "master@{one.week.ago} signifie" où master avait l'habitude de pointer il y a une semaine dans ce dépôt local "". Ou la discussion sur<refname>@{<date>}
inman gitrevisions
. Etcore.reflogExpire
dansman git-config
.Pas tout à fait une solution à la question, mais j'ai pensé qu'il valait la peine de noter l'approche que j'utilise lorsque j'ai une branche de longue date:
En même temps je crée la branche, je crée aussi un tag avec le même nom mais avec un
-init
suffixe, par exemplefeature-branch
etfeature-branch-init
.(C'est assez bizarre que ce soit une question tellement difficile à répondre!)
la source
Un moyen simple de rendre plus facile la visualisation du point de branchement
git log --graph
consiste à utiliser l'option--first-parent
.Par exemple, prenez le dépôt de la réponse acceptée :
Ajoutez maintenant
--first-parent
:C'est plus simple!
Notez que si le dépôt a beaucoup de branches, vous voudrez spécifier les 2 branches que vous comparez au lieu d'utiliser
--all
:la source
Le problème semble être de trouver la coupe à validation unique la plus récente entre les deux branches d'un côté et la plus ancienne ancêtre commun de l'autre (probablement la validation initiale du dépôt). Cela correspond à mon intuition de ce qu'est le point de "ramification".
Cela à l'esprit, ce n'est pas du tout facile à calculer avec les commandes git shell normales, car
git rev-list
- notre outil le plus puissant - ne nous permet pas de restreindre le chemin par lequel une validation est atteinte. Le plus proche que nous avons estgit rev-list --boundary
, ce qui peut nous donner un ensemble de tous les commits qui "ont bloqué notre chemin". (Remarque:git rev-list --ancestry-path
c'est intéressant mais je ne sais pas comment le rendre utile ici.)Voici le script: https://gist.github.com/abortz/d464c88923c520b79e3d . C'est relativement simple, mais en raison d'une boucle, c'est assez compliqué pour justifier un résumé.
Notez que la plupart des autres solutions proposées ici ne peuvent pas fonctionner dans toutes les situations pour une raison simple:
git rev-list --first-parent
n'est pas fiable dans l'historique de linéarisation car il peut y avoir des fusions avec l'une ou l'autre commande.git rev-list --topo-order
, d'autre part, est très utile - pour les commits de marche dans l'ordre topographique - mais faire des différences est fragile: il existe plusieurs ordres topographiques possibles pour un graphique donné, vous dépendez donc d'une certaine stabilité des ordres. Cela dit, la solution de strongk7 fonctionne probablement très bien la plupart du temps. Cependant, il est plus lent que le mien car il a dû parcourir toute l'histoire du repo ... deux fois. :-)la source
Ce qui suit implémente l'équivalent git de svn log --stop-on-copy et peut également être utilisé pour trouver l'origine de la branche.
Approche
Comme toutes les rivières qui coulent vers la mer, toutes les branches se dirigent vers la maîtrise et nous trouvons donc une base de fusion entre des branches apparemment sans rapport. Lorsque nous revenons de la tête de la branche aux ancêtres, nous pouvons nous arrêter à la première base de fusion potentielle, car en théorie, elle devrait être le point d'origine de cette branche.
Remarques
détails: https://stackoverflow.com/a/35353202/9950
la source
Vous pouvez utiliser la commande suivante pour renvoyer le commit le plus ancien dans branch_a, qui n'est pas accessible depuis master:
Peut-être avec une vérification de cohérence supplémentaire que le parent de cette validation est réellement accessible à partir du maître ...
la source
Vous pouvez examiner le reflog de la branche A pour trouver à partir de quelle validation elle a été créée, ainsi que l'historique complet des validations de cette branche pointée. Les reflogs sont là
.git/logs
.la source
Je crois que j'ai trouvé un moyen qui traite tous les cas de coin mentionnés ici:
Charles Bailey a tout à fait raison de dire que les solutions basées sur l'ordre des ancêtres n'ont qu'une valeur limitée; à la fin de la journée, vous avez besoin d'une sorte d'enregistrement de "ce commit est venu de la branche X", mais cet enregistrement existe déjà; par défaut, 'git merge' utiliserait un message de validation tel que "Merge branch 'branch_A' into master", cela vous indique que toutes les validations du deuxième parent (commit ^ 2) provenaient de 'branch_A' et ont été fusionnées avec la première parent (commit ^ 1), qui est «maître».
Armé de ces informations, vous pouvez trouver la première fusion de 'branch_A' (qui est quand 'branch_A' a vraiment vu le jour), et trouver la base de fusion, qui serait le point de branchement :)
J'ai essayé avec les référentiels de Mark Booth et Charles Bailey et la solution fonctionne; comment ne pouvait-il pas? La seule façon dont cela ne fonctionnerait pas est si vous avez modifié manuellement le message de validation par défaut pour les fusions afin que les informations de branche soient vraiment perdues.
Pour l'utilité:
Ensuite, vous pouvez le faire '
git branch-point branch_A
'.Prendre plaisir ;)
la source
git merge -m
pour dire dans quoi j'ai fusionné plutôt que le nom d'une branche potentiellement éphémère (par exemple "fusionner les modifications de la ligne principale en refactoriser la fonctionnalité xyz"). Supposons que j'aie été moins utile avec mon-m
dans mon exemple? Le problème n'est tout simplement pas soluble dans toute sa généralité car je peux faire la même histoire avec une ou deux branches temporaires et il n'y a aucun moyen de faire la différence.