Temukan komit gabungan yang menyertakan komit tertentu

183

Bayangkan sejarah berikut ini:

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

Bagaimana saya dapat menemukan ketika komit "c" telah digabungkan menjadi master (yaitu, temukan merger komit "h")?

Guillaume Morin
sumber

Jawaban:

158

Contoh Anda menunjukkan bahwa cabang featuremasih tersedia.

Dalam hal ini hadalah hasil terakhir dari:

git log master ^feature --ancestry-path

Jika cabang featuretidak tersedia lagi, Anda bisa menunjukkan komit gabungan di garis sejarah antara cdan master:

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

Namun ini juga akan menunjukkan semua gabungan yang terjadi setelah h, dan di antara edan gdi feature.


Membandingkan hasil dari perintah berikut:

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

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

akan memberi Anda SHA-1 hsebagai baris terakhir yang sama.

Jika tersedia, Anda dapat menggunakan comm -1 -2hasil ini. Jika Anda menggunakan msysgit, Anda dapat menggunakan kode perl berikut untuk membandingkan:

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

(kode perl dari http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , yang mengambilnya dari "seseorang di grup berita comp.unix.shell").

Lihat penggantian proses jika Anda ingin membuatnya satu-liner.

Gauthier
sumber
4
Bahkan jika Anda memiliki comm, Anda tidak dapat menggunakannya karena output dari perintah "git rev-list" tidak diurutkan secara leksikografis. Anda tentu saja dapat mengurutkan output dari setiap perintah sebelum mencari baris yang sama, tetapi komit yang diinginkan tidak harus menjadi yang terakhir. Jadi saya pikir sesuatu seperti perintah perl (meskipun tidak jelas) diperlukan.
mhagger
12
Saya baru saja menulis skrip git-saat-digabung yang mengimplementasikan saran ini (dengan beberapa fitur lainnya). Lihat github.com/mhagger/git-when-merged
mhagger
2
Misalkan pada suatu saat masterdigabung menjadi feature, maka segera featuredigabung menjadi mastersebagai fast-forward (ujung featurepengganti master). Apakah itu menyebabkan --first-parentmengembalikan orangtua yang salah?
Kelvin
4
Saya mencoba comm -1 -2tetapi tidak berhasil. commhanya berfungsi pada garis yang diurutkan. (Perl one-liner bekerja, meskipun saya tidak bisa membacanya.)
Domon
2
Ini agak terlambat tetapi menambahkan sehingga orang dapat menemukannya berguna karena git tidak menyediakan ini. Ada beberapa kasus sudut yang saya pikir jawaban ini tidak menangani (lihat jawaban saya di bawah ini stackoverflow.com/a/43716029/58678 yang menangani sebagian besar kasus sudut). Berikut adalah kasus-kasus sudut: git find-merge h master(mengembalikan apa-apa selain harus mengembalikan h), git find-merge d master(mengembalikan f tetapi harus mengembalikan d), git find-merge c feature(mengembalikan e tetapi harus mengembalikan g).
hIpPy
133

Tambahkan ini ke Anda ~/.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'"

Maka Anda dapat menggunakan alias seperti ini:

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

Untuk melihat pesan komit gabungan dan detail lainnya, gunakan git show-mergedengan argumen yang sama.

(Berdasarkan jawaban Gauthier . Terima kasih kepada Rosen Matev dan javabrett karena telah memperbaiki masalah sort.)

robinst
sumber
6
Cantik! Ini adalah solusi drop-in terbaik.
N Jones
1
Berhati-hatilah karena sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2tidak menemukan baris terakhir yang benar secara umum. Berikut ini contoh gagal.
Rosen Matev
1
@RosenMatev Ditemukan di 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7sini ketika saya menjalankannya pada pasta Anda, apakah itu tidak diharapkan? OS apa yang Anda pakai?
robinst
1
@robinst, milikmu sudah benar. Saya mendapat "(GNU coreutils) 8.4" dan bagi saya ia menemukan29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev
2
@ Powow: Tidak ada, itu hanya satu perintah, bukan dua untuk kenyamanan.
robinst
28

git-get-merge akan menemukan dan menunjukkan komit gabungan yang Anda cari:

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

