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:
...--F--G--H <-- master
\
I--J <-- develop
di mana huruf besar mewakili ID hash Git yang sebenarnya, kami sering hanyaH mencari komit atau hanya komitI-J dalam git logkeluaran kami . Komit melalui Gada 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 His G, dan induk dari Jadalah I. Induk dari Iadalah Glagi. Induk dari Gadalah F, dan Fmemiliki induk yang tidak ditampilkan di sini: itu bagian dari ...bagian.)
Untuk kasus yang sangat sederhana ini, kita dapat menggunakan:
git log master..develop # note: two dots
untuk melihat I-J, atau:
git log develop..master # note: two dots
untuk melihat Hsaja. 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 komit Hatau komit J— 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:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
kita dapat memilih salah satu dari nama ini, seperti name4atau name3, dan bertanya: komit mana yang dapat ditemukan dengan nama itu, tetapi tidak dengan nama lain? Jika kita memilih name3jawabannya adalah komit L. Jika kita memilih name4, jawabannya adalah tidak ada komit sama sekali: komit bahwa name4nama adalah komit Ntetapi komit Ndapat ditemukan dengan memulai name5dan 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 memilih name1sebagai "nama yang menarik", dan mengatakan tunjukkan komit yang dapat dijangkau dari name1tetapi bukan nama lain , kita akan melihat komit gabungan Mserta komit reguler I.
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 menampilkan M(ini adalah gabungan), tetapi dengan asumsi orang tua pertama dari gabungan Madalah komit I, kita bahkan tidak akan melihat komit Jdan K. Kami akan berakhir menampilkan komit I, dan juga melakukan H, G, F, dan sebagainya-tak satu pun dari ini komit merge dan semua bisa dicapai dengan mulai Mdan bekerja mundur, mengunjungi hanya pertama orang tua masing-masing gabungan komit.
Jawaban paling populer sangat cocok untuk, misalnya, melihat masterkapan masterdimaksudkan untuk menjadi cabang khusus gabungan. Jika semua "pekerjaan nyata" dilakukan pada cabang samping yang kemudian digabung master, kita akan memiliki pola seperti ini:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
di mana semua okomit tanpa nama adalah komit biasa (bukan gabungan) dan Mdan Nmerupakan komit gabungan. Komit Iadalah komit awal: komit pertama yang pernah dibuat, dan satu-satunya yang harus ada di master yang bukan komit gabungan. Jika git log --first-parent --no-merges mastermenunjukkan komit selain I , kami memiliki situasi seperti ini:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
di mana kami ingin melihat komit *yang dibuat secara langsung master, bukan dengan menggabungkan beberapa cabang fitur.
Singkatnya, jawaban populer sangat bagus untuk melihat masterkapan masterdimaksudkan 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:
git checkout master
...
git status
berkata on branch master, tapi:
git checkout origin/master
...
git status
kata HEAD detached at origin/master. Saya lebih suka setuju dengan git checkout/ git switch: origin/masterbukan nama cabang karena Anda tidak bisa mendapatkan "di" nya.
Jawaban yang diterima menggunakan nama pelacakan jarak jauh origin/*sebagai "nama cabang":
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
Garis tengah, yang memanggil git for-each-ref, mengulangi nama pelacak jarak jauh untuk nama jarak jauh origin.
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 komit
Apa 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 seperti v2.1, atau nama pelacakan jarak jauh seperti origin/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 logdan git rev-list. Perintah-perintah ini sangat mirip — sebenarnya sebagian besar dibuat dari file sumber yang sama — tetapi keluarannya berbeda: git logmenghasilkan keluaran untuk dibaca manusia, sementara git rev-listmenghasilkan 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 --notatau 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-mergesatau --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 logatau git rev-listdan memberinya beberapa titik awal komit. Mereka menempatkan komit tersebut ke dalam antrean prioritas. Misalnya, sederhana:
git log master
mengubah nama mastermenjadi ID hash mentah dan menempatkan ID hash tersebut ke dalam antrean. Atau:
git log master develop
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-ordermemberi tahu git logatau git rev-listmenggunakan 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 dengan master developasumsi 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:
- Saat ada komit dalam antrean:
- Hapus entri antrian pertama.
- Putuskan apakah akan mencetak komit ini atau tidak. Misalnya
--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: untuk git log, tampilkan lognya; untuk git rev-list, cetak ID hash-nya.
- Letakkan beberapa atau semua komit induk komit ini ke dalam antrian (selama tidak ada sekarang, dan belum dikunjungi 2 ). Default normal adalah menempatkan semua orang tua. Menggunakan
--first-parentmenekan semua kecuali orang tua pertama dari setiap gabungan.
(Keduanya git logdan git rev-listdapat 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 HEADdan 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 dengan git logkeluaran 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 logatau git rev-listlebih dari satu komitmen awal, opsi penyortiran penting.
Terakhir, pertimbangkan efek dari --not atau ^di depan penentu komit. Ini memiliki beberapa cara untuk menulisnya:
git log master --not develop
atau:
git log ^develop master
atau:
git log develop..master
semuanya memiliki arti yang sama. Itu --notseperti awalan^ kecuali itu berlaku untuk lebih dari satu nama:
git log ^branch1 ^branch2 branch3
berarti bukan branch1, bukan branch2, yes branch3;tapi:
git log --not branch1 branch2 branch3
berarti bukan branch1, bukan branch2, bukan branch3, dan Anda harus menggunakan yang kedua--not untuk mematikannya:
git log --not branch1 branch2 --not branch3
yang agak canggung. Kedua arahan "bukan" digabungkan melalui XOR, jadi jika Anda benar-benar ingin, Anda dapat menulis:
git log --not branch1 branch2 ^branch3
berarti bukan branch1, bukan branch2, ya branch3 , jika Anda ingin mengaburkan .
Ini semua bekerja dengan mempengaruhi grafik berjalan. Saat git logatau git rev-listmenjalankan 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, jadi git log master ^mastertidak 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:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
Sintaks tiga titik berarti komitmen dapat dijangkau dari sisi kiri atau kanan, tetapi mengecualikan komitmen yang dapat dijangkau dari keduanya . Dalam hal ini origin/masterkomit b34789c0b,, itu sendiri dapat dijangkau dari origin/pu(fc307aa37... ) sehingga origin/masterhash 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:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
The ^@berarti sintaks semua orang tua yang diberikan komit , dan master^sendiri-induk pertama dari komit dipilih oleh cabang-nama master-adalah gabungan komit, sehingga memiliki dua orang tua. Ini adalah dua orang tua. Dan:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
The ^!berarti akhiran komit itu sendiri, namun tidak satupun dari orang tuanya . Dalam hal ini, master^adalah 0b07eecf6.... 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-listdengan 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-orderdan --topo-ordertambahkan 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.
- The
--no-mergesargumen, ditemukan di kedua diterima dan jawaban saat-top-peringkat, menekan menunjukkan beberapa komit bahwa yang berjalan.
- The
--first-parentargumen, dari saat-top-peringkat-jawaban, menekan berjalan beberapa bagian dari grafik, selama grafik-berjalan sendiri.
- The
--notawalan 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.
git log ^branch1 ^branch2 merge-only-branchsintaks saja?