Menemukan titik cabang dengan Git?

454

Saya memiliki repositori dengan cabang master dan A dan banyak aktivitas penggabungan antara keduanya. Bagaimana saya bisa menemukan komit di repositori saya ketika cabang A dibuat berdasarkan master?

Repositori saya pada dasarnya terlihat seperti ini:

-- X -- A -- B -- C -- D -- F  (master) 
          \     /   \     /
           \   /     \   /
             G -- H -- I -- J  (branch A)

Saya mencari revisi A, yang bukan yang git merge-base (--all)ditemukan.

Jochen
sumber

Jawaban:

498

Saya mencari hal yang sama, dan saya menemukan pertanyaan ini. Terima kasih telah menanyakannya!

Namun, saya menemukan bahwa jawaban yang saya lihat di sini tampaknya tidak cukup memberikan jawaban yang Anda minta (atau yang saya cari) - mereka tampaknya memberikan Gkomit, bukan Akomit.

Jadi, saya telah membuat pohon berikut (surat-surat yang diberikan dalam urutan kronologis), sehingga saya dapat menguji berbagai hal:

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

Ini terlihat sedikit berbeda dari milik Anda, karena saya ingin memastikan bahwa saya mendapat (mengacu pada grafik ini, bukan milik Anda) B, tetapi bukan A (dan bukan D atau E). Berikut adalah surat-surat yang dilampirkan pada awalan SHA dan komit pesan (repo saya dapat dikloning dari sini , jika itu menarik bagi siapa pun):

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

Jadi, tujuan: menemukan B . Berikut adalah tiga cara yang saya temukan, setelah sedikit mengutak-atik:


1. secara visual, dengan gitk:

Anda harus melihat secara visual pohon seperti ini (seperti yang dilihat dari master):

tangkapan layar gitk dari master

atau di sini (dilihat dari topik):

tangkapan layar gitk dari topik

dalam kedua kasus, saya telah memilih komit yang ada Bdi grafik saya. Setelah Anda mengkliknya, SHA lengkapnya disajikan dalam bidang input teks tepat di bawah grafik.


2. secara visual, tetapi dari terminal:

git log --graph --oneline --all

(Edit / catatan tambahan: menambahkan --decoratejuga bisa menarik; menambahkan indikasi nama cabang, tag, dll. Tidak menambahkan ini ke baris perintah di atas karena output di bawah ini tidak mencerminkan penggunaannya.)

yang menunjukkan (dengan asumsi git config --global color.ui auto):

keluaran dari git log --graph --oneline --all

Atau, dalam teks langsung:

* a9546a2 bergabung dari topik kembali ke master
| \  
| * 648ca35 menggabungkan master ke topik
| | \  
| * | 132ee2a komit pertama pada cabang topik
* | | e7c863d komit pada master setelah master digabung ke topik
| | /  
| / |   
* | 37ad159 pasca-cabang berkomitmen pada master
| /  
* 6aafd7f komit kedua pada master sebelum bercabang
* 4112403 komit awal pada master

dalam kedua kasus, kita melihat 6aafd7f komit sebagai titik umum terendah, yaitu Bdi grafik saya, atau Adi Anda.


3. Dengan shell magic:

Anda tidak menentukan dalam pertanyaan Anda apakah Anda menginginkan sesuatu seperti di atas, atau satu perintah yang hanya akan membuat Anda satu revisi, dan tidak ada yang lain. Nah, ini yang terakhir:

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

Yang juga bisa Anda masukkan ke ~ / .gitconfig Anda (catatan: trailing dash itu penting; terima kasih Brian karena telah memperhatikan itu) :

