Znajdź zatwierdzenie scalające, które zawiera określone zatwierdzenie

183

Wyobraź sobie następującą historię:

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

Jak mogę sprawdzić, kiedy zatwierdzenie „c” zostało scalone z nadrzędnym (tj. Znaleźć polecenie scalające „h”)?

Guillaume Morin
źródło

Odpowiedzi:

158

Twój przykład pokazuje, że gałąź featurejest nadal dostępna.

W takim przypadku hjest to ostatni wynik:

git log master ^feature --ancestry-path

Jeśli gałąź featurenie jest już dostępna, możesz wyświetlić zatwierdzenia scalania w linii historii między ca master:

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

Będzie to jednak również pokazać wszystkie scala że stało się po hi między ei gna temat feature.


Porównanie wyniku następujących poleceń:

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

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

poda SHA-1 hjako ostatni wspólny wiersz.

Jeśli masz to dostępne, możesz użyć comm -1 -2na tych wynikach. Jeśli jesteś na msysgit, możesz użyć następującego kodu Perla do porównania:

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

(kod perla z http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , który wziął go od „kogoś z grupy dyskusyjnej comp.unix.shell”).

Zobacz podstawianie procesu, jeśli chcesz, aby było to jednowierszowe.

Gauthier
źródło
4
Nawet jeśli masz comm, nie możesz go użyć, ponieważ dane wyjściowe polecenia "git rev-list" nie są posortowane leksykograficznie. Możesz oczywiście posortować dane wyjściowe każdego polecenia, zanim zaczniesz szukać wspólnych wierszy, ale wtedy pożądane zatwierdzenie niekoniecznie będzie ostatnim. Więc myślę, że coś takiego jak polecenie Perl (choć niejasne) jest konieczne.
mhagger
12
Właśnie napisałem skrypt git-when-scaled, który implementuje tę sugestię (z kilkoma innymi funkcjami). Zobacz github.com/mhagger/git-when-merged
mhagger
2
Załóżmy, że w pewnym momencie masterzostał scalony z feature, a następnie natychmiast featurejest scalony masterjako przewijanie do przodu (wskazówka featurezastępuje master). Czy to spowodowałoby --first-parentpowrót niewłaściwego rodzica?
Kelvin,
4
Próbowałem, comm -1 -2ale to nie zadziałało. commdziała tylko na posortowanych liniach. (Jedna linijka perla działa, chociaż nie mogłem jej przeczytać.)
Domon
2
To trochę za późno, ale dodawanie, aby ludzie mogli uznać to za przydatne, ponieważ natywnie git tego nie zapewnia. Są pewne przypadki narożne, które moim zdaniem nie rozwiązują (zobacz moją odpowiedź poniżej stackoverflow.com/a/43716029/58678, która obsługuje większość przypadków narożnych). Oto narożne przypadki: git find-merge h master(zwraca nic, ale powinno zwrócić h), git find-merge d master(zwraca f, ale powinno zwrócić d), git find-merge c feature(zwraca e, ale powinno zwrócić g).
hIpPy
133

Dodaj to do ~/.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'"

Następnie możesz użyć takich aliasów:

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

Aby zobaczyć komunikat zatwierdzenia scalającego i inne szczegóły, użyj git show-mergez tymi samymi argumentami.

(Na podstawie odpowiedzi Gauthiera . Podziękowania dla Rosena Mateva i javabretta za naprawienie problemu z sort.)

robinst
źródło
6
Piękny! To najlepsze rozwiązanie typu drop-in.
N Jones
1
Uważaj, ponieważ sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2nie znajduje poprawnie ostatniego wspólnego wiersza. Oto przykład niepowodzenia.
Rosen Matev,
1
@RosenMatev Znajduje się 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7tutaj, kiedy uruchomię go na twojej paście, czy nie jest to oczekiwane? Z jakiego systemu operacyjnego korzystasz?
robinst
1
@robinst, twoja jest poprawna. Mam „(GNU coreutils) 8.4” i dla mnie to znajduje29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev,
2
@powlo: Nic, dla wygody to tylko jedno polecenie zamiast dwóch.
robinst
28

