Suchen Sie nach einem Zusammenführungs-Commit, das ein bestimmtes Commit enthält

181

Stellen Sie sich folgende Geschichte vor:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

Wie kann ich feststellen, wann Commit "c" mit Master zusammengeführt wurde (dh Merge Commit "h" finden)?

Guillaume Morin
quelle

Antworten:

156

Ihr Beispiel zeigt, dass der Zweig featurenoch verfügbar ist.

In diesem Fall hist das letzte Ergebnis von:

git log master ^feature --ancestry-path

Wenn der Zweig featurenicht mehr verfügbar ist, können Sie die Zusammenführungs-Commits in der Verlaufszeile zwischen cund anzeigen master:

git log <SHA-1_for_c>..master --ancestry-path --merges

Dies zeigt jedoch auch alle Zusammenführungen an, die nach hund zwischen eund gan stattgefunden haben feature.


Vergleichen des Ergebnisses der folgenden Befehle:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

gibt Ihnen die SHA-1 hals letzte gemeinsame Zeile.

Wenn Sie es zur Verfügung haben, können Sie es comm -1 -2für diese Ergebnisse verwenden. Wenn Sie mit msysgit arbeiten, können Sie den folgenden Perl-Code zum Vergleichen verwenden:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(Perl-Code von http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , der von "jemandem in der Nachrichtengruppe comp.unix.shell" stammt).

Siehe Prozessersetzung, wenn Sie es zu einem Einzeiler machen möchten.

Gauthier
quelle
4
Selbst wenn Sie Comm haben, können Sie es nicht verwenden, da die Ausgabe des Befehls "git rev-list" nicht lexikografisch sortiert ist. Sie könnten natürlich die Ausgabe jedes Befehls sortieren, bevor Sie nach gemeinsamen Zeilen suchen, aber dann wäre das gewünschte Commit nicht unbedingt das letzte. Ich denke also, dass so etwas wie der Perl-Befehl (obwohl dunkel) notwendig ist.
mhagger
12
Ich habe gerade ein Skript geschrieben, das diesen Vorschlag implementiert (mit einigen anderen Funktionen). Siehe github.com/mhagger/git-when-merged
mhagger
2
Nehmen wir an einem gewissen Punkt masterwurde in verschmolzen feature, dann sofort featureverschmolzen wird masterals Vorspulen (Spitze featureersetzt master). Würde dies dazu führen --first-parent, dass der falsche Elternteil zurückgegeben wird?
Kelvin
4
Ich habe es versucht, comm -1 -2aber es hat nicht funktioniert. commfunktioniert nur auf sortierten Zeilen. (Der Perl-Einzeiler funktioniert, obwohl ich ihn nicht lesen konnte.)
Domon
2
Dies ist etwas spät, aber das Hinzufügen, damit die Leute es nützlich finden können, da Git dies nativ nicht bietet. Es gibt einige Eckfälle, die meiner Meinung nach mit dieser Antwort nicht behandelt werden können (siehe meine Antwort unter stackoverflow.com/a/43716029/58678, die die meisten Eckfälle behandelt). Hier sind die Eckfälle: git find-merge h master(gibt nichts zurück, sollte aber h zurückgeben), git find-merge d master(gibt f zurück, sollte aber d zurückgeben), git find-merge c feature(gibt e zurück, sollte aber g zurückgeben).
HPPy
130

Fügen Sie dies zu Ihrem hinzu ~/.gitconfig:

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

Dann können Sie die Aliase wie folgt verwenden:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

Verwenden Sie, um die Nachricht des Zusammenführungs-Commits und andere Details anzuzeigen git show-merge dieselben Argumente, anzuzeigen.

(Basierend auf Gauthiers Antwort . Vielen Dank an Rosen Matev und Javabrett für die Behebung eines Problems mit sort.)

robinst
quelle
6
Wunderschönen! Dies ist die beste Drop-In-Lösung.
N Jones
1
Beachten Sie, dass sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2die letzte gemeinsame Zeile nicht richtig gefunden wird. Hier ist ein Beispiel, wo es fehlschlägt.
Rosen Matev
1
@ RosenMatev Es wird 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7hier gefunden, wenn ich es auf Ihrer Paste ausführe. Wird das nicht erwartet? Auf welchem ​​Betriebssystem bist du?
Robinst
1
@robinst, deine ist richtig. Ich habe "(GNU coreutils) 8.4" und für mich findet es29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev
2
@powlo: Nichts, es ist nur ein Befehl statt zwei.
Robinst
28

git-get-merge sucht und zeigt das gesuchte Merge-Commit:

pip install git-get-merge
git get-merge <SHA-1>