[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' -

Yang dapat dilakukan melalui baris perintah berikut (berbelit-belit dengan mengutip):

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'\'' -'

Catatan: zshbisa saja dengan mudah bash, tetapi tidaksh akan berfungsi - sintaksisnya tidak ada di vanilla . (Sekali lagi terima kasih, @conny, karena membuat saya menyadarinya dalam komentar tentang jawaban lain di halaman ini!)<()sh

Catatan: Versi alternatif di atas:

Berkat liori untuk menunjukkan bahwa di atas bisa jatuh ketika membandingkan cabang identik, dan muncul dengan bentuk diff alternatif yang menghilangkan bentuk sed dari campuran, dan membuat ini "lebih aman" (yaitu mengembalikan hasil (yaitu, komit terbaru) bahkan ketika Anda membandingkan master ke master):

Sebagai baris .git-config:

[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' -

Dari 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'\'' -'

Jadi, di pohon tes saya (yang tidak tersedia untuk sementara waktu, maaf; sudah kembali), yang sekarang bekerja pada master dan topik (masing-masing memberikan komit G dan B). Terima kasih lagi, liori, untuk formulir alternatif.


Jadi, itulah yang saya [dan liori] buat. Tampaknya bekerja untuk saya. Itu juga memungkinkan beberapa alias tambahan yang mungkin berguna:

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

Selamat git!

lindes
sumber
5
Terima kasih lindes, opsi shell sangat bagus untuk situasi di mana Anda ingin menemukan titik cabang dari cabang pemeliharaan yang berjalan lama. Saat Anda mencari revisi yang mungkin seribu komit di masa lalu, opsi visual benar-benar tidak akan memotongnya. * 8 ')
Mark Booth
4
Dalam metode ketiga Anda, Anda bergantung pada konteks akan menunjukkan baris pertama yang tidak berubah. Ini tidak akan terjadi pada kasus tepi tertentu atau jika Anda memiliki persyaratan yang sedikit berbeda (mis. Saya hanya perlu salah satu dari sejarah menjadi --first-parent, dan saya menggunakan metode ini dalam skrip yang kadang-kadang mungkin menggunakan yang sama cabang di kedua sisi). Saya menemukan lebih aman untuk menggunakan diff's if-then-else modus dan menghapus berubah / dihapus baris dari output bukannya menghitung pada memiliki konteks yang cukup besar, oleh.: diff --old-line-format='' --new-line-format='' <(git rev-list …) <(git rev-list …)|head -1.
liori
3
git log -n1 --format=format:%H $(git log --reverse --format=format:%H master..topic | head -1)~akan bekerja juga, saya pikir
Tobias Schulte
4
@ JakubNarębski: Apakah Anda punya cara untuk git merge-base --fork-point ...memberi commit B (commit 6aafd7f) untuk pohon ini? Saya sedang sibuk dengan hal-hal lain ketika Anda memposting itu, dan itu terdengar bagus, tapi akhirnya saya mencobanya, dan saya tidak membuatnya berfungsi ... Saya juga mendapatkan sesuatu yang lebih baru, atau hanya gagal diam (tidak ada kesalahan) pesan, namun status keluar 1), berusaha argumen seperti git co master; git merge-base --fork-point topic, git co topic; git merge-base --fork-point master, git merge-base --fork-point topic master(baik untuk checkout), dll Apakah ada sesuatu yang saya lakukan salah atau hilang?
lindes
25
@ JakubNarębski @lindes --fork-pointdidasarkan pada reflog, jadi itu hanya akan berfungsi jika Anda membuat perubahan secara lokal. Meskipun begitu, entri reflog dapat kedaluwarsa. Ini berguna tetapi tidak dapat diandalkan sama sekali.
Daniel Lubarov
131

Anda mungkin mencari git merge-base:

git merge-base menemukan leluhur yang sama (s) terbaik antara dua komit untuk digunakan dalam gabungan tiga arah. Satu leluhur yang sama lebih baik dari leluhur yang sama jika leluhur yang kedua adalah leluhur yang terdahulu. Nenek moyang bersama yang tidak memiliki leluhur bersama yang lebih baik adalah leluhur bersama terbaik , yaitu basis gabungan . Perhatikan bahwa mungkin ada lebih dari satu pangkalan gabungan untuk sepasang komit.

Greg Hewgill
sumber
10
Perhatikan juga --allopsi untuk " git merge-base"
Jakub Narębski
10
Ini tidak menjawab pertanyaan awal, tetapi kebanyakan orang menanyakan pertanyaan yang jauh lebih sederhana yang mana jawabannya: :)
FelipeC
6
dia bilang dia tidak mau hasil dari git merge-base
Tom Tanner
9
@ TomTanner: Saya baru saja melihat riwayat pertanyaan dan pertanyaan asli diedit untuk memasukkan catatan itu sekitar git merge-baselima jam setelah jawaban saya diposting (mungkin sebagai jawaban atas jawaban saya). Namun demikian, saya akan membiarkan jawaban ini apa adanya karena mungkin masih bermanfaat bagi orang lain yang menemukan pertanyaan ini melalui pencarian.
Greg Hewgill
Saya percaya Anda bisa menggunakan hasil dari gabungan -satu - semua dan kemudian menyaring daftar pengembalian dengan menggunakan menggabungkan-basis --adalah leluhur untuk menemukan leluhur umum tertua dari daftar, tetapi ini bukan cara paling sederhana .
Matt_G
42

Saya sudah terbiasa git rev-listdengan hal semacam ini. Misalnya, (perhatikan 3 titik)

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

akan memuntahkan titik cabang. Sekarang, itu tidak sempurna; karena Anda telah menggabungkan master ke cabang A beberapa kali, itu akan membagi beberapa kemungkinan poin cabang (pada dasarnya, titik cabang asli dan kemudian setiap titik di mana Anda menggabungkan master ke cabang A). Namun, setidaknya harus mempersempit kemungkinan.

Saya telah menambahkan perintah itu ke alias saya ~/.gitconfigsebagai:

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

jadi saya bisa menyebutnya sebagai:

$ git diverges branch-a master
mipadi
sumber
Catatan: ini tampaknya memberikan komit pertama pada cabang, bukan leluhur yang sama. (Yaitu memberi Gbukan A, per grafik dalam pertanyaan asli.) Saya punya jawaban yang didapat A, bahwa saya akan memposting saat ini.
lindes
@ lindes: Ini memberi leluhur yang sama dalam setiap kasus saya sudah mencobanya. Apakah Anda memiliki contoh di mana tidak?
mipadi
Iya. Dalam jawaban saya (yang memiliki tautan ke repo Anda dapat mengkloning; git checkout topickemudian menjalankannya dengan topicdi tempat branch-a), ia mencantumkan 648ca357b946939654da12aaf2dc072763f3caeedan 37ad15952db5c065679d7fc31838369712f0b338- keduanya 37ad159dan 648ca35berada dalam leluhur cabang saat ini (yang terakhir adalah kepala KEPALA saat ini topic), tetapi tidak ada titik sebelum percabangan terjadi. Apakah Anda mendapatkan sesuatu yang berbeda?
lindes
@ lindes: Saya tidak dapat mengkloning repo Anda (mungkin masalah izin?).
mipadi
Ups, maaf! Terima kasih telah memberitahu saya. Saya lupa menjalankan git update-server-info. Seharusnya lebih baik pergi sekarang. :)
lindes
31

Jika Anda suka perintah singkat,

git rev-list $ (daftar git rev - first-parent ^ branch_name master | tail -n1) ^^! 

Berikut penjelasannya.

Perintah berikut memberi Anda daftar semua komit di master yang terjadi setelah branch_name dibuat

git rev-list --first-parent ^ branch_name master 

Karena Anda hanya peduli tentang komitmen paling awal, Anda ingin baris terakhir dari output:

git rev-list ^ branch_name - master pertama-induk | ekor -n1

Induk dari komit paling awal yang bukan nenek moyang dari "branch_name" adalah, menurut definisi, dalam "branch_name," dan berada di "master" karena itu merupakan leluhur dari sesuatu dalam "master." Jadi, Anda memiliki komit paling awal yang ada di kedua cabang.

Perintah

git rev-list commit ^^!

hanyalah cara untuk menunjukkan referensi komit induk. Anda bisa menggunakannya

