Bagaimana saya bisa tahu jika satu komit adalah turunan dari komit lain?

146

Dengan Git, bagaimana saya bisa tahu jika satu komit di cabang saya adalah keturunan dari komit lain?

engie
sumber
2
Pertanyaan yang sama menanyakan yang sebaliknya: stackoverflow.com/questions/18345157/…
Chris Cleeland
11
Bisakah Anda mengubah jawaban yang Anda terima? Mayoritas menyukai --is-ancestorsolusinya.
Robert Siemer

Jawaban:

51

Jika Anda ingin memeriksa ini secara programatik (misalnya dalam skrip), Anda dapat memeriksa apakah git merge-base A Bsama dengan git rev-parse --verify A(maka A dapat dijangkau dari B), atau jika itu git rev-parse --verify B(maka B dapat dijangkau dari A). git rev-parsedi sini diperlukan untuk mengkonversi dari nama komit ke komit SHA-1 / commit id.

Menggunakan git rev-listlike dalam jawaban VonC juga dimungkinkan.

Sunting: di Git modern ada dukungan eksplisit untuk kueri ini dalam bentuk git merge-base --is-ancestor.


Jika salah satu dari komitmen yang Anda tanyakan adalah tip cabang , maka git branch --contains <commit>atau git branch --merged <commit>mungkin solusi non-programatik yang lebih baik.

Jakub Narębski
sumber
1
Mungkin cara tercepat adalah dengan git checkout -b quickcheck <more-recent-commit-ID>kemudian git branch --contains <older-commit-ID>(dan kemudian git branch -D quickcheckmenyingkirkan cabang sementara).
clee
2
Dua pendekatan yang mungkin, keduanya jauh lebih buruk daripada yang ada di jawaban @ MattR.
jwg
6
@ jwg: Jawaban MattR lebih baik, tetapi jawaban ini (dan mungkin diterima) mendahului git 1.8.0 dan git merge-base --is-ancestor2 tahun.
Jakub Narębski
@ JakubNarębski Cukup adil, maaf.
jwg
2
Dalam repositori besar (2 juta komit), saya membandingkan kecepatan git branch --contains <commit>dan git merge-base --is-ancestor ...: 3m40s vs 0,14s
hagello
259

Dari Git 1.8.0, ini didukung sebagai opsi untuk merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

Dari halaman manual:

--adalah leluhur

Periksa apakah yang pertama adalah leluhur dari yang kedua, dan keluar dengan status 0 jika benar, atau dengan status 1 jika tidak. Kesalahan ditandai oleh status bukan nol yang bukan 1.

Sebagai contoh:

git merge-base --is-ancestor origin/master master; echo $?
Matt R
sumber
4
Bagus! Berikut ini adalah skrip shell yang membungkus jawaban ini dalam sesuatu dengan keluaran yang dapat diterima manusia: gist.github.com/simonwhitaker/6354592
Simon Whitaker
1
Dengan kata lain: git merge-base THING --is-ancestor OF_THING && echo yes || echo nomisalnya:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594
2
@smarber git merge-base --is-ancestor -- commit commitbekerja untuk hash di sisi saya dengan git2.1.4 (Debian / Devuan 7.10 jessie) dan 1.9.1 (Ubuntu 14.04 terpercaya) yang agak kuno sekarang. Ini bekerja bahkan untuk Wheezy Debian, jika Anda melakukannya sudo apt-get install git/wheezy-backports.
Tino
15

Operasi semacam ini bergantung pada gagasan tentang berbagai revisi yang dirinci dalam pertanyaan SO: " Perbedaan dalam 'asal git log / master' vs 'asal git log / master ..' ".

git rev-list harus dapat berjalan kembali dari komit, hingga yang lain jika dapat dijangkau.

Jadi saya akan mencoba:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Komit batas diawali dengan -)

Jika komit terakhir yang ditampilkan adalah sama dengan komit pertama dalam git rev-listperintah, maka komit tersebut dapat dicapai dari komit kedua.

Jika komit pertama tidak dapat dijangkau dari komit kedua, git rev-listseharusnya tidak menghasilkan apa-apa.

git rev-list --boundary A..B

akan selesai A, jika Adapat dijangkau dari B.
Itu sama dengan:

git rev-list --boundary B --not A

, dengan Bsebuah referensi positif , dan Asebuah referensi negatif .
Ini akan dimulai pada Bdan berjalan kembali melalui grafik sampai bertemu dengan revisi yang dapat dicapai A.
Saya berpendapat bahwa jika Alangsung dapat dijangkau dari B, itu akan menemui (dan menampilkan, karena --boundaryopsi) Aitu sendiri.