Perintah mengikuti anak-anak dari komit yang diberikan sampai penggabungan ke cabang lain (mungkin master) ditemukan.

Jian
sumber
22

Artinya, untuk meringkas posting Gauthier:

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

EDIT: karena ini menggunakan proses substitusi " <()", itu tidak kompatibel dengan POSIX, dan mungkin tidak berfungsi dengan shell Anda. Ini bekerja dengan bashatau zshmeskipun.

Totor
sumber
4
Saya tidak sering menyalin / menempel kode dari internet, tetapi ketika saya melakukannya berfungsi dengan baik! Terima kasih Totor karena mengizinkan saya untuk tidak berpikir.
Ben
2
@ TheodoreR.Smith sayangnya, sintaksnya <()tidak kompatibel dengan POSIX. Anda perlu menggunakan bash, zshatau substitusi proses pendukung shell . Saya mengedit jawaban saya sesuai dengan itu.
Totor
Tidak bekerja untuk saya. Saya pikir cabang fitur saya pertama kali digabungkan ke cabang lain sebelum digabung menjadi master. Itu bisa menjelaskan mengapa perintah Anda menghasilkan "Gabung cabang 'release_branch'" daripada "Gabungkan cabang 'fitur_branch'".
Tim Kuipers
14

Saya perlu melakukan ini, dan entah bagaimana menemukan git-when-merged(yang sebenarnya merujuk pertanyaan SO ini, tetapi Michael Haggerty tidak pernah menambahkan referensi ke skrip Python yang sangat bagus di sini). Jadi sekarang saya punya.

Alex Dupuy
sumber
Saya benar-benar datang ke pertanyaan ini dari halamannya.
Flavius
12

Membangun berdasarkan jawaban hebat Gauthier, kita tidak perlu menggunakan communtuk membandingkan daftar. Karena kami sedang mencari hasil terakhir --ancestry-pathyang juga ada --first-parent, kami dapat dengan mudah menerima yang terakhir di keluaran yang pertama:

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

Atau untuk sesuatu yang tajam dan dapat digunakan kembali, inilah fungsi untuk muncul .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
kejahatan
sumber
Ini berhasil untuk saya. commtidak berfungsi saat input tidak diurutkan.
hIpPy
4

Untuk kerumunan Ruby, ada git-mana . Sangat mudah.

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

Saya menggunakan skrip bash di bawah ini yang saya tempatkan di jalur ~/bin/git-find-merge. Ini didasarkan pada jawaban Gauthier dan jawaban evilstreak dengan beberapa penyesuaian untuk menangani kasus sudut. commmelempar ketika input tidak diurutkan. grep -fbekerja dengan sempurna.

Kasus sudut:

  • Jika komit adalah gabungan komit di jalur cabang orang tua pertama, maka kembalikan komit.
  • Jika komit adalah NON-gabungkan komit di jalur cabang orang tua pertama, lalu kembalikan cabang. Entah gabungan atau komit hanya pada cabang dan tidak ada cara yang baik untuk mengetahui komit yang tepat.
  • Jika komit dan cabang sama, maka kembalikan komit.

~/bin/git-find-merge naskah:

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

Yang memungkinkan saya melakukan ini:

(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

Skrip ini juga tersedia di github saya .

hIpPy
sumber
2
git log --topo-order

Kemudian cari gabungan pertama sebelum komit.

Roma
sumber
1

Versi ruby ​​saya dari ide @ robinst, bekerja dua kali lebih cepat (yang penting ketika mencari komit yang sangat lama).

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

Anda bisa menggunakannya seperti ini:

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

Anda dapat mencoba sesuatu seperti ini. Idenya adalah untuk beralih melalui semua gabungan komit dan melihat apakah komit "c" dapat dicapai dari salah satu dari mereka:

$ 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
sumber
Ini sama dengan:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov
0

Saya harus melakukan ini beberapa kali (terima kasih kepada semua orang yang menjawab pertanyaan ini!), Dan akhirnya menulis skrip (menggunakan metode Gauthier) yang dapat saya tambahkan ke koleksi kecil utilitas git saya. Anda dapat menemukannya di sini: https://github.com/mwoehlke/git-utils/blob/master/bin/git-merge-point .

Matius
sumber