git log -1 commit ^

atau terserah.

PS: Saya tidak setuju dengan argumen bahwa tatanan leluhur tidak relevan. Tergantung pada apa yang Anda inginkan. Misalnya, dalam hal ini

Master _C1___C2_______
  \ \ _XXXXX_ cabang A (Xs menunjukkan saling silang antara master dan A)
   \ _____ / cabang B

masuk akal untuk menghasilkan C2 sebagai komit "percabangan". Ini terjadi ketika pengembang keluar dari "master." Ketika dia bercabang, cabang "B" bahkan tidak bergabung di cabangnya! Inilah yang diberikan oleh solusi dalam posting ini.

Jika yang Anda inginkan adalah komit C terakhir sehingga semua jalur dari asal ke komit terakhir di cabang "A" melewati C, maka Anda ingin mengabaikan urutan leluhur. Itu murni topologi dan memberi Anda ide sejak saat Anda memiliki dua versi kode yang berjalan pada saat yang sama. Saat itulah Anda akan menggunakan pendekatan berbasis gabungan, dan itu akan mengembalikan C1 dalam contoh saya.

Lionel
sumber
3
Sejauh ini, ini adalah jawaban terbersih, mari kita pilih ini sebagai yang teratas. Suntingan yang disarankan: git rev-list commit^^!dapat disederhanakan sebagaigit rev-parse commit^
Russell Davis
Ini seharusnya jawabannya!
trinth
2
Jawaban ini bagus, saya hanya mengganti git rev-list --first-parent ^branch_name masterdengan git rev-list --first-parent branch_name ^masterkarena jika cabang master 0 berkomitmen di depan cabang lain (fast-forwardable untuk itu), tidak ada output yang akan dibuat. Dengan solusi saya, tidak ada output yang dibuat jika master benar-benar di depan (yaitu cabang telah sepenuhnya digabung), itulah yang saya inginkan.
Michael Schmeißer
3
Ini tidak akan berhasil kecuali saya benar-benar kehilangan sesuatu. Ada gabungan di kedua arah di cabang contoh. Sepertinya Anda mencoba mempertimbangkannya, tetapi saya yakin ini akan menyebabkan jawaban Anda gagal. git rev-list --first-parent ^topic masterhanya akan membawa Anda kembali ke komit pertama setelah penggabungan terakhir dari masterke topic(jika itu ada).
jerry
1
@ Jerry Anda benar, jawaban ini sampah; misalnya, dalam hal backmerge baru saja terjadi (master digabung ke dalam topik), dan master tidak memiliki komit baru setelah itu, perintah git rev-list pertama - perintah first-parent tidak menghasilkan apa-apa sama sekali.
TamaMcGlinn
19

Mengingat begitu banyak jawaban di utas ini tidak memberikan jawaban yang ditanyakan oleh pertanyaan itu, berikut adalah ringkasan hasil dari setiap solusi, bersama dengan skrip yang saya gunakan untuk mereplikasi repositori yang diberikan dalam pertanyaan.

Log

Membuat repositori dengan struktur yang diberikan, kita mendapatkan log git dari:

$ 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

Satu-satunya tambahan saya, adalah tag yang membuatnya eksplisit tentang titik di mana kami membuat cabang dan dengan demikian komit yang ingin kami temukan.

Solusi yang berhasil

Satu-satunya solusi yang berfungsi adalah yang disediakan oleh lindes dengan benar mengembalikan A:

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

Seperti yang ditunjukkan Charles Bailey , solusi ini sangat rapuh.

Jika Anda branch_Ake dalam masterdan kemudian menggabungkan masterke dalam branch_Atanpa intervensi komit maka solusi Lindes' hanya memberi Anda terbaru pertama divergance .

Itu berarti bahwa untuk alur kerja saya, saya pikir saya harus tetap dengan menandai titik cabang dari cabang yang sudah berjalan lama, karena saya tidak dapat menjamin bahwa mereka dapat ditemukan nanti.

Ini benar-benar semua bermuara gits kurang dari apa yang hgdisebutnya bernama cabang . Blogger jhw menyebut garis keturunan ini vs. keluarga dalam artikelnya Why I Like Mercurial More Than Git dan artikel lanjutannya More On Mercurial vs. Git (dengan Grafik!) . Saya akan merekomendasikan orang membaca mereka untuk melihat mengapa beberapa mualaf lincah rindu tidak memiliki nama cabang di git.

Solusi yang tidak berhasil

Solusi yang diberikan oleh mipadi menghasilkan dua jawaban, Idan C:

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

Solusi yang diberikan oleh Greg Hewgill kembaliI

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

Solusi yang diberikan oleh Karl kembali X:

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

Naskah

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"

Saya ragu versi git membuat banyak perbedaan untuk ini, tetapi:

$ git --version
git version 1.7.1

Terima kasih kepada Charles Bailey karena telah menunjukkan kepada saya cara yang lebih ringkas untuk membuat skrip contoh repositori.

Mark Booth
sumber
2
Solusi oleh Karl mudah untuk memperbaiki: diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1. Terima kasih telah memberikan instruksi untuk membuat repo :)
FelipeC
Saya pikir maksud Anda "Variasi yang dibersihkan dari solusi yang disediakan oleh Karl mengembalikan X". Yang asli bekerja dengan baik itu hanya jelek :-)
Karl
Tidak, dokumen asli Anda tidak berfungsi dengan baik. Memang, variasi bekerja lebih buruk. Tetapi menambahkan opsi --topo-order membuat versi Anda berfungsi :)
FelipeC
@felipec - Lihat komentar terakhir saya pada jawaban oleh Charles Bailey . Sayangnya obrolan kami (dan dengan demikian semua komentar lama) sekarang telah dihapus. Saya akan mencoba memperbarui jawaban saya ketika saya punya waktu.
Mark Booth
Menarik. Saya anggap topologi adalah default. Konyol saya :-)
Karl
10