git-get-merge zlokalizuje i pokaże zatwierdzenie scalające, którego szukasz:

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

Polecenie podąża za elementami podrzędnymi danego zatwierdzenia, dopóki nie zostanie znalezione połączenie z inną gałęzią (prawdopodobnie master).

Jian
źródło
22

Czyli podsumowując post Gauthiera:

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

EDYCJA: ponieważ używa podstawiania procesu " <()", nie jest kompatybilna z POSIX i może nie działać z twoją powłoką. Działa z bashlub zshchociaż.

Totor
źródło
4
Nieczęsto kopiuję / wklejam kod z internetu, ale kiedy to robię, działa to idealnie! Dzięki Totor, że pozwoliłeś mi nie myśleć.
Ben
2
@ TheodoreR.Smith, niestety, składnia <()nie jest zgodna z POSIX. Trzeba użyć bash, zshalbo powłokę nośną zmiany procesu . Odpowiednio zredagowałem odpowiedź.
Totor
Nie działa dla mnie. Myślę, że moja gałąź funkcji najpierw została scalona z inną gałęzią, zanim została połączona z wzorcem. To może wyjaśniać, dlaczego Twoje polecenia powodują wyświetlenie „Merge branch 'release_branch'” zamiast „Merge branch 'feature_branch'”.
Tim Kuipers
14

Musiałem to zrobić i jakoś znalazłem git-when-merged(co faktycznie odnosi się do tego pytania SO, ale Michael Haggerty nigdy nie dodał tutaj odniesienia do swojego bardzo ładnego skryptu Pythona). Więc teraz mam.

Alex Dupuy
źródło
Właściwie doszedłem do tego pytania z jego strony.
Flavius
12

Opierając się na świetnej odpowiedzi Gauthiera, nie musimy używać commdo porównywania list. Ponieważ szukamy ostatniego wyniku, w --ancestry-pathktórym również znajduje się w --first-parent, możemy po prostu grepować ten drugi w wyniku pierwszego:

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

A jeśli szukasz czegoś zgrabnego i wielokrotnego użytku, oto funkcja, do której można wejść .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
zła passa
źródło
To zadziałało dla mnie. commnie działał, gdy dane wejściowe nie były posortowane.
hIpPy
4

Dla tłumu Ruby jest git-skąd . Bardzo łatwe.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway
stal
źródło
4

Używam poniżej skryptu bash, który umieszczam na ścieżce ~/bin/git-find-merge. Jest on oparty na odpowiedź Gauthier jest i odpowiedź evilstreak jest z kilku poprawek do obsługi spraw rożny. commzgłasza, gdy dane wejściowe nie są posortowane. grep -fdziała świetnie.

Obudowy narożne:

  • Jeśli zatwierdzenie jest zatwierdzeniem scalającym na pierwszej ścieżce nadrzędnej gałęzi, zwróć zatwierdzenie.
  • Jeśli zatwierdzenie jest zatwierdzeniem NON-merge na pierwszej ścieżce nadrzędnej gałęzi, a następnie zwraca gałąź. To albo ff merge, albo commit jest tylko na gałęzi i nie ma dobrego sposobu na znalezienie właściwego zatwierdzenia.
  • Jeśli zatwierdzenie i gałąź są takie same, zwróć zatwierdzenie.

~/bin/git-find-merge scenariusz:

#!/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

Co pozwala mi to zrobić:

(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

Ten skrypt jest również dostępny na moim githubie .

hIpPy
źródło
2
git log --topo-order

Następnie poszukaj pierwszego scalenia przed zatwierdzeniem.

rzymski
źródło
1

Moja rubinowa wersja pomysłu @ robinst działa dwa razy szybciej (co jest ważne podczas wyszukiwania bardzo starego commita).

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

Możesz go po prostu użyć w ten sposób:

ruby find-commit.rb SHA master
Kaplan Ilya
źródło
0

Możesz spróbować czegoś takiego. Chodzi o to, aby iterować przez wszystkie zatwierdzenia typu merge i sprawdzić, czy zatwierdzenie „c” jest osiągalne z jednego z nich:

$ 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
źródło
To jest to samo, co:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov