Diberikan sebuah cabang, saya ingin melihat daftar komit yang hanya ada di cabang itu. Dalam pertanyaan ini kita membahas cara untuk melihat komit mana yang ada di satu cabang tetapi tidak di satu atau lebih cabang lain yang ditentukan.
Ini sedikit berbeda. Saya ingin melihat yang melakukan adalah pada satu cabang tetapi tidak pada setiap cabang lain.
Kasus penggunaan adalah dalam strategi percabangan di mana beberapa cabang hanya boleh digabungkan, dan tidak pernah diterapkan secara langsung. Ini akan digunakan untuk memeriksa apakah ada komit yang telah dibuat secara langsung di cabang "hanya gabungan".
EDIT: Berikut adalah langkah-langkah untuk menyiapkan repo git tiruan untuk diuji:
git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt
git commit -am "2nd valid commit on master"
git checkout merge-only
git merge master
Hanya komit dengan pesan "komit buruk langsung di hanya gabungan", yang dibuat langsung di cabang khusus gabungan, yang akan muncul.
git log ^branch1 ^branch2 merge-only-branch
sintaks saja?git log ^branch1 ^branch2 merge-only-branch
memerlukan daftar keluar setiap cabang tunggal. Itu dapat dihindari dengan beberapa penggunaan bash / grep yang cerdik (lihat jawaban saya di bawah), tetapi saya berharap git memiliki dukungan bawaan untuk ini. Anda benar bahwa ini menganggap semua cabang merge-from adalah jarak jauh (hanya lokal dan tidak ada untuk pengembang lain). Menggunakan--no-merges
menghilangkan setiap komit yang digabungkan dan kemudian memiliki cabang gabungan asli mereka dihapus, jadi ini mengasumsikan cabang gabungan dari disimpan sampai mereka telah digabungkan ke cabang non-gabungan-saja (yaitu master).Jawaban:
Kami baru saja menemukan solusi elegan ini
Dalam contoh Anda, tentu saja komit awal masih muncul.
jawaban ini tidak benar-benar menjawab pertanyaan, karena komit awal masih muncul. Di sisi lain, banyak orang yang datang ke sini sepertinya menemukan jawaban yang mereka cari.
sumber
initial valid commit
, yang merupakan bagian dari cabangmerge-only
danmaster
. Namun, jika seseorang melakukan upaya untuk memasukkan nama cabang saat ini di bagian akhir diikuti oleh ^ -nama cabang yang diawali dengan yang diketahui dari cabang saat ini, itu memecahkan setengah masalah (tidak termasuk hal-hal yang digabungkan). Contoh:git log --first-parent --no-merges merge-only ^master
git log --first-parent --no-merges | grep <branch_name>
Atas kebaikan teman saya Redmumba :
... di mana
origin/merge-only
nama cabang hanya gabungan jarak jauh Anda. Jika bekerja pada repo git khusus lokal, gantikanrefs/remotes/origin
denganrefs/heads
, dan gantikan nama cabang jarak jauhorigin/merge-only
dengan nama cabang lokalmerge-only
, yaitu:sumber
git for-each-ref
untuk membuat daftar setiap nama ref di asalnya, dangrep -v
untuk menghilangkan cabang hanya gabungan.git log
mengambil--not
opsi, yang kami berikan daftar semua referensi kami (kecuali cabang hanya-gabungan). Jika Anda memiliki jawaban yang lebih elegan untuk masalah ini, mari kita dengarkan./*
di belakangnyagit for-each-refs
bergantung pada tidak mencocokkan beberapa file yang ada dan tidak memilikifailglob
ataunullglob
menyetel ( opsi bash , shell lain bervariasi). Anda harus mengutip / menghilangkan tanda bintang atau membiarkannya/*
saja (git for-each-ref
pola dapat cocok dengan "dari awal hingga garis miring"). Mungkin usegrep -Fv refs/remotes/origin/foo
(refs/heads/foo
) untuk lebih ketat tentang ref mana yang dihilangkan.git log --no-merges B1 --not B2
:, di mana B1 adalah cabang yang Anda minati, dan B2 adalah cabang yang ingin Anda bandingkan dengan B1. Baik B1 dan B2 dapat menjadi cabang lokal atau jarak jauh, sehingga Anda dapat menentukangit log --no-merges master --not origin/master
, atau bahkan menentukan dua cabang jarak jauh.Ini akan menunjukkan semua komit yang dibuat di cabang Anda.
sumber
origin/branchName
akan menunjuk ke kepala cabang jarak jauh DANHEAD
akan menunjuk ke commitid dari komit lokal terakhir di cabang itu. Jadi ini tidak akan berfungsi jika Anda telah menggunakan git push.Jawaban @Prakash berhasil. Hanya untuk kejelasan ...
mencantumkan komit di cabang fitur tetapi bukan cabang upstream (biasanya master Anda).
sumber
Mungkin ini bisa membantu:
sumber
Coba ini:
Pada dasarnya
git rev-list --all ^branch
mendapatkan semua revisi bukan di cabang dan kemudian Anda semua revisi di repo dan kurangi daftar sebelumnya yang hanya revisi di cabang.Setelah komentar @ Brian:
Dari dokumentasi git rev-list:
List commits that are reachable by following the parent links from the given commit(s)
Jadi perintah seperti di
git rev-list A
mana A adalah komit akan mencantumkan komitmen yang dapat dijangkau dari A termasuk A.Dengan pemikiran seperti itu, sesuatu seperti
git rev-list --all ^A
akan mencantumkan komitmen yang tidak dapat dijangkau dari A
Begitu
git rev-list --all ^branch
akan mencantumkan semua komitmen yang tidak dapat dijangkau dari ujung cabang. Yang akan menghapus semua komit di cabang, atau dengan kata lain komit yang hanya ada di cabang lain.Sekarang mari kita ke
git rev-list --all --not $(git rev-list --all ^branch)
Ini akan menjadi seperti
git rev-list --all --not {commits only in other branches}
Jadi kami ingin membuat daftar
all
yang tidak dapat dijangkau dariall commits only in other branches
Yang merupakan kumpulan komit yang hanya ada di cabang. Mari kita ambil contoh sederhana:
Di sini tujuannya adalah untuk mendapatkan D dan E, komit bukan di cabang lain mana pun.
git rev-list --all ^branch
berikan hanya BSekarang,
git rev-list --all --not B
inilah yang kita lihat. Yang jugagit rev-list -all ^B
- kami ingin semua komitmen tidak dapat dijangkau dari B. Dalam kasus kami, D dan E. Yang mana yang kami inginkan.Semoga ini menjelaskan bagaimana perintah bekerja dengan benar.
Edit setelah komentar:
Setelah langkah-langkah di atas, jika Anda melakukan,
git rev-list --all --not $(git rev-list --all ^merge-only)
Anda akan mendapatkan komit yang Anda cari - yang"bad commit directly on merge-only"
satu.Tetapi begitu Anda melakukan langkah terakhir dalam langkah Anda
git merge master
, perintah tidak akan memberikan hasil yang diharapkan. Karena saat ini tidak ada komit yang tidak ada di hanya-gabungan karena satu komit ekstra di master juga telah digabungkan menjadi hanya-gabungan. Jadigit rev-list --all ^branch
memberikan hasil kosong dan karenanyagit rev-list -all --not $(git rev-list --all ^branch)
akan memberikan semua komit hanya dalam bentuk gabungan.sumber
xargs -L 1 -t git branch -a --contains
menampilkan banyak positif palsu (komit yang sebenarnya ada di cabang lain). Saya mencoba dengan dan tanpa--no-merges
. Terima kasih telah menjawabnya!git rev-list --all ^branch
akan memberi Anda semua komitmen yang tidak adabranch
. Anda kemudian menguranginya dari daftar yang ada dibranch
; tapi menurut definisi, semua komit yang tidakbranch
ada tidak masukbranch
, jadi Anda tidak mengurangi apa pun. Apa yang dicari jimmyorr adalah komit yang ada di dalambranch
tetapi tidak adamaster
, atau cabang lainnya. Anda tidak ingin mengurangi komit yang tidak adabranch
; Anda ingin mengurangi komit yang ada di cabang lain.branch
, tetapi begitu jugagit rev-list branch
. Anda hanya menulisgit rev-list branch
dengan cara yang lebih rumit (dan lebih lambat). Tidak berfungsi untuk menjawab pertanyaan, yaitu bagaimana menemukan semua komitbranch
yang tidak ada di cabang lain .Variasi lain dari jawaban yang diterima, untuk digunakan dengan
master
git log origin/master --not $(git branch -a | grep -Fv master)
Filter semua komit yang terjadi di cabang mana pun selain master.
sumber
Ini bukan jawaban yang sebenarnya, tetapi saya membutuhkan akses ke pemformatan, dan banyak ruang. Saya akan mencoba menjelaskan teori di balik apa yang saya anggap sebagai dua jawaban terbaik: jawaban yang diterima dan (setidaknya saat ini) peringkat teratas . Namun nyatanya, mereka menjawab pertanyaan yang berbeda .
Commit di Git sangat sering "di" lebih dari satu cabang pada satu waktu. Memang, sebagian besar pertanyaannya. Diberikan:
di mana huruf besar mewakili ID hash Git yang sebenarnya, kami sering hanya
H
mencari komit atau hanya komitI-J
dalamgit log
keluaran kami . Komit melaluiG
ada di kedua cabang, jadi kami ingin mengecualikannya.(Perhatikan bahwa dalam grafik yang digambar seperti ini, komit yang lebih baru mengarah ke kanan. Nama tersebut memilih komit paling kanan tunggal pada baris itu. Masing-masing komit tersebut memiliki komit induk, yaitu komit di sebelah kiri: induk dari
H
isG
, dan induk dariJ
adalahI
. Induk dariI
adalahG
lagi. Induk dariG
adalahF
, danF
memiliki induk yang tidak ditampilkan di sini: itu bagian dari...
bagian.)Untuk kasus yang sangat sederhana ini, kita dapat menggunakan:
untuk melihat
I-J
, atau:untuk melihat
H
saja. Nama sisi kanan, setelah dua titik, memberi tahu Git: ya, komit ini . Nama sisi kiri, sebelum dua titik, memberi tahu Git: tidak, bukan komit ini . Git dimulai di akhir —at komitH
atau komitJ
— dan bekerja mundur . Untuk (lebih) lagi tentang ini, lihat Think Like (a) Git .Cara pertanyaan asli diutarakan, keinginannya adalah untuk menemukan komitmen yang dapat dijangkau dari satu nama tertentu, tetapi tidak dari nama lain dalam kategori umum yang sama. Artinya, jika kita memiliki grafik yang lebih kompleks:
kita dapat memilih salah satu dari nama ini, seperti
name4
atauname3
, dan bertanya: komit mana yang dapat ditemukan dengan nama itu, tetapi tidak dengan nama lain? Jika kita memilihname3
jawabannya adalah komitL
. Jika kita memilihname4
, jawabannya adalah tidak ada komit sama sekali: komit bahwaname4
nama adalah komitN
tetapi komitN
dapat ditemukan dengan memulainame5
dan bekerja mundur.Jawaban yang diterima berfungsi dengan nama pelacakan jarak jauh, bukan nama cabang, dan memungkinkan Anda untuk menetapkan satu — yang dieja
origin/merge-only
— sebagai nama yang dipilih dan melihat semua nama lain di namespace itu. Ini juga menghindari menampilkan penggabungan: jika kita memilihname1
sebagai "nama yang menarik", dan mengatakan tunjukkan komit yang dapat dijangkau dariname1
tetapi bukan nama lain , kita akan melihat komit gabunganM
serta komit regulerI
.Jawaban paling populer agak berbeda. Ini semua tentang melintasi grafik komit tanpa mengikuti kedua kaki penggabungan, dan tanpa menunjukkan komitmen apa pun yang merupakan penggabungan. Jika kita mulai dengan
name1
, misalnya, kita tidak akan menampilkanM
(ini adalah gabungan), tetapi dengan asumsi orang tua pertama dari gabunganM
adalah komitI
, kita bahkan tidak akan melihat komitJ
danK
. Kami akan berakhir menampilkan komitI
, dan juga melakukanH
,G
,F
, dan sebagainya-tak satu pun dari ini komit merge dan semua bisa dicapai dengan mulaiM
dan bekerja mundur, mengunjungi hanya pertama orang tua masing-masing gabungan komit.Jawaban paling populer sangat cocok untuk, misalnya, melihat
master
kapanmaster
dimaksudkan untuk menjadi cabang khusus gabungan. Jika semua "pekerjaan nyata" dilakukan pada cabang samping yang kemudian digabungmaster
, kita akan memiliki pola seperti ini:di mana semua
o
komit tanpa nama adalah komit biasa (bukan gabungan) danM
danN
merupakan komit gabungan. KomitI
adalah komit awal: komit pertama yang pernah dibuat, dan satu-satunya yang harus ada di master yang bukan komit gabungan. Jikagit log --first-parent --no-merges master
menunjukkan komit selainI
, kami memiliki situasi seperti ini:di mana kami ingin melihat komit
*
yang dibuat secara langsungmaster
, bukan dengan menggabungkan beberapa cabang fitur.Singkatnya, jawaban populer sangat bagus untuk melihat
master
kapanmaster
dimaksudkan untuk menjadi hanya gabungan, tetapi tidak terlalu bagus untuk situasi lain. Jawaban yang diterima berfungsi untuk situasi lain ini.Apakah nama pelacak jarak jauh seperti nama
origin/master
cabang ?Beberapa bagian Git mengatakan tidak:
berkata
on branch master
, tapi:kata
HEAD detached at origin/master
. Saya lebih suka setuju dengangit checkout
/git switch
:origin/master
bukan nama cabang karena Anda tidak bisa mendapatkan "di" nya.Jawaban yang diterima menggunakan nama pelacakan jarak jauh
origin/*
sebagai "nama cabang":Garis tengah, yang memanggil
git for-each-ref
, mengulangi nama pelacak jarak jauh untuk nama jarak jauhorigin
.Alasan ini adalah solusi yang baik untuk masalah asli adalah karena kami tertarik di sini pada nama cabang orang lain , daripada nama cabang kami . Tapi itu berarti kita telah mendefinisikan cabang sebagai sesuatu selain dari nama cabang kita . Tidak apa-apa: ketahuilah bahwa Anda sedang melakukan ini, saat Anda melakukannya.
git log
melintasi beberapa bagian dari grafik komitApa yang sebenarnya kami cari di sini adalah rangkaian dari apa yang saya sebut daglets: lihat Apa sebenarnya yang kami maksud dengan "cabang"? Artinya, kami mencari fragmen dalam beberapa subset dari grafik komit keseluruhan .
Setiap kali kita melihat Git melihat nama cabang seperti
master
, nama tag sepertiv2.1
, atau nama pelacakan jarak jauh sepertiorigin/master
, kita cenderung ingin Git memberi tahu kita tentang komit itu dan setiap komit yang bisa kita dapatkan dari komit itu: mulai dari sana , dan bekerja mundur.Dalam matematika, ini disebut sebagai grafik berjalan . Grafik komit Git adalah Grafik Asiklik Terarah atau DAG , dan grafik semacam ini sangat cocok untuk berjalan. Saat berjalan di grafik seperti itu, seseorang akan mengunjungi setiap simpul grafik yang dapat dijangkau melalui jalur yang digunakan. Titik-titik dalam grafik Git adalah komit, dengan ujung-ujungnya menjadi busur — tautan satu arah — dari setiap turunan ke setiap induk. (Di sinilah Think Like (a) Git masuk. Sifat busur satu arah berarti Git harus bekerja mundur, dari anak ke orang tua.)
Dua perintah utama Git untuk pembuatan grafik adalah
git log
dangit rev-list
. Perintah-perintah ini sangat mirip — sebenarnya sebagian besar dibuat dari file sumber yang sama — tetapi keluarannya berbeda:git log
menghasilkan keluaran untuk dibaca manusia, sementaragit rev-list
menghasilkan keluaran yang dimaksudkan untuk dibaca oleh program Git lain. 1 Kedua perintah ini melakukan jenis grafik berjalan.Penjelajahan grafik yang mereka lakukan secara khusus: mengingat beberapa set komit titik awal (mungkin hanya satu komit, mungkin sekumpulan ID hash, mungkin sekumpulan nama yang menyelesaikan menjadi ID hash), menjalankan grafik, mengunjungi komit . Arahan tertentu, seperti
--not
atau awalan^
, atau--ancestry-path
, atau--first-parent
, memodifikasi jalan grafik dengan cara tertentu.Saat mereka menjalankan grafik, mereka mengunjungi setiap commit. Tetapi mereka hanya mencetak beberapa subset yang dipilih dari walking commit. Arahan seperti
--no-merges
atau--before <date>
beri tahu kode berjalan grafik yang berkomitmen untuk dicetak .Untuk melakukan kunjungan ini, satu komit pada satu waktu, dua perintah ini menggunakan antrian prioritas . Anda menjalankan
git log
ataugit rev-list
dan memberinya beberapa titik awal komit. Mereka menempatkan komit tersebut ke dalam antrean prioritas. Misalnya, sederhana:mengubah nama
master
menjadi ID hash mentah dan menempatkan ID hash tersebut ke dalam antrean. Atau:mengubah kedua nama menjadi ID hash dan — dengan asumsi ini adalah dua ID hash yang berbeda — menempatkan keduanya ke dalam antrean.
Prioritas komit dalam antrian ini ditentukan oleh lebih banyak argumen. Misalnya, argumen
--author-date-order
memberi tahugit log
ataugit rev-list
menggunakan stempel waktu penulis , bukan stempel waktu pelaku. Standarnya adalah menggunakan stempel waktu pelaku dan memilih commit terbaru berdasarkan tanggal: yang memiliki tanggal numerik tertinggi. Jadi denganmaster develop
asumsi ini menyelesaikan dua komit yang berbeda, Git akan menunjukkan mana yang lebih dulu datang , karena itu akan berada di depan antrian.Bagaimanapun, revisi kode berjalan sekarang berjalan dalam satu putaran:
--no-merges
,: tidak mencetak apa pun jika itu adalah komit gabungan;--before
: tidak mencetak apa pun jika tanggalnya tidak datang sebelum waktu yang ditentukan. Jika pencetakan tidak ditekan, cetak komit: untukgit log
, tampilkan lognya; untukgit rev-list
, cetak ID hash-nya.--first-parent
menekan semua kecuali orang tua pertama dari setiap gabungan.(Keduanya
git log
dangit rev-list
dapat melakukan penyederhanaan riwayat dengan atau tanpa penulisan ulang orang tua pada saat ini juga, tetapi kami akan melewatkannya di sini.)Untuk rantai sederhana, seperti mulai
HEAD
dan bekerja mundur saat tidak ada komit gabungan, antrean selalu memiliki satu komit di dalamnya di bagian atas perulangan. Ada satu komit, jadi kami mengeluarkannya dan mencetaknya dan meletakkan (tunggal) induknya ke dalam antrian dan berputar lagi, dan kami mengikuti rantai ke belakang sampai kami mencapai komit pertama, atau pengguna bosan dengangit log
keluaran dan keluar program. Dalam kasus ini, tidak ada opsi pengurutan yang penting: hanya ada satu komit untuk ditampilkan.Ketika ada penggabungan dan kami mengikuti kedua orang tua — keduanya merupakan "kaki" dari penggabungan — atau saat Anda memberi
git log
ataugit rev-list
lebih dari satu komitmen awal, opsi penyortiran penting.Terakhir, pertimbangkan efek dari
--not
atau^
di depan penentu komit. Ini memiliki beberapa cara untuk menulisnya:atau:
atau:
semuanya memiliki arti yang sama. Itu
--not
seperti awalan^
kecuali itu berlaku untuk lebih dari satu nama:berarti bukan branch1, bukan branch2, yes branch3;tapi:
berarti bukan branch1, bukan branch2, bukan branch3, dan Anda harus menggunakan yang kedua
--not
untuk mematikannya:yang agak canggung. Kedua arahan "bukan" digabungkan melalui XOR, jadi jika Anda benar-benar ingin, Anda dapat menulis:
berarti bukan branch1, bukan branch2, ya branch3 , jika Anda ingin mengaburkan .
Ini semua bekerja dengan mempengaruhi grafik berjalan. Saat
git log
ataugit rev-list
menjalankan grafik, itu memastikan untuk tidak memasukkan ke dalam antrian prioritas setiap komit yang dapat dijangkau dari referensi yang dinegasikan . (Faktanya, mereka juga mempengaruhi pengaturan awal: komit yang dinegasikan tidak dapat masuk ke antrian prioritas langsung dari baris perintah, jadigit log master ^master
tidak menunjukkan apa-apa, misalnya.)Semua sintaksis mewah yang dijelaskan dalam dokumentasi gitrevision memanfaatkan ini, dan Anda dapat mengeksposnya dengan panggilan sederhana ke
git rev-parse
. Misalnya:Sintaks tiga titik berarti komitmen dapat dijangkau dari sisi kiri atau kanan, tetapi mengecualikan komitmen yang dapat dijangkau dari keduanya . Dalam hal ini
origin/master
komitb34789c0b
,, itu sendiri dapat dijangkau dariorigin/pu
(fc307aa37...
) sehinggaorigin/master
hash muncul dua kali, sekali dengan negasi, tetapi pada kenyataannya Git mencapai sintaks tiga titik dengan memasukkan dua referensi positif — dua ID hash non-negasi — dan satu negatif, diwakili oleh^
awalan.Demikian pula:
The
^@
berarti sintaks semua orang tua yang diberikan komit , danmaster^
sendiri-induk pertama dari komit dipilih oleh cabang-namamaster
-adalah gabungan komit, sehingga memiliki dua orang tua. Ini adalah dua orang tua. Dan:The
^!
berarti akhiran komit itu sendiri, namun tidak satupun dari orang tuanya . Dalam hal ini,master^
adalah0b07eecf6...
. Kami sudah melihat kedua orang tua dengan^@
sufiks; di sini mereka lagi, tapi kali ini, dinegasikan.1 Banyak program Git benar-benar berjalan
git rev-list
dengan berbagai opsi, dan membaca keluarannya, untuk mengetahui komit dan / atau objek Git lain yang akan digunakan.2 Karena grafiknya asiklik , mungkin untuk menjamin bahwa tidak ada yang sudah dikunjungi, jika kita menambahkan batasan jangan pernah tampilkan induk sebelum menampilkan semua anaknya ke prioritas.
--date-order
,,--author-date-order
dan--topo-order
tambahkan batasan ini. Tata urutan default — yang tidak memiliki nama — tidak. Jika stempel waktu commit tidak benar — jika misalnya beberapa commit dibuat "di masa mendatang" oleh komputer yang jamnya mati — ini dalam beberapa kasus dapat menyebabkan keluaran yang tampak aneh.Jika Anda berhasil sejauh ini, Anda sekarang tahu banyak tentangnya
git log
Ringkasan:
git log
adalah tentang menampilkan beberapa komit yang dipilih saat menjalankan beberapa atau semua bagian grafik.--no-merges
argumen, ditemukan di kedua diterima dan jawaban saat-top-peringkat, menekan menunjukkan beberapa komit bahwa yang berjalan.--first-parent
argumen, dari saat-top-peringkat-jawaban, menekan berjalan beberapa bagian dari grafik, selama grafik-berjalan sendiri.--not
awalan untuk argumen baris perintah, seperti yang digunakan dalam jawaban yang diterima, menekan pernah mengunjungi beberapa bagian dari grafik sama sekali, benar dari awal.Kami mendapatkan jawaban yang kami suka, untuk dua pertanyaan berbeda, menggunakan fitur-fitur ini.
sumber