Secara umum, ini tidak mungkin. Dalam sejarah cabang, cabang-dan-merger sebelum cabang bernama bercabang dan cabang perantara dari dua cabang bernama terlihat sama.

Di git, cabang hanyalah nama terkini dari ujung bagian sejarah. Mereka tidak benar-benar memiliki identitas yang kuat.

Ini biasanya bukan masalah besar karena basis gabungan (lihat jawaban Greg Hewgill) dari dua commit biasanya jauh lebih bermanfaat, memberikan komit terbaru yang dibagikan oleh kedua cabang.

Solusi yang mengandalkan urutan orang tua dari komit jelas tidak akan berfungsi dalam situasi di mana cabang telah sepenuhnya terintegrasi pada beberapa titik dalam sejarah cabang.

git commit --allow-empty -m root # actual branch commit
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git merge --ff-only master
git commit --allow-empty -m "More work on branch_A"
git checkout master
git commit --allow-empty -m "More work on master"

Teknik ini juga jatuh jika gabungan integrasi telah dibuat dengan orang tua terbalik (misalnya cabang sementara digunakan untuk melakukan uji gabungan menjadi master dan kemudian diteruskan ke cabang fitur untuk membangun lebih jauh).

git commit --allow-empty -m root # actual branch point
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git commit --allow-empty -m "More work on branch_A"

git checkout -b tmp-branch master
git merge -m "Merge branch_A into tmp-branch (master copy)" branch_A
git checkout branch_A
git merge --ff-only tmp-branch
git branch -d tmp-branch

git checkout master
git commit --allow-empty -m "More work on master"
CB Bailey
sumber
3
Terima kasih Charles, Anda sudah meyakinkan saya, jika saya ingin tahu titik di mana cabang awalnya berbeda , saya harus menandai itu. Saya benar-benar berharap gitmemiliki hgcabang yang setara dengan yang bernama, itu akan membuat mengelola cabang pemeliharaan yang berumur panjang jadi lebih mudah.
Mark Booth
1
"Dalam git, cabang hanyalah nama terkini dari ujung bagian sejarah. Mereka tidak benar-benar memiliki identitas yang kuat" Itu hal yang menakutkan untuk dikatakan dan meyakinkan saya bahwa saya perlu memahami cabang Git dengan lebih baik - terima kasih (+ 1)
dumbledad
Dalam sejarah cabang, cabang-dan-merger sebelum cabang bernama bercabang dan cabang perantara dari dua cabang bernama terlihat sama. Ya. +1.
user1071847
5

Bagaimana dengan sesuatu seperti

git log --pretty=oneline master > 1
git log --pretty=oneline branch_A > 2

git rev-parse `diff 1 2 | tail -1 | cut -c 3-42`^
Karl
sumber
Ini bekerja. Benar-benar rumit tetapi satu-satunya hal yang saya temukan yang tampaknya benar-benar melakukan pekerjaan itu.
GaryO
1
Git alias setara: diverges = !bash -c 'git rev-parse $(diff <(git log --pretty=oneline ${1}) <(git log --pretty=oneline ${2}) | tail -1 | cut -c 3-42)^'(tanpa file sementara)
conny
@conny: Oh, wow - Saya belum pernah melihat sintaks <(foo) ... itu sangat berguna, terima kasih! (Bekerja di zsh, juga, FYI.)
lindes
ini tampaknya memberi saya komitmen pertama pada cabang, daripada leluhur yang sama. (Yaitu memberi Gbukan A, per grafik dalam pertanyaan asli.) Saya pikir saya telah menemukan jawaban, yang akan saya posting saat ini.
lindes
Alih-alih 'git log --pretty = oneline' Anda hanya dapat melakukan 'git rev-list', maka Anda dapat melewatkan cut juga, apalagi, ini memberikan orang tua komit dari titik divergensi, jadi hanya ekor -2 | kepala 1. Jadi:diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
FelipeC
5

pasti saya melewatkan sesuatu, tetapi IMO, semua masalah di atas disebabkan karena kami selalu berusaha menemukan titik cabang kembali dalam sejarah, dan itu menyebabkan semua jenis masalah karena kombinasi penggabungan yang tersedia.

Alih-alih, saya telah mengikuti pendekatan yang berbeda, berdasarkan fakta bahwa kedua cabang memiliki banyak sejarah, persis semua sejarah sebelum bercabang adalah 100% sama, jadi alih-alih kembali, proposal saya adalah tentang maju (dari tanggal 1). commit), mencari perbedaan pertama di kedua cabang. Titik cabang akan, sederhananya, induk dari perbedaan pertama yang ditemukan.

Dalam praktek:

#!/bin/bash
diff <( git rev-list "${1:-master}" --reverse --topo-order ) \
     <( git rev-list "${2:-HEAD}" --reverse --topo-order) \
--unified=1 | sed -ne 's/^ //p' | head -1

Dan itu menyelesaikan semua kasus saya yang biasa. Tentu ada perbatasan yang tidak tertutup tapi ... ciao :-)

stronk7
sumber
diff <(git rev-list "$ {1: -master}" --first-parent) <(git rev-list "$ {2: -HEAD}" --first-parent) -U1 | tail -1
Alexander Bird
saya menemukan ini lebih cepat (2-100x):comm --nocheck-order -1 -2 <(git rev-list --reverse --topo-order topic) <(git rev-list --reverse --topo-order master) | head -1
Andrei Neculau
4

Setelah banyak penelitian dan diskusi, jelas tidak ada peluru ajaib yang akan berfungsi dalam semua situasi, setidaknya tidak dalam versi Git saat ini.

Itu sebabnya saya menulis beberapa tambalan yang menambahkan konsep tailcabang. Setiap kali cabang dibuat, pointer ke titik awal juga dibuat, tailref. Ref ini diperbarui setiap kali cabang ditata ulang.

Untuk mengetahui titik cabang dari cabang devel, yang harus Anda lakukan adalah menggunakan devel@{tail}, itu saja.

https://github.com/felipec/git/commits/fc/tail

FelipeC
sumber
1
Mungkin menjadi satu-satunya solusi yang stabil. Apakah Anda melihat apakah ini bisa masuk ke git? Saya tidak melihat permintaan tarik.
Alexander Klimetschek
@AlexanderKlimetschek Saya tidak mengirim tambalan, dan saya pikir itu tidak akan diterima. Namun, saya mencoba metode yang berbeda: hook "update-branch" yang melakukan sesuatu yang sangat mirip. Cara ini secara default Git tidak akan melakukan apa-apa, tetapi Anda dapat mengaktifkan hook untuk memperbarui cabang ekor. Anda tidak akan memiliki devel @ {tail}, tetapi tidak akan terlalu buruk untuk menggunakan tails / devel sebagai gantinya.
FelipeC
3

Berikut ini adalah versi perbaikan dari jawaban sebelumnya saya jawaban sebelumnya . Itu bergantung pada pesan komit dari gabungan untuk menemukan di mana cabang pertama kali dibuat.

Ini bekerja pada semua repositori yang disebutkan di sini, dan saya bahkan telah membahas beberapa yang rumit yang muncul di milis . Saya juga menulis tes untuk ini.

find_merge ()
{
    local selection extra
    test "$2" && extra=" into $2"
    git rev-list --min-parents=2 --grep="Merge branch '$1'$extra" --topo-order ${3:---all} | tail -1
}

branch_point ()
{
    local first_merge second_merge merge
    first_merge=$(find_merge $1 "" "$1 $2")
    second_merge=$(find_merge $2 $1 $first_merge)
    merge=${second_merge:-$first_merge}

    if [ "$merge" ]; then
        git merge-base $merge^1 $merge^2
    else
        git merge-base $1 $2
    fi
}
FelipeC
sumber
3

Perintah berikut akan mengungkapkan SHA1 dari Komit A

git merge-base --fork-point A

Gayan Pathirage
sumber
Ini tidak akan terjadi jika cabang induk dan anak memiliki gabungan antara satu sama lain di antaranya.
nawfal
2

Saya sepertinya senang

git rev-list branch...master

Baris terakhir yang Anda dapatkan adalah komit pertama di cabang, jadi itu masalah mendapatkan induk dari itu. Begitu

git rev-list -1 `git rev-list branch...master | tail -1`^

Tampaknya bekerja untuk saya dan tidak perlu diff dan sebagainya (yang membantu karena kami tidak memiliki versi diff)

Koreksi: Ini tidak berfungsi jika Anda berada di cabang master, tapi saya melakukan ini dalam skrip sehingga itu tidak menjadi masalah

Tom Tanner
sumber
2

Untuk menemukan komit dari titik percabangan, Anda bisa menggunakan ini.

git log --ancestry-path master..topicbranch
Andor
sumber
Perintah ini tidak berfungsi untuk saya pada contoh yang diberikan. Tolong apa yang akan Anda berikan sebagai parameter untuk rentang komit?
Jesper Rønn-Jensen
2

Kadang-kadang secara efektif tidak mungkin (dengan beberapa pengecualian di mana Anda mungkin beruntung memiliki data tambahan) dan solusi di sini tidak akan berfungsi.

Git tidak menyimpan riwayat referensi (termasuk cabang). Hanya menyimpan posisi saat ini untuk setiap cabang (kepala). Ini berarti Anda dapat kehilangan beberapa sejarah cabang di git seiring waktu. Setiap kali Anda bercabang misalnya, segera hilang cabang mana yang asli. Semua yang dilakukan cabang adalah:

git checkout branch1    # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1

Anda mungkin berasumsi bahwa komitmen pertama adalah cabang. Ini cenderung menjadi masalah tetapi tidak selalu demikian. Tidak ada yang menghentikan Anda dari melakukan ke salah satu cabang terlebih dahulu setelah operasi di atas. Selain itu, cap waktu git tidak dijamin dapat diandalkan. Tidak sampai Anda berkomitmen untuk keduanya bahwa mereka benar-benar menjadi cabang secara struktural.

Sementara dalam diagram kita cenderung melakukan angka secara konseptual, git tidak memiliki konsep sekuens yang stabil ketika pohon komit bercabang. Dalam hal ini Anda dapat menganggap angka-angka (urutan menunjukkan) ditentukan oleh cap waktu (mungkin menyenangkan untuk melihat bagaimana UI git menangani hal-hal ketika Anda mengatur semua cap waktu menjadi sama).

Inilah yang manusia harapkan secara konseptual:

After branch:
       C1 (B1)
      /
    -
      \
       C1 (B2)
After first commit:
       C1 (B1)
      /
    - 
      \
       C1 - C2 (B2)

Inilah yang sebenarnya Anda dapatkan:

After branch:
    - C1 (B1) (B2)
After first commit (human):
    - C1 (B1)
        \
         C2 (B2)
After first commit (real):
    - C1 (B1) - C2 (B2)

Anda akan menganggap B1 sebagai cabang asli tetapi itu bisa benar-benar menjadi cabang mati (seseorang melakukan checkout -b tetapi tidak pernah berkomitmen untuk itu). Baru setelah Anda berkomitmen untuk keduanya Anda mendapatkan struktur cabang yang sah dalam git:

Either:
      / - C2 (B1)
    -- C1
      \ - C3 (B2)
Or:
      / - C3 (B1)
    -- C1
      \ - C2 (B2)

Anda selalu tahu bahwa C1 datang sebelum C2 dan C3 tetapi Anda tidak pernah andal tahu jika C2 datang sebelum C3 atau C3 datang sebelum C2 (karena Anda dapat mengatur waktu di workstation Anda dengan apa pun misalnya). B1 dan B2 juga menyesatkan karena Anda tidak tahu cabang mana yang lebih dulu. Anda dapat membuat tebakan yang sangat bagus dan biasanya akurat dalam banyak kasus. Ini agak seperti trek balap. Semua hal umumnya sama dengan mobil maka Anda dapat mengasumsikan bahwa mobil yang datang di pangkuan mulai pangkuan di belakang. Kami juga memiliki konvensi yang sangat andal, misalnya master akan hampir selalu mewakili cabang yang paling lama hidup meskipun sayangnya saya telah melihat kasus di mana bahkan ini tidak terjadi.

Contoh yang diberikan di sini adalah contoh pelestarian sejarah:

Human:
    - X - A - B - C - D - F (B1)
           \     / \     /
            G - H ----- I - J (B2)
Real:
            B ----- C - D - F (B1)
           /       / \     /
    - X - A       /   \   /
           \     /     \ /
            G - H ----- I - J (B2)

Nyata di sini juga menyesatkan karena kita sebagai manusia membacanya dari kiri ke kanan, root to leaf (ref). Git tidak melakukan itu. Di mana kita melakukan (A-> B) di kepala kita git (A <-B atau B-> A). Bunyinya dari ref ke root. Ac bisa di mana saja tetapi cenderung daun, setidaknya untuk cabang aktif. Seorang ref menunjuk ke sebuah komit dan melakukan hanya berisi suka kepada orang tua mereka, bukan untuk anak-anak mereka. Ketika komit adalah gabungan komit, komit akan memiliki lebih dari satu orangtua. Orang tua pertama selalu merupakan komit asli yang digabungkan. Orang tua lain selalu melakukan komitmen yang digabungkan ke dalam komitmen asli.

Paths:
    F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
    J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))

Ini bukan representasi yang sangat efisien, melainkan ekspresi dari semua jalur yang dapat diambil git dari setiap referensi (B1 dan B2).

Penyimpanan internal Git lebih mirip seperti ini (bukan berarti A sebagai orangtua muncul dua kali):

    F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A

Jika Anda membuang komit git mentah, Anda akan melihat nol atau lebih bidang induk. Jika ada nol, itu berarti tidak ada orang tua dan komit adalah root (Anda sebenarnya dapat memiliki beberapa root). Jika ada satu, itu berarti tidak ada penggabungan dan itu bukan komit root. Jika ada lebih dari satu itu berarti komit adalah hasil penggabungan dan semua orang tua setelah yang pertama adalah gabungan komit.

Paths simplified:
    F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
    F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
    J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
    F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
    F->D->C->B->A | J->I->->G->A | A->X
Topological:
    - X - A - B - C - D - F (B1)
           \
            G - H - I - J (B2)

Ketika keduanya mengenai A rantai mereka akan sama, sebelum itu rantai mereka akan sama sekali berbeda. Komitmen pertama yang dimiliki dua komitmen lainnya adalah leluhur yang sama dan dari mana mereka menyimpang. mungkin ada beberapa kebingungan di sini antara istilah commit, branch, dan ref. Anda sebenarnya bisa menggabungkan komit. Inilah yang sebenarnya dilakukan penggabungan. Seorang ref hanya menunjuk ke sebuah komit dan sebuah cabang tidak lebih dari sebuah ref dalam folder .git / refs / head, lokasi folder adalah yang menentukan bahwa ref adalah cabang daripada sesuatu yang lain seperti sebuah tag.

Di mana Anda kehilangan sejarah adalah penggabungan akan melakukan satu dari dua hal tergantung pada keadaan.

Mempertimbangkan:

      / - B (B1)
    - A
      \ - C (B2)

Dalam hal ini gabungan di kedua arah akan membuat komit baru dengan induk pertama sebagai komit yang ditunjukkan oleh cabang yang dicentang saat ini dan induk kedua sebagai komit di ujung cabang yang Anda gabungkan ke cabang Anda saat ini. Itu harus membuat komitmen baru karena kedua cabang memiliki perubahan sejak nenek moyang mereka yang harus digabungkan.

      / - B - D (B1)
    - A      /
      \ --- C (B2)

Pada titik ini D (B1) sekarang memiliki kedua set perubahan dari kedua cabang (itu sendiri dan B2). Namun cabang kedua tidak memiliki perubahan dari B1. Jika Anda menggabungkan perubahan dari B1 ke B2 sehingga mereka disinkronkan maka Anda mungkin mengharapkan sesuatu yang terlihat seperti ini (Anda dapat memaksa git merge untuk melakukannya seperti ini dengan --no-ff):

Expected:
      / - B - D (B1)
    - A      / \
      \ --- C - E (B2)
Reality:
      / - B - D (B1) (B2)
    - A      /
      \ --- C

Anda akan mendapatkannya meskipun B1 memiliki komitmen tambahan. Selama tidak ada perubahan dalam B2 yang tidak dimiliki B1, kedua cabang akan digabungkan. Itu melakukan fast forward yang seperti rebase (rebases juga memakan atau linearisasi sejarah), kecuali tidak seperti rebase karena hanya satu cabang yang memiliki set perubahan, ia tidak harus menerapkan perubahan dari satu cabang di atas itu dari yang lain.

From:
      / - B - D - E (B1)
    - A      /
      \ --- C (B2)
To:
      / - B - D - E (B1) (B2)
    - A      /
      \ --- C

Jika Anda berhenti bekerja pada B1 maka banyak hal baik untuk menjaga sejarah dalam jangka panjang. Hanya B1 (yang mungkin master) yang akan maju secara khusus sehingga lokasi B2 dalam riwayat B2 berhasil menunjukkan titik bahwa ia digabungkan ke dalam B1. Inilah yang git harapkan Anda lakukan, untuk bercabang B dari A, maka Anda dapat menggabungkan A menjadi B sebanyak yang Anda suka dengan perubahan yang terakumulasi, namun ketika menggabungkan B kembali ke A, tidak diharapkan bahwa Anda akan bekerja pada B dan selanjutnya . Jika Anda terus bekerja pada cabang Anda setelah maju cepat menggabungkannya kembali ke cabang yang Anda kerjakan maka menghapus riwayat B sebelumnya setiap kali. Anda benar-benar membuat cabang baru setiap kali setelah komit maju cepat ke sumber lalu komit ke cabang.

         0   1   2   3   4 (B1)
        /-\ /-\ /-\ /-\ /
    ----   -   -   -   -
        \-/ \-/ \-/ \-/ \
         5   6   7   8   9 (B2)

