Menemukan cabang apa yang dilakukan oleh Git

650

Apakah ada cara untuk mengetahui dari mana sebuah commit berasal dari nilai hash SHA-1-nya ?

Poin bonus jika Anda dapat memberi tahu saya cara melakukannya menggunakan Ruby Grit.

Ethan Gunderson
sumber
4
Metode yang berbeda di bawah ini adalah pragmatis, berguna, cara untuk bekerja menyimpulkan suatu kemungkinan jawaban, tapi mari kita perhatikan bahwa di git pertanyaan itu sendiri adalah kesalahpahaman, komit tidak datang dari cabang. Cabang datang dan pergi, mereka bergerak, hanya berkomitmen mewakili sejarah repo yang sebenarnya. Kemudian lagi, ini bukan cara untuk mengatakan solusi di bawah ini buruk. Ketahuilah bahwa tidak ada dari mereka yang memberikan jawaban yang sepenuhnya dapat dipercaya, yang tidak dapat diperoleh dengan desain di git. (Kasus sederhana dihapus cabang: Saya bercabang, saya komit dua kali, saya menggabungkan ke cabang lain, saya menghapus cabang pertama. Dari mana komit "berasal"?
RomainValeri
1
Saya memiliki kasus di mana saya secara eksplisit menarik kedalaman 1 clone dangkal dari tag. Ini murah dan mudah dan efisien ... dan sampai sekarang melakukan semua yang saya inginkan dengan indah. Sekarang saya ingin tahu di cabang mana tag itu berada, saya disembunyikan, setidaknya untuk detail itu. Anda tidak bisa selalu pulang, lol
Paul Hodges

Jawaban:

865

Walaupun Dav benar bahwa informasi itu tidak disimpan secara langsung, itu tidak berarti Anda tidak akan pernah bisa mengetahuinya. Berikut ini beberapa hal yang dapat Anda lakukan.

Temukan cabang tempat komit aktif

git branch -a --contains <commit>

Ini akan memberi tahu Anda semua cabang yang memiliki komit yang diberikan dalam sejarahnya. Jelas ini kurang berguna jika komit sudah digabung.

Cari reflog

Jika Anda bekerja di repositori tempat komit dibuat, Anda bisa mencari reflog untuk baris komit itu. Reflog yang lebih dari 90 hari dipangkas oleh git-gc, jadi jika komitnya terlalu tua, Anda tidak akan menemukannya. Yang mengatakan, Anda bisa melakukan ini:

git reflog show --all | grep a871742

untuk menemukan komit a871742. Perhatikan bahwa Anda HARUS menggunakan 7 digit pertama komit. Outputnya harus seperti ini:

a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite

menunjukkan bahwa komit dibuat pada cabang "selesai". Output default menunjukkan hash komit yang disingkat, jadi pastikan untuk tidak mencari hash lengkap atau Anda tidak akan menemukan apa pun.

git reflog showsebenarnya hanya alias untuk git log -g --abbrev-commit --pretty=oneline, jadi jika Anda ingin mengutak-atik format output untuk membuat hal-hal yang berbeda tersedia, itulah titik awal Anda!

Jika Anda tidak bekerja di repositori tempat komit dibuat, hal terbaik yang dapat Anda lakukan dalam hal ini adalah memeriksa reflog dan temukan kapan komit pertama kali diperkenalkan ke repositori Anda; dengan sedikit keberuntungan, Anda mengambil cabang yang telah dijanjikannya. Ini sedikit lebih rumit, karena Anda tidak dapat berjalan baik di pohon komit maupun reflog secara bersamaan. Anda ingin mem-parsing keluaran reflog, memeriksa setiap hash untuk melihat apakah itu berisi komit yang diinginkan atau tidak.

Temukan komit gabungan selanjutnya

Ini tergantung alur kerja, tetapi dengan alur kerja yang baik, komit dibuat pada cabang pengembangan yang kemudian digabungkan. Anda bisa melakukan ini:

git log --merges <commit>..

untuk melihat menggabungkan komit yang memiliki komit yang diberikan sebagai leluhur. (Jika komit hanya digabung satu kali, komit pertama yang harus Anda gabungkan; jika tidak, Anda harus memeriksa beberapa, saya kira.) Pesan komit gabung harus berisi nama cabang yang digabungkan.

Jika Anda ingin dapat mengandalkan melakukan ini, Anda mungkin ingin menggunakan --no-ffopsi git mergeuntuk memaksa menggabungkan penciptaan komit bahkan dalam kasus fast-forward. (Namun, jangan terlalu bersemangat. Itu bisa menjadi membingungkan jika terlalu sering digunakan.) Jawaban VonC untuk pertanyaan terkait sangat membantu menguraikan topik ini.

Cascabel
sumber
5
+1. Tidak adanya informasi untuk masalah khusus itu membuat Anda bertanya-tanya apakah itu sebenarnya masalah? Cabang dapat berubah, diubah namanya atau dihapus kapan saja. Mungkin git describesudah cukup, karena tag (beranotasi) dapat dilihat lebih penting daripada cabang.
VonC
9
Saya setuju - cabang dimaksudkan untuk menjadi ringan dan fleksibel. Jika Anda mengadopsi alur kerja di mana informasi itu menjadi penting, Anda bisa menggunakan --no-ffopsi untuk memastikan selalu ada komit gabungan, jadi Anda selalu bisa melacak jalur komit yang diberikan saat digabungkan ke master.
Cascabel
32
FYI, jika Anda ingin menemukan komit yang hanya ada di remote, tambahkan -abendera ke perintah pertama misalnyagit branch -a --contains <commit>
Jonathan Day
8
@ JonathanDay: Tidak, itu akan menemukan commit di cabang mana pun. Jika Anda hanya ingin yang di remote, gunakan -r.
Cascabel
7
@SimonTewsi Seperti yang saya katakan, jika ini benar-benar masalah, gunakan merge --no-ffuntuk merekam nama cabang dengan andal saat Anda bergabung. Tetapi sebaliknya, anggaplah nama cabang sebagai label pendek sementara, dan lakukan deskripsi sebagai yang permanen. "Nama pendek apa yang kita sebutkan dengan ini selama pengembangan?" seharusnya tidak menjadi pertanyaan yang sama pentingnya dengan "apa yang dilakukan oleh komitmen ini?"
Cascabel
155

Perintah sederhana ini bekerja seperti mantra:

git name-rev <SHA>

Misalnya (di mana cabang uji adalah nama cabang):

git name-rev 651ad3a
251ad3a remotes/origin/test-branch

Bahkan ini berfungsi untuk skenario kompleks, seperti:

origin/branchA/
              /branchB
                      /commit<SHA1>
                                   /commit<SHA2>

Di sini git name-rev commit<SHA2>mengembalikan branchB .

khichar.anil
sumber
3
Kamu menyelamatkan hariku. Ini solusi sempurna.
Taco
7
Dan hanya butuh 8 tahun untuk solusi sempurna ini. Terima kasih!
S1J0
6
Saya menemukan git name-rev --name-only <SHA>lebih bermanfaat untuk mendapatkan hanya nama cabang. Pertanyaan saya ... Apakah bisa mengembalikan lebih dari satu cabang dalam keadaan apa pun?
Dean Kayton
1
Ini harus menjadi jawaban terbaik.
JCollier
1
Terima kasih @JCollier atas kata-kata baik Anda.
khichar.anil
47

Pembaruan Desember 2013:

komentar sschuberth

git-what-branch(Perl script, lihat di bawah) tampaknya tidak dipertahankan lagi. git-when-mergedadalah alternatif yang ditulis dengan Python yang bekerja sangat baik untuk saya.

Ini didasarkan pada " Temukan komit gabungan yang menyertakan komit tertentu ".

git when-merged [OPTIONS] COMMIT [BRANCH...]

Temukan ketika komit digabung menjadi satu atau beberapa cabang.
Temukan komit gabungan yang dibawa COMMITke dalam CABANG tertentu (es).

Secara khusus, cari komit tertua pada sejarah orang tua pertama BRANCHyang berisi COMMITleluhur.


Jawaban asli September 2010:

Sebastien Douche baru saja twit (16 menit sebelum jawaban SO ini):

git-what-branch : Temukan cabang apa yang digunakan komit, atau bagaimana ia sampai ke cabang bernama

Ini adalah skrip Perl dari Seth Robertson yang tampaknya sangat menarik:

RINGKASAN

git-what-branch [--allref] [--all] [--topo-order | --date-order ]
[--quiet] [--reference-branch=branchname] [--reference=reference]
<commit-hash/tag>...

GAMBARAN

Beri tahu kami (secara default) jalur kausal awal dari commit dan merger untuk menyebabkan komit yang diminta masuk ke cabang bernama. Jika komit dibuat langsung pada cabang bernama, itu jelas merupakan jalur paling awal.

Dengan jalur sebab akibat paling awal, yang kami maksud adalah jalur yang bergabung ke cabang bernama paling awal, dengan waktu komit (kecuali --topo-orderditentukan).

KINERJA

Jika banyak cabang (mis. Ratusan) berisi komit, sistem mungkin membutuhkan waktu lama (untuk komit tertentu di pohon Linux, butuh 8 detik untuk menjelajahi cabang, tetapi ada lebih dari 200 cabang kandidat) untuk melacak jalur. untuk setiap komit.
Pemilihan tertentu --reference-branch --reference taguntuk diperiksa akan ratusan kali lebih cepat (jika Anda memiliki ratusan kandidat cabang).

CONTOH

 # git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:
   v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May  5 08:59:37 2005)
   v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May  3 18:27:24 2005)
   v2.6.12-rc3-461-g84e48b6 is on master
   v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
   [...]

Program ini tidak memperhitungkan efek memetik bunga, hanya menggabungkan operasi.

VONC
sumber
4
git-what-branchsepertinya tidak dipertahankan lagi. git-when-merger adalah alternatif yang ditulis dengan Python yang bekerja sangat baik untuk saya.
sschuberth
@ schuberthth terima kasih atas pembaruan ini. Saya telah memasukkan komentar Anda dalam jawaban untuk lebih banyak visibilitas.
VonC
37

Misalnya, untuk mengetahui bahwa c0118fakomit berasal dari redesign_interactions:

* ccfd449 (HEAD -> develop) Require to return undef if no digits found
*   93dd5ff Merge pull request #4 from KES777/clean_api
|\
| * 39d82d1 Fix tc0118faests for debugging debugger internals
| * ed67179 Move &push_frame out of core
| * 2fd84b5 Do not lose info about call point
| * 3ab09a2 Improve debugger output: Show info about emitted events
| *   a435005 Merge branch 'redesign_interactions' into clean_api
| |\
| | * a06cc29 Code comments
| | * d5d6266 Remove copy/paste code
| | * c0118fa Allow command to choose how continue interaction
| | * 19cb534 Emit &interact event

Anda harus menjalankan:

git log c0118fa..HEAD --ancestry-path --merges

Dan gulir ke bawah untuk menemukan komit gabungan terakhir . Yang mana:

commit a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date:   Sat Oct 1 00:54:18 2016 +0300

    Merge branch 'redesign_interactions' into clean_api

Memperbarui

Atau hanya satu perintah:

git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1
Eugen Konkov
sumber
4
Ini adalah satu-satunya solusi yang memberi saya hasil yang diinginkan. Saya mencoba sisanya.
perajin
Catatan ini hanya berfungsi jika ringkasan komit gabungan berisi nama cabang yang digunakan dalam gabungan, yang biasanya mereka lakukan. Namun git merge -m"Any String Here"akan mengaburkan sumber dan informasi cabang target.
qneill
@qneill: Tidak, penggabungan selalu memiliki info tentang cabang digabung: Merge: f6b70fa d58bdcb. Anda dapat memberi nama komit gabungan Anda dengan Anda. Saya tidak akan memiliki masalah
Eugen Konkov
1
@EugenKonkov begitu cabang-cabang telah melewati penggabungan, sejarah tentang jalur mana dalam grafik tempat asalnya ditinggalkan di reflog, tetapi tidak dalam database objek git. Saya memposting jawaban yang mencoba menjelaskan apa yang saya katakan
qneill
Versi UPD bekerja untuk saya, perintah sederhana yang menemukan komit asli
vpalmu
9

git branch --contains <ref>adalah perintah "porselen" yang paling jelas untuk melakukan ini. Jika Anda ingin melakukan sesuatu yang serupa dengan hanya perintah "plumbing":

COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
  if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
    echo $BRANCH
  fi
done

(crosspost dari jawaban SO ini )

Jeff Bowman
sumber
6

khichar.anil membahas sebagian besar ini dalam jawabannya.

Saya hanya menambahkan bendera yang akan menghapus tag dari daftar nama revisi. Ini memberi kita:

