Perintah git history
(lalitm.com)- Perintah eksperimental
git historyyang diperkenalkan di Git 2.54 dan 2.55 mengotomatiskan perbaikan commit lama dan rekonstruksi branch turunan sambil tetap mempertahankan alur kerja Git yang sudah ada fixupmenggabungkan perubahan yang sudah di-stage ke commit lama, lalu secara otomatis me-rebase semua branch lokal yang diturunkan dari commit tersebut agar sesuai dengan hash commit barurewordmengubah pesan commit lama, sedangkansplitmembagi perubahan dalam sebuah commit berdasarkan hunk menjadi dua commit, lalu menyusun ulang commit dan branch berikutnya- Ketiga subperintah mencegah work tree berada dalam kondisi setengah rusak lewat pemrosesan atomik yang menolak operasi yang berpotensi menimbulkan konflik, tetapi tidak bekerja pada riwayat yang memiliki merge commit
- Meski tidak menyediakan penanganan konflik kelas satu, undo berbasis operation log, maupun pemodelan working copy sebagai commit seperti
jj, beberapa fitur pengeditan riwayat alajjdapat digunakan dari distribusi inti Git tanpa instalasi terpisah
Mengapa pengeditan riwayat Git yang ada terasa merepotkan
- Saat menangani banyak perubahan secara paralel, Anda harus terus berpindah antara branch dan commit, dan selama
rebase -iwork tree bisa terjebak dalam keadaan tidak lengkap - Alternatif Git,
jj, sering disebut sebagai solusi untuk masalah ini, tetapi meski dicoba selama beberapa hari setiap tiga bulan selama satu setengah tahun, pada akhirnya alur kerja Git yang lama tetap digunakan kembali git historyadalah perintah eksperimental yang disertakan dalam distribusi inti Git, sehingga dapat dicoba tanpa memasang tool terpisah- Subperintahnya terdiri dari tiga jenis:
fixup,reword, dansplit
fixup: memperbaiki commit lama dan branch turunan sekaligus
git history fixupmemperbaiki bagian yang salah dari commit lama dan secara otomatis me-rebase branch yang diturunkan dari commit tersebut- Seperti prosedur perbaikan biasa, setelah men-stage perubahan dengan
git add, jalankangit history fixup <commit>agar perubahan yang sudah di-stage digabungkan ke commit target - Ini mirip dengan gabungan
git commit --fixupdan rebase autosquash, tetapi berbeda karena juga memperbarui branch lain yang memuat commit target - Dibandingkan
git rebase --update-refsyang hanya memindahkan referensi dalam rentang rebase saat ini,git historymencari dan menulis ulang semua branch lokal yang diturunkan dari commit target- Jika perlu, cakupan pembaruan dapat dibatasi hanya ke branch saat ini
- Tidak bekerja pada riwayat yang memiliki merge commit, sehingga sulit diterapkan pada sebagian gaya penggunaan Git
- Jika perbaikan digabungkan ke
B, akan dibuatB*dengan hash baru, danCsertaDdi atasnya juga dibuat ulang masing-masing sebagaiC*danD*- Ujung branch
feat-1danfeat-2juga berpindah mengikuti commit baru
- Ujung branch
Pemrosesan atomik dan batasan konflik
- Ketiga subperintah semuanya diproses secara atomik, sehingga tidak meninggalkan work tree dalam kondisi setengah rusak
- Atomisitas dijamin dengan cara menolak operasi yang dapat menimbulkan konflik
- Ini lebih terbatas secara fungsional dibandingkan
jj, yang memperlakukan konflik sebagai objek kelas satu sehingga status konflik dapat dipertahankan selama rebase dan diselesaikan belakangan - Batasan
git historyditerapkan secara sengaja agar penulisan ulang riwayat tidak berubah menjadi operasi pelestarian status- Jika Git mendukung konflik kelas satu di masa mendatang, batasan ini dapat dicabut
reword: mengubah pesan commit tanpa work tree
git history rewordmengubah pesan commit lama dan secara otomatis menyusun ulang commit serta branch di atasnya- Saat menjalankan
git history reword <commit>, pesan lama akan terbuka di editor; setelah diedit dan disimpan, stack commit berikutnya dibangun ulang agar sesuai dengan hash baru - Berguna untuk memperbaiki pesan commit lama agar sesuai dengan struktur saat ini ketika desain berubah selama pengembangan berulang
- Cara kerjanya sama seperti
fixup, kecuali yang diubah hanya pesan commit, bukan isi tree rewordtidak menyentuh indeks atau work tree, dan hanya mengubah graf commit- Commit pada branch yang tidak di-checkout juga dapat diperbaiki tanpa mengganggu pekerjaan saat ini
- Jika pesan
Bdiubah, hashnya ikut berubah, sehinggaCberikutnya dibuat ulang sebagaiC*danfeat-1juga mengikuti commit baru
split: memisahkan satu commit menjadi dua
git history splitmemilih perubahan dalam satu commit secara interaktif dan membaginya menjadi dua commit- Saat menjalankan
git history split <commit>, prompt untuk memilih perubahan commit target berdasarkan hunk akan terbuka- Hunk yang dipilih untuk dipertahankan membentuk commit pertama
- Hunk sisanya masuk ke commit kedua
- Mirip dengan
git add -p, tetapi tidak memerlukan prosedur rumit yang menggabungkangit rebase - Jika
Byang berisi dua perubahan tidak terkait dipisah,B1danB2akan dibuat, danCberikutnya dibangun ulang sebagaiC*di atas kedua commit tersebut - Di antara tiga perintah ini, cakupan penggunaannya paling spesifik, tetapi berguna saat commit memang perlu dipisahkan
Perbedaan fitur dengan jj
git historytidak menggantikan seluruh model kerja yang disediakanjj- Sebaliknya,
git historymendukung perbaikan commit lama, perubahan pesan, pemisahan commit, dan pembaruan branch turunan di dalam Git yang sudah ada, sehingga sebagian keunggulanjjdapat dibawa ke alur kerja saat ini - Ada kemungkinan batasannya dilonggarkan di masa depan, sehingga masih ada ruang untuk peningkatan tambahan pada rilis Git berikutnya
1 komentar
Komentar Hacker News
Git dulu terasa tidak nyaman, tetapi setelah membaca tiga bab pertama dari buku Pro Git yang tersedia gratis, semuanya membaik. Buku itu memberikan model mental yang sangat baik tentang cara kerja internal Git, dan entah baik atau buruk, antarmuka pengguna Git mencerminkan struktur internalnya secara langsung, sehingga setelah memahaminya semuanya terasa masuk akal
git checkoutdirancang agar saat berpindah branch, perubahan yang belum masuk ke Git tidak hilang, tetapi saat memulihkan versi lain dari sebuah file, perubahan justru dikembalikan; bagaimana itu mencerminkan struktur internal secara langsung?Terlepas dari itu, inkonsistensi CLI yang membuat sulit menemukan sintaks baris perintah yang tepat meskipun sudah tahu operasi dan perintah yang diinginkan secara internal juga membuat Git sulit digunakan
git commit -am “Changes”tidaklah cukupAda
git rebase --abort, dan Anda juga bisa membuat tag sebelum rebase lalu kembali dengangit reset --hard $set_tag. Karena keadaan sebelumnya tidak hilang, tidak ada alasan untuk takut pada rebase interaktiftempataubefore-rebasejustru untuk keperluan itu. Dipakai seperti tag, tetapi branch lebih mudah dipindahkan, jadi sebelumrebase -ibesar biasanya saya menjalankangit branch before-rebase/some-featureNyaris tidak pernah benar-benar perlu
git reset before-rebase, tetapi saya sering membandingkangit log -p before-rebasedengan branch setelah rebase untuk memeriksa perubahan tak disengaja selama proses penyelesaian konflikgit reflog?jj undogit rebase, sekitar separuh waktu saya langsung mengetik--abortSaya tidak menganggap diri saya coder atau programmer, tetapi mempelajari Git terasa seperti memberi cara berpikir saya kekuatan super untuk menata sesuatu. Saya memakainya untuk banyak hal: situs web, proyek desain, elektronika, komposisi musik, repositori pengetahuan pribadi, skrip manajemen jarak jauh, manajemen konfigurasi, potongan kode, dan lain-lain. Meski tidak sempurna, saya menyarankan semua orang yang bekerja bersama saya untuk mempelajarinya
Dalam 『Getting Things Done』 karya David Allen, ada pembahasan tentang perlunya sistem tepercaya untuk mengumpulkan dan menata pikiran. Untuk tugas, proyek, dan daftar saya memakai OmniFocus, tetapi untuk menata dan melestarikan software serta sebagian data yang saya buat di komputer, saya memercayai Git
Secara teori saya sangat menyukai perintah-perintah ini dan juga selalu memakai perintah padanannya di
jj, tetapi saya tidak menemukan cara agargit historymenandatangani commit yang dimodifikasi. Setelah melihat manual dan kode sumber Git, jika commit yang sudah ditandatangani GPG diubah dengangit history reword HEAD~, semua tanda tangannya hilangKarena ingin mempertahankan tanda tangan commit, akhirnya saya kembali ke
git rebase -itradisionalgit historysebagai fitur eksperimental, jadi ini layak dilaporkan sebagai bugTanpa perintah baru pun, selama lebih dari 10 tahun saya sudah banyak mengubah riwayat commit Git lokal tanpa kesulitan besar. Saya yakin alasan besar mengapa Git dan rebase terasa sulit adalah masalah antarmuka pengguna, karena pengguna harus mencari sendiri opsi baris perintah yang tepat dan cara kerjanya
Di Windows, dialog rebase TortoiseGit kuat sekaligus mudah digunakan. Dari log commit, cukup klik kanan commit acuan lalu pilih
"Rebase onto this"; seperti di https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.htm..., pengubahan urutan, pemecahan, penggabungan, dan penyuntingan commit terlihat jelas, dan jika dibatalkan di tengah jalan akan kembali ke keadaan semulaNamun karena berupa ekstensi shell, jumlah kliknya terlalu banyak; akan lebih baik kalau berupa aplikasi mandiri dengan tab. Meski begitu, ini tampaknya prinsip desain yang mewarisi TortoiseSVN. Saya juga sempat mencoba Git Extensions, tetapi entah tidak menemukan fitur yang sering saya pakai atau sudah terlalu terbiasa dengan TortoiseGit sehingga tidak menetap memakainya. Tampilan log bisa dilihat di https://tortoisegit.org/docs/tortoisegit/tgit-dug-showlog.ht...
My Computer. Menurut pemahaman saya, ini mencapai puncaknya di XP, lalu sejak Vista Microsoft mengubahnya ke arah yang lebih sulit diperluasNamun saya masih tidak mengerti mengapa suatu menu di VC++6 membuat objek shell yang tampak seperti folder alih-alih daftar biasa, lalu menampilkannya di dialog buka file. Itu adalah wizard untuk menambahkan komponen ke proyek, dan selama dialog terbuka saya sering menemukan folder palsu itu di
My Computerfixupsudah bisa dilakukan dengangit commit --fixupatau sebelumnya dengangit rebase -i, danrewordjuga bisa dilakukan dengangit rebase -iSepertinya sudah cukup banyak umpan balik bahwa proses mengedit file untuk menentukan tindakan yang akan dijalankan pada tiap commit itu tidak nyaman, sehingga ditambahkan perintah tingkat lebih tinggi yang membungkus fungsi yang sudah ada
Pada alat baris perintah, setelah menjalankan perintah berikutnya, keluaran sebelumnya menjadi sulit diakses. Bahkan jika ingin melakukan cherry-pick commit yang terlihat di
git log, jika hash-nya tidak tersisa di terminal, Anda tidak bisa memasukkannya hanya dari ingatan; sedangkan antarmuka stateful mempertahankan beberapa interaksi secara paralel dan menyimpan informasi sebelumnyaGit memiliki sangat banyak informasi status yang harus diingat, seperti branch saat ini, branch sebelumnya, file yang dimodifikasi, commit yang belum di-push, dan sebagainya. Jika tidak menguasai alat pendukung seperti tampilan branch dan autocomplete dari plugin shell, banyak tab/pemisahan jendela, penyalinan antartab, serta pencarian autocomplete, penggunaan Git bisa menjadi sangat tidak menyenangkan
Saya melihat kelemahan praktis
jjhanyalah integrasi Git. Ia menstrukturkan bahasa spesifik domain untuk kumpulan file, kumpulan revisi, dan template, lalu menggunakannya secara konsisten di seluruh konfigurasi dan CLI; alih-alih mengekspos struktur internal secara langsung, ia menyediakan konsep yang kuat melalui antarmukaNamun karena terlalu fleksibel, interoperabilitas dengan Git sangat bergantung pada konvensi dan disiplin, dan karena hampir semua status
jjbisa diekspresikan ke Git, mudah sekali merusak repository remote. Untuk memulihkannya, Anda harus memahami Git secara mendalamUntuk proyek pengembangan baru yang dikerjakan sendiri,
jjlayak dicoba. Dalam kondisi yang menguntungkan, rasanya seperti Git awal jika dibandingkan dengan Subversion; jika lebih dulu terbiasa dengan cara kerjajjitu sendiri, Anda juga bisa sedikit lebih siap menghadapi integrasi Git yang berat nantinya. Konteks terkait ada di https://news.ycombinator.com/item?id=48903054jjjuga memiliki backend non-Git, tetapi saat ini satu-satunya yang dinyatakan cocok untuk lingkungan operasional adalah backend Git.jjtanpa integrasi Git masih lebih dekat ke konsep daripada kenyataanSaya menyukai
jjdan model mental version control-nya sehingga akan terus memakainya, tetapi karena kadang harus turun ke Git untuk menyelesaikan pekerjaan, ia terasa lebih seperti wrapper praktis di atas Git daripada sistem version control baru. Jika Git menambahkan perintah yang mendukung alur kerjajj, akan sulit menemukan alasan untuk memasangjjsecara terpisahCara
git history fixupotomatis me-rebase semua branch turunan tidak cocok dengan alur kerja saya. Saya menyimpan setiap versi branch fitur sepertimyfeature.1,myfeature.2untuk mencari versi lama saat perilaku berubah di versi baru, dangit reflogtidak bisa menggantikannyaMisalnya jika masalah muncul antara
myfeature.58danmyfeature.59, dan tiap fitur memiliki 15 commit, dengangit range-diff myfeature.58~15..myfeature.58 myfeature.59~15..myfeature.59saya bisa melihat perbedaan kedua versi per commit meski commit basisnya berbeda. Karena itu saya tidak menginginkan rebase otomatis semua branch yang menyertakan commit tertentu, dan cara memakai tag untuk versi lama juga tidak terlalu cocok bagi sayaGit terbaru mengimplementasikan tiga operasi
git rebase --interactiveyang sering dipakai sebagai perintah terpisah dengan friksi rendah, dan tampaknya hanya berfungsi saat tidak ada konflikgit historybenar-benar berguna. Saya lebih suka memindahkan commit secara visual dengan rebase interaktif, dan tidak ingin secara tidak sengaja menetapkan hash commit yatim yang sudah hilang dari branch lokal saat ini sebagai targetgit historySaya tidak paham mengapa orang begitu bersusah payah merapikan riwayat Git hingga sempurna. Toh kita tidak akan membaca ulang commit satu per satu, jadi sebelum merge cukup squash semuanya
"oops"dan"fix", saya langsung menolaknyaCommit menyampaikan cerita pekerjaan, jadi kita harus menyediakan riwayat commit yang mudah dibaca agar reviewer tidak perlu bekerja lebih keras untuk memahami maksudnya
Kalau codebase dan fiturnya sangat kecil, mungkin semuanya bisa di-squash, tetapi saya lebih suka cara yang memungkinkan saya melihat alur perubahan sebenarnya dan melakukan cherry-pick atau revert commit secara selektif
Dalam kondisi saat ini checkout di
D, ketika mengubahBmenjadiB', saya memahami bahwagit rebase --update-refsmembiarkanEyang berada di luar cakupan rebase tetap apa adanya, hanya membuatB' → C' → D', dan mempertahankanB → Eyang lamaSebaliknya, apakah
git history fixupjuga memperbaruiEmenjadiE'? Jika ya, saya penasaran apakah dengangit rebaseyang sudah melekat di tangan selama puluhan tahun pun ada cara untuk melakukan pembaruan semua branch turunanEtidak bisa dibiarkan apa adanya. Saya belum pernah memakai--update-refssecara langsung, tetapi jika sesuai dokumentasi, hasilnya akan berupa grafik ketiga yang juga menghasilkanE', jadi perilaku yang diinginkan sudah bisa dilakukan dengangit rebase