1 sampai 3 dan 5 hingga 8 adalah cabang struktural yang muncul jika Anda mengikuti sejarah untuk 4 atau 9. Tidak ada cara di git untuk mengetahui mana dari cabang struktural yang tidak disebutkan namanya dan tidak direferensikan milik dengan cabang bernama dan referensi sebagai ujung struktur. Anda mungkin berasumsi dari gambar ini bahwa 0 hingga 4 milik B1 dan 4 hingga 9 milik B2 tetapi selain dari 4 dan 9 tidak dapat mengetahui cabang mana yang dimiliki cabang mana, saya hanya menggambarnya dengan cara yang memberikan ilusi itu. 0 mungkin milik B2 dan 5 mungkin milik B1. Ada 16 kemungkinan yang berbeda dalam hal ini yang dinamai cabang masing-masing cabang struktural milik.

Ada sejumlah strategi git yang mengatasi hal ini. Anda dapat memaksa git merge untuk tidak pernah maju cepat dan selalu membuat cabang gabungan. Cara mengerikan untuk melestarikan sejarah cabang adalah dengan tag dan / atau cabang (tag sangat direkomendasikan) menurut beberapa konvensi yang Anda pilih. Saya benar-benar tidak akan merekomendasikan komit kosong boneka di cabang tempat Anda bergabung. Konvensi yang sangat umum adalah untuk tidak bergabung ke cabang integrasi sampai Anda ingin benar-benar menutup cabang Anda. Ini adalah praktik yang harus dipatuhi oleh orang-orang karena Anda sedang berusaha untuk memiliki cabang. Namun di dunia nyata ideal tidak selalu berarti praktis melakukan hal yang benar tidak layak untuk setiap situasi. Jika apa yang Anda

jgmjgm
sumber
"Git tidak menyimpan sejarah referensi" Ya, tetapi tidak secara default dan tidak untuk waktu yang lama. Lihat man git-reflogdan bagian tentang tanggal: "master@{one.week.ago} berarti" di mana master menunjuk satu minggu yang lalu di repositori lokal ini "". Atau diskusi di <refname>@{<date>}dalam man gitrevisions. Dan core.reflogExpiredi man git-config.
Patrick Mevzek
2

Tidak cukup solusi untuk pertanyaan itu tetapi saya pikir itu layak untuk mencatat pendekatan yang saya gunakan ketika saya memiliki cabang yang tahan lama:

Pada saat yang sama saya membuat cabang, saya juga membuat tag dengan nama yang sama tetapi dengan -initakhiran, misalnya feature-branchdanfeature-branch-init .

(Agak aneh bahwa ini adalah pertanyaan yang sulit dijawab!)

Stephen Darlington
sumber
1
Mempertimbangkan kebodohan yang sangat membingungkan dalam merancang konsep "cabang" tanpa gagasan tentang Kapan dan di mana ia diciptakan ... ditambah kerumitan luar biasa dari solusi yang disarankan lainnya - oleh orang-orang yang mencoba melakukan kecerdasan dengan cara ini-terlalu-pintar hal, saya pikir saya lebih suka solusi Anda. Hanya membebani seseorang dengan kebutuhan INGAT untuk melakukan ini setiap kali Anda membuat cabang - hal yang sering dilakukan pengguna git. Selain itu - saya membaca di suatu tempat bahwa 'tag memiliki penalti karena' berat '. Tetap saja, saya pikir itulah yang saya pikir akan saya lakukan.
Motti Shneor
1

Cara sederhana untuk membuatnya lebih mudah untuk melihat titik percabangan git log --graphadalah dengan menggunakan opsi--first-parent .

Misalnya, ambil repo dari jawaban yang diterima :

$ git log --all --oneline --decorate --graph

*   a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
|\  
| *   648ca35 (origin/topic) merging master onto topic
| |\  
| * | 132ee2a first commit on topic branch
* | | e7c863d commit on master after master was merged to topic
| |/  
|/|   
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

Sekarang tambahkan --first-parent:

$ git log --all --oneline --decorate --graph --first-parent

* a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
| * 648ca35 (origin/topic) merging master onto topic
| * 132ee2a first commit on topic branch
* | e7c863d commit on master after master was merged to topic
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

Itu membuatnya lebih mudah!

Perhatikan jika repo memiliki banyak cabang yang ingin Anda tentukan 2 cabang yang Anda bandingkan daripada menggunakan --all:

$ git log --decorate --oneline --graph --first-parent master origin/topic
binaryfunt
sumber
0

Masalahnya adalah menemukan potongan terbaru, komit tunggal antara kedua cabang di satu sisi, dan yang paling awal leluhur bersama yang di sisi lain (mungkin komit awal repo). Ini sesuai dengan intuisi saya tentang titik "percabangan" itu.

Yang perlu diingat, ini sama sekali tidak mudah untuk dihitung dengan perintah normal git shell, karena git rev-list- alat kami yang paling kuat - tidak membiarkan kami membatasi jalur yang digunakan untuk mencapai komit. Yang paling dekat yang kita miliki adalah git rev-list --boundary, yang dapat memberi kita satu set semua komitmen yang "menghalangi jalan kita". (Catatan:git rev-list --ancestry-path menarik tetapi saya tidak tahu bagaimana membuatnya berguna di sini.)

Ini skripnya: https://gist.github.com/abortz/d464c88923c520b79e3d . Ini relatif sederhana, tetapi karena satu lingkaran itu cukup rumit untuk menjamin inti.

Perhatikan bahwa sebagian besar solusi lain yang diusulkan di sini tidak mungkin dapat berfungsi di semua situasi karena alasan sederhana: git rev-list --first-parenttidak dapat diandalkan dalam membuat linierisasi sejarah karena mungkin ada penggabungan dengan salah satu pemesanan.