git name-rev --name-only --exclude=tags/* $SHA
phyatt
sumber
Sesuatu sepertinya hilang dalam kalimat kedua.
Peter Mortensen
Saya memperbarui deskripsi. Terima kasih atas umpan baliknya.
phyatt
4

Pilihan orang miskin adalah menggunakan alat tig1 pada HEAD, mencari komit, dan kemudian secara visual mengikuti garis dari komit itu kembali sampai komit gabungan terlihat. Pesan penggabungan default harus menentukan cabang apa yang digabungkan ke tempat :)

1 Tig adalah antarmuka mode teks berbasis ncurses untuk Git. Berfungsi terutama sebagai browser repositori Git, tetapi juga dapat membantu dalam melakukan perubahan untuk komit di tingkat chunk dan bertindak sebagai pager untuk output dari berbagai perintah Git.

pengguna1338062
sumber
3

Sebagai percobaan, saya membuat kait pasca-komit yang menyimpan informasi tentang cabang yang saat ini diperiksa dalam metadata komit. Saya juga sedikit memodifikasi gitk untuk menunjukkan informasi itu.

Anda dapat memeriksanya di sini: https://github.com/pajp/branch-info-commits

pajp
sumber
1
Tetapi jika Anda ingin menambahkan metadata, mengapa tidak menggunakan git note, alih-alih mengutak-atik komit? Lihat stackoverflow.com/questions/5212957/… , stackoverflow.com/questions/7298749/… atau stackoverflow.com/questions/7101158/…
VonC
Sejujurnya, saya tidak tahu ada catatan git ketika saya menulis itu. Ya, menggunakan git note untuk mencapai hal yang sama mungkin merupakan ide yang lebih baik.
pajp
3

Saya berurusan dengan masalah yang sama ( pipa multibranch Jenkins ) - hanya memiliki komit informasi dan mencoba mencari nama cabang dari mana komit ini berasal. Ini harus berfungsi untuk cabang jarak jauh, salinan lokal tidak tersedia.

Inilah yang saya kerjakan:

git rev-parse HEAD | xargs git name-rev

Secara opsional, Anda dapat menghapus output:

git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'
miro
sumber
0

Saya pikir seseorang harus menghadapi masalah yang sama yang tidak dapat menemukan cabang, meskipun sebenarnya ada di satu cabang.

Anda sebaiknya menarik semua terlebih dahulu:

git pull --all

Kemudian lakukan pencarian cabang:

git name-rev <SHA>

atau:

git branch --contains <SHA>
Yates Zhou
sumber
0

Jika OP sedang mencoba untuk menentukan sejarah yang dilalui oleh cabang ketika komit tertentu dibuat ("cari tahu dari mana cabang komit berasal dari diberi nilai hash SHA-1"), maka tanpa reflog tidak ada catatan dalam database objek Git yang menunjukkan apa yang bernama cabang terikat dengan apa yang melakukan histori.

(Saya memposting ini sebagai jawaban sebagai balasan atas komentar.)

Semoga skrip ini mengilustrasikan poin saya:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email [email protected]
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git checkout b1; touch f1; git add f1; git commit -m"Add f1"
git checkout b2; touch f2; git add f2; git commit -m"Add f2"
git merge -m"merge branches" b1; git checkout b1; git merge b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -x;
cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;

Output menunjukkan kurangnya cara untuk mengetahui apakah komit dengan 'Add f1' berasal dari cabang b1 atau b2 dari remote clone / tmp / r2.

(Baris terakhir dari output di sini)

+ cd /tmp/r1
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: merge b2: Fast-forward
086c9ce b1@{1}: commit: Add f1
18feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b1
18feb84 b1@{3}: commit (initial): empty
+ git reflog b2
f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.
80c10e5 b2@{1}: commit: Add f2
18feb84 b2@{2}: branch: Created from b1
+ cd /tmp/r2
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: clone: from /tmp/r1
+ git reflog b2
f0c707d b2@{0}: fetch origin b2:b2: storing head
Qneill
sumber
Dan untuk apa output git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1dan git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1perintah untuk kedua kasus?
Eugen Konkov
SHA tentu saja berubah sewaktu-waktu perintah dijalankan, untuk membalas perintah Anda, saya menggunakan HEAD ^ 1 dan HEAD ^ 2 pada proses baru. Perintah dan outputnya adalah: $ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1yang menghasilkan 376142d merge branchesdan $ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1yang menghasilkan 376142d merge branches - yang menunjukkan ringkasan komit gabung, yang (seperti yang saya nyatakan) dapat ditimpa ketika penggabungan dibuat, mungkin mengaburkan sejarah cabang penggabungan.
qneill
0

TL; DR:

Gunakan di bawah ini jika Anda peduli tentang status keluar shell:

  • branch-current - nama cabang saat ini
  • branch-names - nama cabang bersih (satu per baris)
  • branch-name - Pastikan hanya satu cabang yang dikembalikan branch-names

Keduanya branch-namedan branch-namesmenerima komit sebagai argumen, dan default ke HEADjika tidak ada yang diberikan.


Alias ​​berguna dalam skrip

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461
branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"

Komit hanya dapat dicapai dari satu cabang saja

% git branch-name eae13ea
master
% echo $?
0
  • Output adalah ke STDOUT
  • Nilai keluar adalah 0.

Berkomitmen terjangkau dari banyak cabang

% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
  • Outputnya ke STDERR
  • Nilai keluar adalah 1 .

Karena status keluar, ini dapat dibangun dengan aman. Misalnya, untuk membuat remote digunakan untuk mengambil:

remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"
Tom Hale
sumber
-1

Untuk menemukan cabang lokal:

grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'

Untuk menemukan cabang jarak jauh:

grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'
Gary Lyn
sumber
1
beberapa penjelasan dari contoh Anda akan sangat bagus
Eugen Konkov
-4

Selain mencari melalui semua pohon sampai Anda menemukan hash yang cocok, tidak.

Amber
sumber
7
Saya tidak benar-benar ingin membatalkan ini, karena sebenarnya memang benar bahwa git tidak secara permanen menyimpan informasi itu, tetapi sangat mungkin untuk mencari tahu. Lihat jawaban saya.
Cascabel