git history komutu
(lalitm.com)- Git 2.54 ve 2.55 boyunca kullanıma sunulan deneysel
git historykomutu, mevcut Git iş akışını korurken geçmiş commit’leri düzenlemeyi ve türetilmiş branch’leri yeniden yapılandırmayı otomatikleştirir fixup, stage edilen değişiklikleri geçmişteki bir commit’e birleştirir; ardından o commit’ten türetilen tüm yerel branch’leri yeni commit hash’ine göre otomatik olarak rebase ederrewordgeçmiş commit mesajını değiştirir;splitise bir commit’in değişikliklerini hunk düzeyinde iki commit’e ayırdıktan sonra sonraki commit’leri ve branch’leri yeniden kurar- Bu üç alt komut, çakışma olasılığı taşıyan işlemleri reddeden atomik işleme sayesinde çalışma ağacının yarım kalmış bozuk bir duruma düşmesini engeller; ancak merge commit içeren geçmişlerde çalışmaz
jj’nin birinci sınıf çakışma yönetimi, işlem günlüğü tabanlı geri alma ve çalışma kopyasını commit olarak modelleme yeteneklerini sunmasa da, ayrı kurulum gerektirmeden Git çekirdek dağıtımında çeşitlijjtarzı geçmiş düzenleme özellikleri kullanılabilir
Mevcut Git geçmiş düzenleme neden kullanışsız
- Birden fazla değişikliği paralel ele alınca branch ve commit’ler arasında sürekli gidip gelmek gerekir; ayrıca
rebase -isırasında çalışma ağacı eksik veya bozuk bir duruma düşebilir - Git alternatifi
jj, bu sorunlara çözüm olarak sık anılsa da, bir buçuk yıl boyunca her üç ayda bir birkaç gün denenmesine rağmen sonunda yine mevcut Git iş akışına dönülmüş git history, Git çekirdek dağıtımına dahil olan deneysel bir komut olduğu için ayrı bir araç kurmadan denenebilir- Alt komutlar üç parçadan oluşur:
fixup,rewordvesplit
fixup: geçmiş commit’i ve türetilmiş branch’leri aynı anda düzeltme
git history fixup, geçmişteki bir commit’teki hatayı düzeltir ve o commit’ten türetilen branch’leri otomatik olarak rebase eder- Normal düzeltme akışındaki gibi değişiklikler
git addile stage edildikten sonragit history fixup <commit>çalıştırılırsa, stage edilen değişiklikler hedef commit ile birleştirilir - Bu,
git commit --fixupile autosquash rebase’in birleşimine benzer; ancak farkı, hedef commit’i içeren diğer branch’leri de güncellemesidir git rebase --update-refsyalnızca mevcut rebase kapsamındaki referansları taşırken,git historyhedef commit’ten türeyen tüm yerel branch’leri bulup yeniden yazar- Gerekirse güncelleme kapsamı yalnızca mevcut branch ile sınırlandırılabilir
- Merge commit içeren geçmişlerde çalışmadığı için bazı Git kullanım biçimlerinde uygulanması zordur
- Düzeltme
Biçine birleştirildiğinde yeni hash’e sahipB*oluşur; bunun üzerindekiCveDde sırasıylaC*veD*olarak yeniden oluşturulurfeat-1vefeat-2branch uçları da yeni commit’leri takip edecek şekilde taşınır
Atomik işleme ve çakışma kısıtları
- Üç alt komutun tamamı atomik olarak işlenir; böylece çalışma ağacı yarım bozulmuş bir durumda bırakılmaz
- Atomiklik, çakışma yaratabilecek işlemlerin baştan reddedilmesiyle sağlanır
- Bu, çakışmaları birinci sınıf nesne olarak ele alan ve rebase sırasında çakışma durumunu koruyup daha sonra çözmeye izin veren
jj’ye kıyasla işlevsel olarak daha sınırlıdır git historykısıtları, geçmiş yeniden yazımının durum koruyan bir işleme dönüşmemesi için bilinçli olarak uygulanmıştır- Git gelecekte birinci sınıf çakışma desteği sunarsa bu kısıtlar kaldırılabilir
reword: çalışma ağacına dokunmadan commit mesajı değiştirme
git history reword, geçmiş bir commit’in mesajını değiştirir ve onun üzerindeki commit’leri ve branch’leri otomatik olarak yeniden kurargit history reword <commit>çalıştırıldığında düzenleyicide mevcut mesaj açılır; değiştirip kaydedince sonraki commit yığını yeni hash’e göre yeniden oluşturulur- Yinelemeli geliştirme sırasında tasarım değiştiğinde, eski commit mesajlarını mevcut yapıya uygun hale getirmek için kullanışlıdır
- Ağaç içeriği yerine yalnızca commit mesajını değiştirmesi dışında
fixupile aynı şekilde çalışır reword, index’e ya da çalışma ağacına dokunmadan yalnızca commit grafiğini değiştirir- Checkout edilmemiş branch’lerdeki commit’ler de mevcut çalışmayı kesintiye uğratmadan düzenlenebilir
B’nin mesajı değiştiğinde hash de değişeceği için sonrakiC,C*olarak yeniden oluşturulur vefeat-1de yeni commit’i takip eder
split: tek bir commit’i ikiye ayırma
git history split, bir commit’in değişikliklerini etkileşimli olarak seçip iki commit’e bölergit history split <commit>çalıştırıldığında hedef commit’in değişikliklerini hunk düzeyinde seçmeye yönelik bir istem açılır- Seçilip korunan hunk’lar ilk commit’i oluşturur
- Kalan hunk’lar ikinci commit’e girer
git add -pbenzeri bir deneyim sunar, ancak bunagit rebaseekleyen karmaşık bir sürece ihtiyaç duymaz- Birbiriyle ilgisiz iki değişiklik içeren
BbölündüğündeB1veB2oluşur; sonrakiCise bu iki commit’in üzerineC*olarak yeniden kurulur - Üç komut arasında kullanım alanı en özel olanıdır, ancak gerçekten commit bölmek gerektiğinde faydalıdır
jj ile işlev farkları
git history,jj’nin sunduğu tüm çalışma modelinin yerini almaz- Buna karşılık
git history, mevcut Git içinde geçmiş commit düzeltme, mesaj değiştirme, commit bölme ve türetilmiş branch güncelleme desteği sağlayarakjj’nin bazı avantajlarını mevcut iş akışına taşımayı mümkün kılar - Gelecekte kısıtların gevşetilmesi ihtimali açık olduğundan, sonraki Git sürümlerinde ek iyileştirmeler gelebilir
1 yorum
Hacker News yorumları
Git kullanmak bana zor geliyordu ama ücretsiz yayımlanan Pro Git kitabının ilk üç bölümünü okuyunca durum düzeldi. Git’in iç işleyişine dair çok iyi bir zihinsel model sunuyor; iyi ya da kötü, Git’in kullanıcı arayüzü iç yapısını doğrudan yansıttığı için bunu anlayınca her şey yerine oturdu
git checkout, dal değiştirirken henüz Git’e girmemiş değişiklikleri kaybetmiyor; ama bir dosyanın başka bir sürümünü geri yüklerken değişiklikleri geri alacak şekilde tasarlanmış. Bu iç yapıyı nasıl doğrudan yansıtıyor?Bundan bağımsız olarak, içeride yapmak istediğiniz işlemi ve komutu bilseniz bile doğru komut satırı söz dizimini bulmayı zorlaştıran CLI tutarsızlığı da Git’i kullanmayı zorlaştırıyor
git commit -am “Changes”yeterli değilgit rebase --abortvar; ayrıca rebase’den önce bir etiket oluşturupgit reset --hard $set_tagile geri dönebilirsiniz. Önceki durum kaybolmadığı için etkileşimli rebase’den korkmaya gerek yoktempya dabefore-rebaseadlı çok sayıda dal oluşturuyorum. Etiket gibi kullanıyorum ama dalları taşımak daha kolay olduğu için büyük birrebase -iöncesinde geneldegit branch before-rebase/some-featureçalıştırıyorumGerçekte
git reset before-rebase’e pek ihtiyaç duymadım; ama rebase sonrası dalla karşılaştırıp çatışma çözme sürecindeki istenmeyen değişiklikleri kontrol etmek içingit log -p before-rebasekullandığım çok oldugit reflogkullanıyor musunuz?jj undodan daha basit olamazgit rebaseyazdıktan sonra yaklaşık yarısında hemen--abortyazıyorumKendimi coder ya da programcı olarak görmüyorum ama Git öğrenmek, düşünme biçimime düzenleme süper gücü kazandırmış gibiydi. Web siteleri, tasarım projeleri, elektronik, beste yapma, kişisel bilgi deposu, uzaktan yönetim betikleri, yapılandırma yönetimi, kod parçaları gibi pek çok amaçla kullanıyorum; mükemmel olmasa da birlikte çalıştığım herkese öğrenmesini öneriyorum
David Allen’ın 『Getting Things Done』 kitabında, düşünceleri toplamak ve düzenlemek için güvenilir bir sisteme ihtiyaç olduğundan bahsedilir. Ben işler, projeler ve listeler için OmniFocus kullanıyorum; ancak bilgisayarda ürettiğim yazılımları ve bazı verileri düzenleyip koruma konusunda Git’e güveniyorum
Bu komutları teoride çok seviyorum ve
jj’deki karşılıklarını da sürekli kullanıyorum; ancakgit history’nin değiştirdiği commit’leri imzalamanın yolunu bulamadım. Kılavuza ve Git kaynak koduna baktığımda da mevcut GPG imzalı commit’lerigit history reword HEAD~ile değiştirince tüm imzaların kaybolduğunu görüyorumCommit imzalarını korumak istediğim için sonunda geleneksel
git rebase -iye geri döndümgit historynin deneysel özellik olduğu yazdığına göre bug olarak bildirmeye değerYeni bir komut olmadan da 10 yıldan uzun süredir yerel Git commit geçmişini büyük zorluk yaşamadan kapsamlı biçimde değiştirebildim. Git’in ve rebase’in zor gelmesinin büyük nedeninin, doğru komut satırı seçeneklerini ve davranış biçimini kullanıcının kendisinin çözmesini gerektiren bir kullanıcı arayüzü sorunu olduğundan eminim
Windows’ta TortoiseGit’in rebase iletişim kutusu hem güçlü hem de kullanımı kolaydı. Commit günlüğünde temel alınacak commit’e sağ tıklayıp
"Rebase onto this"seçmek yeterli; https://tortoisegit.org/docs/tortoisegit/tgit-dug-rebase.htm... adresinde görüldüğü gibi commit’lerin sırasını değiştirme, bölme, birleştirme ve düzenleme net biçimde yapılabiliyor; ortasında iptal ederseniz de başlangıçtaki duruma dönüyorAncak kabuk uzantısı şeklinde çalıştığı için tıklama sayısı fazla; sekmeli bağımsız bir uygulama olsa daha iyi olurdu, ama bu TortoiseSVN’den devralınan bir tasarım ilkesi gibi görünüyor. Git Extensions’ı da bir süre denedim, fakat sık kullandığım özellikleri bulamadım ya da TortoiseGit’e zaten alışmış olduğum için ona geçemedim. Günlük ekranı https://tortoisegit.org/docs/tortoisegit/tgit-dug-showlog.ht... adresinde görülebilir
My Computergibi özel nesnelere ekleme yapabildiği entegre kabuk oldu. Bunun XP’de zirveye çıktığını, Vista’dan itibaren Microsoft’un genişletmeyi zorlaştıran bir yöne değiştirdiğini düşünüyorumYine de VC++6’daki bir menünün neden normal bir liste yerine klasör gibi görünen bir kabuk nesnesi oluşturup dosya açma iletişim kutusunda gösterdiğini hâlâ anlamıyorum. Projeye bileşen ekleyen bir sihirbazdı; iletişim kutusu açıkken o sahte klasörü
My Computeriçinde arardımfixupzatengit commit --fixupile ya da ondan öncegit rebase -iile mümkündü;reworddegit rebase -iile yapılabiliyorduHer commit için uygulanacak işlemi belirlemek üzere dosya düzenleme sürecinin rahatsız edici olduğuna dair yeterince geri bildirim birikmiş olmalı ki, mevcut işlevleri saran daha üst düzey bir komut eklenmiş gibi görünüyor
Komut satırı araçlarında bir sonraki komutu çalıştırdığınızda önceki çıktıya erişmek zorlaşır.
git logiçinde gördüğünüz bir commit’i cherry-pick yapmak isteseniz bile hash terminalde kalmamışsa ezberden giremezsiniz; oysa durumunu koruyan bir arayüz birden fazla etkileşimi paralel tutar ve önceki bilgileri saklarGit’te mevcut branch, önceki branch, değiştirilmiş dosyalar, push edilmemiş commit’ler gibi hatırlanması gereken çok fazla durum bilgisi var. Kabuk eklentisinin branch göstergesi ve otomatik tamamlama, birden fazla sekme/pencere bölme, sekmeler arası kopyalama, otomatik tamamlama araması gibi çevre araçlara alışamazsanız Git kullanımı çok tatsız hâle gelebilir
jj’nin pratikteki tek zayıflığının Git entegrasyonu olduğunu düşünüyorum. Dosya kümeleri, revizyon kümeleri ve şablonlar için alana özgü dilleri biçimlendirip yapılandırma ve CLI genelinde tutarlı biçimde kullanıyor; iç yapıyı doğrudan açığa çıkarmadan güçlü kavramları arayüz olarak sunuyorAncak fazla esnek olduğu için Git ile birlikte çalışabilirlik büyük ölçüde geleneklere ve disipline dayanıyor; neredeyse tüm
jjdurumları Git ile ifade edilebildiğinden uzak depoyu bozmak kolay. Bunu düzeltmek için Git’i derinlemesine anlamak gerekiyorYeni ve tek başına yürütülen bir geliştirme projesiyse
jjdenenmeye değer. Uygun koşullarda bakınca, Subversion’a kıyasla ilk dönem Git’i andırıyor; öncejj’nin kendi yöntemine alışmak, sonradan gelecek zorlu Git entegrasyonuna da biraz daha iyi hazırlanmayı sağlayabilir. İlgili bağlam https://news.ycombinator.com/item?id=48903054 adresindejjiçinde Git dışı backend’ler de var, ancak şu an üretim ortamına uygun olduğu açıkça belirtilen tek seçenek Git backend’i. Git entegrasyonu olmayanjjşimdilik gerçeklikten çok bir kavrama yakınjj’yi ve onun sürüm kontrolüne dair zihinsel modelini sevdiğim için kullanmaya devam edeceğim; ama bazen işi bitirmek için Git seviyesine inmem gerektiğinden, yeni bir sürüm kontrol sisteminden çok Git üzerinde kullanışlı bir sarmalayıcı gibi geliyor. Git,jjiş akışını destekleyen komutlar eklersejj’yi ayrıca kurmak için neden bulmak zorlaşırgit history fixup’ın tüm alt branch’leri otomatik rebase etme biçimi benim iş akışıma uymuyor. Özellik branch’inin her sürümünümyfeature.1,myfeature.2gibi saklayıp yeni sürümde davranış değiştiğinde eski sürümü buluyorum;git reflogbunun yerini tutamazÖrneğin sorun
myfeature.58ilemyfeature.59arasında ortaya çıktıysa ve her özellikte 15 commit varsa, temel commit farklı olsa bile iki sürüm arasındaki farkı commit bazındagit range-diff myfeature.58~15..myfeature.58 myfeature.59~15..myfeature.59ile görebilirim. Bu yüzden belirli bir commit’i içeren tüm branch’lerin otomatik rebase edilmesini istemiyorum; eski sürümler için tag kullanma yöntemi de bana pek uymadıGüncel Git, sık kullanılan üç
git rebase --interactiveişlemini daha az sürtünmeli ayrı komutlar olarak uygulamış ve yalnızca çakışma olmadığında çalışıyor gibi görünüyorgit history’nin gerçekten yararlı olup olmadığı şüpheli. Ben commit’leri interaktif rebase ile görsel olarak taşımayı tercih ediyorum ve mevcut yerel branch’te artık bulunmayan yetim bir commit hash’ini yanlışlıklagit historyhedefi olarak belirtmek istememGit geçmişini kusursuzca düzenlemek için neden bu kadar uğraşıldığını anlamıyorum. Nasıl olsa tek tek commit'leri tekrar okumuyoruz; birleştirmeden önce hepsini squash etmek yeterli
"oops"ve"fix"ile doluysa hemen geri çeviririmCommit'ler yapılan işin hikâyesini aktarır; bu yüzden inceleyen kişinin niyeti anlamak için daha fazla uğraşmasına gerek kalmasın diye okunabilir bir commit geçmişi sunmak gerekir
Kod tabanı ve özellikler çok küçükse hepsini squash etmek sorun olmayabilir; ama ben gerçek değişiklik akışını görebildiğim, commit'leri seçici olarak cherry-pick yapabildiğim veya geri alabildiğim bir yöntemi tercih ederim
Şu anda
Dcheckout edilmiş durumdaykenB'yiB'olarak değiştirdiğimizde,git rebase --update-refs'in rebase kapsamı dışındakiE'yi olduğu gibi bırakıp yalnızcaB' → C' → D'oluşturduğunu ve mevcutB → E'yi koruduğunu anlamıştımBuna karşılık
git history fixup,E'yi deE'olarak güncelliyor mu? Öyleyse onlarca yıldır elim alışmış olangit rebaseile tüm alt dalları güncellemenin bir yolu olup olmadığını merak ediyorumEolduğu gibi bırakılamaz.--update-refs'i bizzat denemedim ama dokümana göre sonuç üçüncü grafikteki gibiE'de oluşacak şekilde olmalı; yani istediğiniz davranış zatengit rebaseile mümkün