git rev-list --topo-order, di sisi lain, sangat berguna - untuk komit berjalan dalam urutan topografi - tetapi melakukan diff adalah rapuh: ada beberapa urutan topografi yang mungkin untuk grafik yang diberikan, jadi Anda bergantung pada stabilitas tertentu dari urutan tersebut. Yang mengatakan, solusi strongk7 mungkin bekerja dengan sangat baik sebagian besar waktu. Namun lebih lambat milikku karena harus berjalan sepanjang sejarah repo ... dua kali. :-)

Andrew Bortz
sumber
0

Berikut ini mengimplementasikan git yang setara dengan svn log --stop-on-copy dan juga dapat digunakan untuk menemukan asal cabang.

Pendekatan

  1. Dapatkan kepala untuk semua cabang
  2. kumpulkan mergeBase untuk cabang sasaran masing-masing cabang lainnya
  3. git.log dan iterate
  4. Berhenti komit pertama yang muncul di daftar mergeBase

Seperti semua sungai mengalir ke laut, semua cabang berlari untuk menguasai dan oleh karena itu kami menemukan gabungan antara cabang-cabang yang tampaknya tidak berhubungan. Ketika kita berjalan kembali dari kepala cabang melalui leluhur, kita dapat berhenti di pangkalan gabungan potensial pertama karena secara teori itu harus menjadi titik asal cabang ini.

Catatan

  • Saya belum mencoba pendekatan ini di mana cabang saudara dan sepupu bergabung satu sama lain.
  • Saya tahu pasti ada solusi yang lebih baik.

detail: https://stackoverflow.com/a/35353202/9950

Peter Kahn
sumber
-1

Anda bisa menggunakan perintah berikut untuk mengembalikan komit tertua di branch_a, yang tidak dapat dijangkau dari master:

git rev-list branch_a ^master | tail -1

Mungkin dengan pemeriksaan kewarasan tambahan bahwa orang tua dari komit tersebut sebenarnya dapat dijangkau dari master ...

Marc Richarme
sumber
1
Ini tidak berhasil. Jika branch_a digabung menjadi master sekali, dan kemudian melanjutkan, komit pada gabungan itu akan dianggap sebagai bagian dari master, sehingga mereka tidak akan muncul di master ^.
FelipeC
-2

Anda dapat memeriksa reflog cabang A untuk menemukan komit mana yang diciptakannya, serta sejarah lengkap yang dilakukan oleh cabang tersebut. Reflog ada di .git/logs.

Paggas
sumber
2
Saya tidak berpikir ini bekerja secara umum karena reflog dapat dipangkas. Dan saya tidak berpikir (?) Reflog didorong, jadi ini hanya akan bekerja dalam situasi repo tunggal.
GaryO
-2

Saya percaya saya telah menemukan cara yang berhubungan dengan semua kasus sudut yang disebutkan di sini:

branch=branch_A
merge=$(git rev-list --min-parents=2 --grep="Merge.*$branch" --all | tail -1)
git merge-base $merge^1 $merge^2

Charles Bailey cukup benar bahwa solusi berdasarkan urutan leluhur hanya memiliki nilai terbatas; pada akhir hari Anda membutuhkan semacam catatan "komit ini berasal dari cabang X", tetapi catatan tersebut sudah ada; secara default 'git merge' akan menggunakan pesan komit seperti "Gabung cabang 'branch_A' menjadi master", ini memberitahu Anda bahwa semua komit dari induk kedua (komit ^ 2) berasal dari 'branch_A' dan digabungkan ke yang pertama parent (commit ^ 1), yang merupakan 'master'.

Berbekal informasi ini, Anda dapat menemukan gabungan pertama 'branch_A' (saat itulah 'branch_A' benar-benar ada), dan menemukan basis-gabungan, yang akan menjadi titik cabang :)

Saya sudah mencoba dengan repositori Mark Booth dan Charles Bailey dan solusinya berhasil; bagaimana tidak bisa? Satu-satunya cara ini tidak akan berhasil adalah jika Anda telah secara manual mengubah pesan komit default untuk penggabungan sehingga informasi cabang benar-benar hilang.

Untuk kegunaan:

[alias]
    branch-point = !sh -c 'merge=$(git rev-list --min-parents=2 --grep="Merge.*$1" --all | tail -1) && git merge-base $merge^1 $merge^2'

Maka kamu bisa melakukan 'git branch-point branch_A '.

Nikmati ;)

FelipeC
sumber
4
Mengandalkan pesan gabungan lebih rapuh daripada membuat hipotesis tentang pesanan orang tua. Ini bukan hanya situasi hipotetis; Saya sering menggunakan git merge -muntuk mengatakan apa yang telah saya gabungkan daripada nama cabang yang berpotensi singkat (mis. "Gabungkan perubahan arus utama menjadi fitur xyz refactor"). Misalkan saya kurang membantu dengan -mcontoh saya? Masalahnya sama sekali tidak larut secara umum karena saya dapat membuat sejarah yang sama dengan satu atau dua cabang sementara dan tidak ada cara untuk membedakannya.
CB Bailey
1
@CharlesBailey Itu masalah Anda saat itu. Anda tidak boleh menghapus baris-baris itu dari pesan komit, Anda harus menambahkan sisa pesan di bawah yang asli. Saat ini 'git merge' secara otomatis membuka editor untuk Anda tambahkan apa pun yang Anda inginkan, dan untuk versi git lama Anda dapat melakukan 'git merge --edit'. Either way, Anda dapat menggunakan komit kait untuk menambahkan "Berkomitmen pada cabang 'foo'" untuk masing-masing dan setiap komit jika itu yang Anda inginkan. Namun, solusi ini bekerja untuk kebanyakan orang .
FelipeC
2
Tidak bekerja untuk saya. Branch_A dicabut dari master yang sudah memiliki banyak penggabungan. Logika ini tidak memberi saya hash komit yang tepat di mana branch_A dibuat.
Venkat Kotra