Der Befehl folgt den untergeordneten Elementen des angegebenen Commits, bis eine Zusammenführung in einen anderen Zweig (vermutlich Master) gefunden wird.

Jian
quelle
22

Das heißt, um Gauthiers Beitrag zusammenzufassen:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

BEARBEITEN: Da hierfür die Prozessersetzung " <()" verwendet wird, ist es nicht POSIX-kompatibel und funktioniert möglicherweise nicht mit Ihrer Shell. Es funktioniert mit bashoder zshobwohl.

Totor
quelle
4
Ich kopiere / füge nicht oft Code aus dem Internet ein, aber wenn ich das mache, funktioniert es perfekt! Danke Totor, dass ich nicht nachdenken darf.
Ben
2
@ TheodoreR.Smith Leider ist die Syntax <()nicht POSIX-kompatibel. Sie verwenden müssen bash, zshoder ein Shell - Unterstützung Prozess Substitution . Ich habe meine Antwort entsprechend bearbeitet.
Totor
Hat bei mir nicht funktioniert. Ich denke, mein Feature-Zweig wurde zuerst mit einem anderen Zweig zusammengeführt, bevor er mit dem Master zusammengeführt wurde. Dies könnte erklären, warum Ihre Befehle zu "Zweig 'release_branch' zusammenführen" und nicht zu Zweig 'feature_branch' zusammenführen "führen.
Tim Kuipers
14

Ich musste das tun und fand es irgendwie git-when-merged(was tatsächlich auf diese SO-Frage verweist, aber Michael Haggerty hat hier nie einen Verweis auf sein sehr schönes Python-Skript hinzugefügt). Also jetzt habe ich.

Alex Dupuy
quelle
Ich bin tatsächlich von seiner Seite zu dieser Frage gekommen.
Flavius
12

Aufbauend auf Gauthiers großartiger Antwort müssen wir commdie Listen nicht vergleichen. Da wir nach dem letzten Ergebnis suchen, in --ancestry-pathdem auch enthalten ist --first-parent, können wir in der Ausgabe des ersteren einfach nach dem letzteren suchen :

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

Oder für etwas Bissiges und Wiederverwendbares, hier ist eine Funktion, in die Sie eintauchen können .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
böser Streifen
quelle
Das hat bei mir funktioniert. commfunktionierte nicht, wenn die Eingaben nicht sortiert waren.
hIpPy
4

Für die Ruby-Menge gibt es Git-Woher . Sehr leicht.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway
Stahl
quelle
4

Ich benutze das folgende Bash-Skript, das ich am Pfad platziere ~/bin/git-find-merge. Es basiert auf Gauthiers Antwort und der Antwort von evilstreak mit wenigen Änderungen, um Eckfälle zu behandeln. commwird ausgelöst, wenn die Eingaben nicht sortiert sind. grep -ffunktioniert perfekt.

Eckfälle:

  • Wenn Commit ein Merge-Commit für den übergeordneten Pfad der Verzweigung ist, geben Sie Commit zurück.
  • Wenn Commit ein NON-Merge-Commit für den ersten übergeordneten Pfad der Verzweigung ist, geben Sie die Verzweigung zurück. Es handelt sich entweder um eine Zusammenführung oder um ein Commit, das sich nur in einem Zweig befindet, und es gibt keinen guten Weg, um das richtige Commit herauszufinden.
  • Wenn Commit und Branch identisch sind, geben Sie Commit zurück.

~/bin/git-find-merge Skript:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

Was mich das machen lässt:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

Dieses Skript ist auch auf meinem Github verfügbar .

hIpPy
quelle
2
git log --topo-order

Suchen Sie dann vor dem Festschreiben nach der ersten Zusammenführung.

römisch
quelle
1

Meine Ruby-Version von @ robinsts Idee funktioniert zweimal schneller (was wichtig ist, wenn nach sehr alten Commits gesucht wird).

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

Sie können es einfach so verwenden:

ruby find-commit.rb SHA master
Kaplan Ilya
quelle
0

Sie können so etwas versuchen. Die Idee ist, alle Zusammenführungs-Commits zu durchlaufen und zu prüfen, ob das Commit "c" von einem von ihnen aus erreichbar ist:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done
Holygeek
quelle
Dies ist das gleiche wie:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov
0

Ich musste dies mehrmals tun (danke an alle, die diese Frage beantwortet haben!) Und schrieb schließlich ein Skript (nach Gauthiers Methode), das ich meiner kleinen Sammlung von Git-Dienstprogrammen hinzufügen konnte. Sie finden es hier: https://github.com/mwoehlke/git-utils/blob/master/bin/git-merge-point .

Matthew
quelle