VONC
sumber
Ini kedengarannya seperti kasus penggunaan yang cukup umum sehingga saya terkejut bahwa git belum menerbitkan perintah "porselen" yang melakukan hal ini.
Lawrence I. Siden
1
@liden: benar. Catatan-samping: jangan lupa bahwa untuk memeriksa sesuatu secara terprogram , Anda tidak seharusnya menggunakan perintah porselen, (seperti pada stackoverflow.com/questions/6976473/… ), tetapi perintah pipa ledeng (seperti yang diilustrasikan dalam stackoverflow.com/questions / 3878624 / ... )
VonC
Oh, kawan, sepertinya aku harus kembali dan mengerjakan skrip skrip Shellku!
Lawrence I. Siden
1
pertanyaan: mengapa -85e54e2...dalam cuplikan memiliki minus? juga kemungkinan salah ketik: "... sama dengan komit pertama ..."
sdaau
1
@ Sdaau -berarti komit batas. Saya telah mengedit jawaban untuk membuatnya lebih jelas, juga untuk menyegarkan tautan doc dan memperbaiki kesalahan ketik untuk jawaban berusia 5 tahun ini.
VonC
11

Cara lain adalah menggunakan git logdan grep.

git log --pretty=format:%H abc123 | grep def456

Ini akan menghasilkan satu baris output jika komit def456 adalah leluhur komit abc123, atau tidak ada output sebaliknya.

Anda biasanya bisa lolos dengan menghilangkan --prettyargumen, tetapi diperlukan jika Anda ingin memastikan bahwa Anda hanya mencari melalui hash yang sebenarnya dan tidak melalui komentar log dan sebagainya.

itub
sumber
Saya berharap solusi ini lambat, tetapi sebenarnya cukup cepat, bahkan untuk proyek dengan komitmen 20k +
Renato Zannon
2
alih-alih --prettysaya menggunakan --oneline: git log --oneline ce2ee3d | grep ec219ccworks great
gens
3

https://stackoverflow.com/a/13526591/895245 menyebutkannya, sekarang untuk membuatnya lebih ramah manusia:

git-is-ancestor() (
  if git merge-base --is-ancestor "$1" "$2"; then
      echo 'ancestor'
  elif git merge-base --is-ancestor "$2" "$1"; then
      echo 'descendant'
  else
      echo 'unrelated'
  fi
)
alias giia='git-is-ancestor'
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
ini akan mengembalikan 'tidak terkait' jika kedua parameter menunjuk ke komit yang sama
mik
1

git show-branch branch-sha1 commit-sha1

Dimana:

  • branch-sha1: sha1 di cabang yang ingin Anda periksa
  • commit-sha1: sha1 dari commit yang ingin Anda periksa
viq
sumber
0

Jika Anda menggunakan git merge-base --is-ancestor, pastikan untuk menggunakan Git 2.28 (Q3 2020)

Dengan Git 2.28 (Q3 2020), beberapa bidang " struct commit" yang tidak harus selalu ada telah dipindahkan untuk melakukan slab.

Lihat komit c752ad0 , komit c49c82a , komit 4844812 , komit 6da43d9 (17 Jun 2020) oleh Abhishek Kumar ( abhishekkumar2718) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit d80bea4 , 06 Jul 2020)

commit-graph: perkenalkan commit_graph_data_slab

Ditandatangani oleh: Abhishek Kumar

Komit struct digunakan dalam banyak konteks. Namun, anggota generationdan graph_poshanya digunakan untuk operasi terkait grafik-komitmen dan sebaliknya membuang memori.

Pemborosan ini akan lebih jelas ketika kita beralih ke nomor generasi v2, yang menggunakan nomor generasi 64-bit sebagai ganti 32-bit saat ini.

Karena mereka sering diakses bersama, mari kita perkenalkan struct commit_graph_datadan pindahkan ke commit_graph_dataslab.

Sementara keseluruhan test suite berjalan secepat master, (seri: 26m48s,: master27m34s, lebih cepat 2,87%), perintah-perintah tertentu seperti git merge-base --is-ancestordiperlambat oleh 40% seperti yang ditemukan oleh Szeder Gábor .
Setelah meminimalkan akses commit-slab, perlambatan tetap ada tetapi mendekati 20%.

Derrick Stolee percaya perlambatan ini disebabkan oleh algoritma yang mendasari daripada lambatnya akses commit-slab dan kami akan menindaklanjuti dalam seri selanjutnya.

VONC
sumber
-1

Membangun jawaban itub, jika Anda perlu melakukan ini untuk semua tag di repositori:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
ocroquette
sumber