587 votes

Trouver un point de branchement avec Git ?

J’ai un référentiel avec maître de branches et de A et de beaucoup d’activité de la fusion entre les deux. Comment puis-je trouver le commit dans mon référentiel lors de branche Qu'a été créé fonction maître ?

Mon dépôt essentiellement ressemble à ceci :

Je suis à la recherche pour révision A, qui n’est pas `` trouve.

628voto

lindes Points 2402

Je cherchais la même chose, et j'ai trouvé cette question. Merci pour la poser!

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é (ou que je cherchais) -- ils semblent donner de l' G s'engager, au lieu de l' A commettre.

Donc, j'ai créé l'arbre suivant (lettres attribuées dans l'ordre chronologique), j'ai donc pu tester des choses:

A - B - D - F - G   <- "master" branch (at G)
     \   \     /
      C - E --'     <- "topic" branch (still at E)

Cela ressemble un peu différente de la vôtre, car je voulais m'assurer que j'ai eu (en se référant à ce graphique, pas le vôtre) B, mais pas Un (et non pas D ou E). Voici les lettres attachées à SHA, des préfixes et des messages de commit (mon repo peut être cloné à partir d' ici, si c'est intéressant pour quelqu'un):

G: a9546a2 merge from topic back to master
F: e7c863d commit on master after master was merged to topic
E: 648ca35 merging master onto topic
D: 37ad159 post-branch commit on master
C: 132ee2a first commit on topic branch
B: 6aafd7f second commit on master before branching
A: 4112403 initial commit on master

Donc, le but: trouver B. Voici trois façons que j'ai trouvé, après un peu de bricolage:


1. visuellement, avec gitk:

Vous devriez voir une arborescence comme ceci (vu de master):

gitk screen capture from master

ou ici (vu de sujet):

gitk screen capture from topic

dans les deux cas, j'ai choisi la livraison c'est - B sur mon graphique. Une fois que vous cliquez sur elle, de son plein SHA est présenté dans un champ de saisie de texte juste en dessous du graphique.


2. visuellement, mais à partir du terminal:

git log --graph --oneline --all

ce qui montre (en supposant git config --global color.ui auto):

output of git log --graph --oneline --all

Ou, dans le texte de droite:

* a9546a2 de fusion de sujet de retour à la maîtrise de
|\ 
| * 648ca35 la fusion de master sur le sujet
| |\ 
| * | 132ee2a premier commit sur le sujet de la branche
* | | e7c863d s'engager sur le master après master a été fusionnée à la rubrique
| |/ 
|/| 
* | 37ad159 post-direction de s'engager sur le maître
|/ 
* 6aafd7f deuxième s'engager sur le maître, avant de s'orienter
* 4112403 initiale s'engager sur le maître

dans les deux cas, nous voyons la 6aafd7f commettre le plus bas point commun, c'est à dire B sur mon graphique, ou A dans le vôtre.


3. Avec la coque de la magie:

Vous ne précisez pas dans votre question si vous vouliez quelque chose comme ci-dessus, ou d'une seule commande que vais juste vous obtenir une révision, et rien d'autre. Eh bien, voici la dernière:

diff -u <(git rev-list --first-parent topic) \
             <(git rev-list --first-parent master) | \
     sed -ne 's/^ //p' | head -1
6aafd7ff98017c816033df18395c5c1e7829960d

Qui vous pouvez aussi mettre dans votre ~/.gitconfig comme (note: les dash est important; merci Brian pour attirer l'attention):

[alias]
    oldest-ancestor = !zsh -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -1' -

Ce qui pourrait être fait par la suite (alambiquée citations) de ligne de commande:

git config --global alias.oldest-ancestor '!zsh -c '\''diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne "s/^ //p" | head -1'\'' -'

Remarque: zsh pourrait tout aussi bien avoir été bash, mais sh aura pas de travail -- <() syntaxe n'existe pas dans la vanille sh. (Merci encore, @conny, pour me mettre au courant de il dans un commentaire sur une autre réponse sur cette page!)

Remarque: version Alternative de la ci-dessus:

Grâce à liori pour souligner que le ci-dessus pourrait tomber lors de la comparaison des branches identiques, et à venir avec une autre diff forme qui supprime le sed forme à partir de la combinaison, et fait de ce "plus sûr" (c'est à dire qu'il renvoie un résultat (à savoir, les plus récentes s'engager), même lorsque vous comparez maître de master):

Comme une .git-config ligne:

[alias]
    oldest-ancestor = !zsh -c 'diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1' -

À partir du shell:

git config --global alias.oldest-ancestor '!zsh -c '\''diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1'\'' -'

Donc, dans mon test de l'arbre (qui a été indisponible pendant un certain temps, désolé; il est de retour), qui travaille maintenant à la fois le maître et le sujet (donner commet G et B, respectivement). Merci encore, liori, pour l'autre forme.


Donc, c'est ce que j' [et liori] est venu avec. Il semble fonctionner pour moi. Il permet également un supplément de couple d'alias qui peut s'avérer pratique:

git config --global alias.branchdiff '!sh -c "git diff `git oldest-ancestor`.."'
git config --global alias.branchlog '!sh -c "git log `git oldest-ancestor`.."'

Heureux git-ing!

160voto

Greg Hewgill Points 356191

Vous cherchez peut-être pour git merge-base:

git merge-base trouve meilleur ancêtre commun(s) entre les deux s'engage à utiliser dans un three-way merge. Un ancêtre commun est meilleur qu'un autre ancêtre commun si ce dernier est un ancêtre de l'ancien. Un ancêtre commun qui n'a pas de mieux ancêtre commun est un meilleur ancêtre commun, c'est à dire une fusion de la base. Notez qu'il peut y avoir plus d'une fusion de base pour une paire de commits.

46voto

mipadi Points 135410

J'ai utilisé git rev-list pour ce genre de chose. Par exemple,

$ git rev-list --boundary branch-a...master | grep ^- | cut -c2-

va cracher le point de branchement. Maintenant, il n'est pas parfait, puisque vous avez fusionné maître dans la branche A une couple de fois, que vais partager quelques possibles points de branchement (en gros, l'original, le point de branchement et puis chaque point où vous fusionnées master dans Une branche). Cependant, il devrait au moins réduire le nombre de possibilités.

J'ai ajouté la commande de mon alias en ~/.gitconfig comme:

[alias]
    diverges = !sh -c 'git rev-list --boundary $1...$2 | grep ^- | cut -c2-'

donc, je peux l'appeler comme:

$ git diverges branch-a master

37voto

Lionel Points 1655

Si vous aimez laconique commandes,

git rev-liste $(git rev-liste-premier-parent ^branch_name maître | tail-n1)^^! 

Voici une explication.

La commande suivante vous donne la liste de tous les commits en maître qui s'est produite après branch_name a été créé

git rev-liste-premier-parent ^branch_name maître 

Puisque vous ne se soucient que les premiers de ces commits que vous voulez la dernière ligne de la sortie:

git rev-liste ^branch_name-parent maître | tail-n1

Le parent des premiers à commettre ce n'est pas un ancêtre de "branch_name" est, par définition, dans "branch_name," et en "maître" puisque c'est un ancêtre de quelque chose de "maître". Vous avez les premiers s'engager c'est dans les deux branches.

La commande

git rev-liste de commettre^^!

c'est une façon de montrer que le parent s'engager de référence. Vous pouvez utiliser

la commande git log -1 commettre^

ou que ce soit.

PS: je suis en désaccord avec l'argument que l'ancêtre de l'ordre n'est pas pertinent. Cela dépend de ce que vous voulez. Par exemple, dans ce cas

_C1___C2_______ maître
 \ \_XXXXX_ de la direction générale de (Xs désigner arbitraire cross-overs entre le maître et l'Un)
 \_____/ de la branche B

il est parfaitement logique de sortie C2 comme le "branching" s'engager. C'est lorsque le développeur ramifié à partir de "maître". Quand il ramifiée, succursale "B" n'était même pas fusionné dans sa direction! C'est ce que la solution dans ce post donne.

Si ce que vous voulez, c'est le dernier commit C telle que tous les chemins de l'origine à la le dernier commit sur la branche "Une" allez par le biais de C, alors vous voulez ignorer l'ascendance de l'ordre. 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 souhaitez faire avec la fusion de la base en fonction des approches, et ce sera le retour de C1 dans mon exemple.

28voto

Mark Booth Points 2710

Étant donné le grand nombre de réponses dans ce fil ne donne pas la réponse à la question été demander, voici un résumé des résultats de chaque solution, avec le script que j'ai utilisé pour reproduire le référentiel donné dans la question.

Le journal

La création d'un référentiel avec la structure de donnée, on obtient la commande git log:

$ git --no-pager log --graph --oneline --all --decorate
* b80b645 (HEAD, branch_A) J - Work in branch_A branch
| *   3bd4054 (master) F - Merge branch_A into branch master
| |\  
| |/  
|/|   
* |   a06711b I - Merge master into branch_A
|\ \  
* | | bcad6a3 H - Work in branch_A
| | * b46632a D - Work in branch master
| |/  
| *   413851d C - Merge branch_A into branch master
| |\  
| |/  
|/|   
* | 6e343aa G - Work in branch_A
| * 89655bb B - Work in branch master
|/  
* 74c6405 (tag: branch_A_tag) A - Work in branch master
* 7a1c939 X - Work in branch master

Mon seul ajout, est la balise qui le rend explicite sur le point où nous avons créé la direction générale et donc de la validation nous souhaitons trouver.

La solution qui fonctionne

La seule solution qui fonctionne est celui fourni par le lindes correctement les retours A:

$ diff -u <(git rev-list --first-parent branch_A) \
          <(git rev-list --first-parent master) | \
      sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5

Comme Charles Bailey points de plus, cette solution est très fragile.

Si vous branch_A en master puis de fusionner master en branch_A sans intervenant s'engage alors lindes solution ne vous donne que le plus récent en premier divergance.

Cela signifie que pour mon travail, je pense que je vais avoir à coller avec le marquage de la direction générale point de la longue course branches, puisque je ne peux pas garantir qu'ils peuvent être sûrement être trouvé plus tard.

- Ce vraiment tout se résume gits manque de ce que hg des appels branches nommées. Le blogueur jhw appelle ces lignées contre les familles dans son article Pourquoi j'Aime Mercurial Plus De Git et de son suivi article de Plus Sur Mercurial vs Git (avec Graphiques!). Je recommande de les lire pour voir pourquoi certains mercurial convertit manquer de ne pas avoir nommé branches en git.

Les solutions qui ne fonctionnent pas

La solution fournie par la mipadi retourne deux réponses, en I et C:

$ git rev-list --boundary branch_A...master | grep ^- | cut -c2-
a06711b55cf7275e8c3c843748daaa0aa75aef54
413851dfecab2718a3692a4bba13b50b81e36afc

La solution fournie par Greg Hewgill retour I

$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54

La solution fournie par Karl retours X:

$ diff -u <(git log --pretty=oneline branch_A) \
          <(git log --pretty=oneline master) | \
       tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5

Le script

mkdir $1
cd $1
git init
git commit --allow-empty -m "X - Work in branch master"
git commit --allow-empty -m "A - Work in branch master"
git branch branch_A
git tag branch_A_tag     -m "Tag branch point of branch_A"
git commit --allow-empty -m "B - Work in branch master"
git checkout branch_A
git commit --allow-empty -m "G - Work in branch_A"
git checkout master
git merge branch_A       -m "C - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "H - Work in branch_A"
git merge master         -m "I - Merge master into branch_A"
git checkout master
git commit --allow-empty -m "D - Work in branch master"
git merge branch_A       -m "F - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "J - Work in branch_A branch"

Je doute que la version git fait beaucoup de différence, mais:

$ git --version
git version 1.7.1

Grâce à Charles Bailey pour me montrer d'une façon plus compacte pour le script de l'exemple de référentiel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X