Git
Chapters ▾ 2nd Edition

7.6 Git Tools - Geçmişi Yeniden Yazma

Geçmişi Yeniden Yazma

Git ile çalışırken, yerel katkı geçmişinizi gözden geçirmek isteyebilirsiniz. Git’in harika özelliklerinden biri, son anda kararlar almanıza olanak tanımasıdır. İzlemde katkı öncesi hangi dosyaların hangi katkılara gideceğine karar verebilir, git stash ile bir şey üzerinde henüz üzerinde çalışmak istemediğinize karar verebilir ve zaten işlenmiş olan katkıları başka bir şekilde gerçekleşmiş gibi yeniden yazabilirsiniz. Bu, katkıların sırasını, mesajlarını veya bir katkıdaki dosyaları değiştirmeyi, katkıları birleştirmeyi veya ayırmayı veya işlenmiş katkıları tamamen kaldırmayı içerebilir - tüm bunları çalışmalarınızı başkalarıyla paylaşmadan önce yapabilirsiniz.

Bu bölümde, bu görevleri nasıl gerçekleştireceğinizi göreceksiniz, böylece çalışmanızı diğerleriyle paylaşmadan önce katkı geçmişinizin istediğiniz gibi görünmesini sağlayabilirsiniz.

Örnek 16. Çalışmanız sizi tatmin edecek seviyeye gelene kadar itmeyin

Git’in temel kurallarından biri, kopyanız içindeki çoğu şeyin yerel olması nedeniyle, geçmişinizi yerel olarak yeniden yazma özgürlüğünüzün olduğudur. Ancak, çalışmanızı gönderdikten sonra işler tamamen farklı olur ve onu değiştirmek için iyi bir nedeniniz yoksa, gönderilen işi öyle kabul etmelisiniz. Kısacası, çalışmanız sizi memnun edinceye kadar itmeyin ve hazır olana kadar dış dünya ile paylaşmayın.

Son Katkıyı Değiştirme

En son katkınızı değiştirmek, muhtemelen yapacağınız en yaygın tarih yazma işlemidir. Genellikle en son katkınız üzerinde iki temel şey yapmak istersiniz: sadece katkı mesajını değiştirmek ya da dosya ekleyip, kaldırarak veya değiştirerek katkının gerçek içeriğini değiştirmek.

Eğer sadece son katkı mesajınızı değiştirmek istiyorsanız, bu kolaydır:

$ git commit --amend

Yukarıdaki komut, önceki katkı mesajını bir düzenleyici oturumuna yükler; burada mesajı değiştirebilir, bu değişiklikleri kaydedebilir ve çıkabilirsiniz. Düzenleyiciyi kaydedip kapatırsanız, düzenleyici güncellenmiş katkı mesajını içeren yeni bir katkı yazar ve onu yeni son katkınız yapar.

Eğer son katkınızın içeriğini değiştirmek istiyorsanız, işlem temel olarak aynı şekilde çalışır: önce unuttuğunuz değişiklikleri yapın ve bu değişiklikleri izleme alın. Ardından çalıştıracağınız git commit --amend komutu; son katkınızı, yeni ve geliştirilmiş bir katkı ile değiştirir.

Bu teknikle dikkatli olmanız gerekmektedir çünkü amend komutu, katkının SHA-1 karmasını değiştirir. Bu, çok küçük boyutta bir yeniden temellemeye benzer - eğer çoktan uzak repoya ittiyseniz son katkınızı değiştirmeyin.

Örnek 17. Düzeltme yapılan bir katkı, bir düzeltilmiş katkı mesajına ihtiyaç duyabilir (veya duymayabilir).

İşlenmiş bir katkıyı düzelttiğinizde, hem katkı mesajını hem de katkının içeriğini değiştirme fırsatınız vardır. Eğer katkının içeriğini önemli ölçüde değiştiriyorsanız, düzeltilmiş içeriği yansıtacak şekilde katkı mesajını da güncellemelisiniz.

Öte yandan, düzeltmeleriniz yeterince küçükse (aptal bir yazım hatasını düzeltmek veya izleme almayı unuttuğunuz bir dosyayı eklemek gibi) ve önceki katkı mesajı tamamen uygunsuz değilse; basitçe değişiklikleri yapın, bunları izleme alın ve gereksiz yere düzenleyici oturumunu açmaktan kaçının:

$ git commit --amend --no-edit

Çoklu Katkı Mesajı Değişimi

Geçmişte daha geride olan bir katkıyı değiştirmek için daha karmaşık araçlara geçiş yapmanız gerekir. Git’in bir tarih değiştirme aracı yoktur, ancak rebase komutunu kullanarak bir dizi katkıyı başka bir yere taşımak yerine başlangıçta dayandıkları HEAD’e yeniden temelleyebilirsiniz. Etkileşimli temelleme aracı ile, değiştirmek istediğiniz her katkıdan sonra durabilir; mesajı değiştirebilir, dosyaları ekleyebilir veya istediğiniz her şeyi yapabilirsiniz. Etkileşimli temellemeyi git rebase komutuna -i seçeneğini ekleyerek çalıştırabilirsiniz. Hangi katkıyı yeniden temellemek istediğinizi belirtmek için, katkıların hangi katkıya yeniden temelleneceğini söyleyerek ne kadar geriye gitmek istediğinizi belirtmelisiniz.

Örneğin, son üç katkı mesajını veya bu gruptaki herhangi bir katkı mesajını değiştirmek istiyorsanız, git rebase -i komutuna düzenlemek istediğiniz son katkının öncelini (yani HEAD~2^ veya HEAD~3) argüman olarak sağlarsınız. ~3 'ü hatırlamak daha kolay olabilir, çünkü son üç katkıyı düzenlemeye çalışıyorsunuz, ancak aslında düzenlemek istediğiniz son katkının dört katkı öncesini belirlediğinizi unutmayın:

$ git rebase -i HEAD~3

Tekrar hatırlatmak gerekirse, bu bir yeniden temelleme komutudur - HEAD~3..HEAD aralığındaki değiştirilmiş bir mesajı olan her katkı ve tüm ardılları yeniden yazılacaktır. Merkezi bir sunucuya zaten gönderdiğiniz herhangi bir katkıyı dahil etmeyin; bunu yapmak, aynı değişikliğin alternatif bir sürümü yüzünden, diğer geliştiricileri kafasını karıştırabilir.

Bu komutu çalıştırmak, metin düzenleyicinizde şuna benzer bir katkı listesi almanızı sağlar:

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Bu katkılar, genellikle log komutunu kullanarak gördüğünüz sıralamanın tam tersi şekilde listelenmiştir. log komutunu çalıştırırsanız, şuna benzer bir şey görürsünüz:

$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d added cat-file
310154e updated README formatting and added blame
f7f3f6d changed my name a bit

Ters sıralı olduğuna dikkat edin. Etkileşimli yeniden temelleme, çalıştıracağı bir betik sağlar. Bu betik, komut satırında belirttiğiniz katkıyla (HEAD~3) başlar ve her bir katkının getirdiği değişiklikleri en üstten en alta yeniden işler. En eski olanı en üstte listeler, çünkü işlenecek olan ilk katkıdır.

Betiği, düzenlemek istediğiniz katkıya gelince duracak şekilde düzenlemeniz gerekir. Bunu yapmak için, betiğin durmasını istediğiniz her katkıdan sonra gelen yerdeki kelimeyi pick yerine edit olarak değiştirin. Örneğin, sadece üçüncü katkı mesajını değiştirmek için dosyayı şuna benzer bir şekilde değiştirirsiniz:

edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

Düzenleyiciyi kaydedip çıktığınızda, Git sizi listedeki son katkıya geri götürür ve aşağıdaki mesajı yazarak sizi komut satırına bırakır:

$ git rebase -i HEAD~3
Stopped at f7f3f6d... changed my name a bit
You can amend the commit now, with

       git commit --amend

Once you're satisfied with your changes, run

       git rebase --continue

Bu talimatlar size tam olarak ne yapmanız gerektiğini söyler. Şunu yazın:

$ git commit --amend

Katkı mesajını değiştirin ve düzenleyiciden çıkın. Ardından, şunu çalıştırın:

$ git rebase --continue

Bu komut diğer iki katkıyı otomatik olarak uygulayacak ve işiniz bitmiş olacak. Eğer daha fazla satırda pick yerine edit yazdıysanız, her biri için bu adımları tekrarlayabilirsiniz. Her seferinde Git duracak, katkıyı düzeltmenize izin verecek ve işiniz bittiğinde devam edecektir.

RKatkıları sıralama

Ayrıca, etkileşimli yeniden temellemeleri kullanarak katkıları yeniden sıralamak veya tamamen kaldırmak da mümkündür. Eğer added cat-file katkısını kaldırmak ve diğer iki katkının tanıtılma sırasını değiştirmek istiyorsanız, yeniden temelleme betiğini bundan:

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

şuna çevirebilirsiniz:

pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit

Düzenleyiciyi kaydedip, çıktığınızda; Git dalınızı bu katkıların önceline geri sarp, önce 310154e ve f7f3f6d 'yi uyguladı, ve ardından durdu. Bu şekilde, bu katkıların sırasını etkili bir şekilde değiştirir ve added cat-file katkısını tamamen kaldırırsınız.

Katkıları Sıkıştırmak

Etkileşimli yeniden temelleme yoluyla seriyi tek bir katkıya sıkıştırmak da mümkündür. Betik, yeniden temelleme mesajına faydalı talimatlar ekler:

#
# komutlar:
# p, pick <commit> = katkıyı kullan
# r, reword <commit> = katkıyı kullan, ama katkı mesajını düzenle
# e, edit <commit> = katkıyı kullan, ama değişikliği (amend) durdur
# s, squash <commit> = katkıyı kullan, ama bir önceki katkıyla harmanla
# f, fixup <commit> = "sıkıştırmak" (squash) gibi, ama katkının günlük mesajını sil
# x, exec <command> = komutu kabuk (shell) kullanarak çalıştır (hattın kalanında)
# b, break = burada dur ('git rebase --continue' ile daha sonra yeniden temellemeye devam et)
# d, drop <commit> = katkıyı kaldır
# l, label <label> = şu anki HEAD'i bir isimle etiketle
# t, reset <label> = HEAD'i bir etikete sıfırla
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       Özgün birleştirme katkı mesajını kullanarak bir birleştirme katkısı oluşturun
# .       (orijinal birleştirme katkısı belirtilmemişse tek satır)
# .       Katkı mesajını yeniden düzenlemek için `-c <katkı>` kullanın.
# .
# Bu satırlar yeniden sıralanabilir; bunlar üstten alta doğru çalıştırılır.
#
# Buradan bir satırı kaldırırsanız, O KATKI KAYBOLACAKTIR.
#
# Ancak, her şeyi kaldırırsanız, yeniden temelleme iptal edilecektir.
#
# Boş katkıların yorum satırı olarak işaretlendiğini unutmayın.

Eğer pick veya edit yerine squash belirtirseniz; Git hem o değişikliği, hem de direkt olarak ondan önce gelen değişikliği uygular ve sizen katkı mesajlarını birleştirmenizi ister. Eğer, bu üç katkıdan tek bir katkı yapmak istiyorsanız, betiği şu şekilde yazmanız gerekir:

pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file

Düzenleyiciyi kaydetdip çıktığınızda, Git tüm üç değişikliği uygular ve ardından üç katkı mesajını birleştirmeniz için sizi tekrar düzenleyiciye yönlendirir:

# This is a combination of 3 commits.
# The first commit's message is:
changed my name a bit

# This is the 2nd commit message:

updated README formatting and added blame

# This is the 3rd commit message:

added cat-file

Bunu kaydettiğinizde, önceki üç katkının değişikliklerini tanıtan tek bir katkınız olur.

Bir Katkıyı Bölme

Bir katkıyı bölmek, bir katkıyı geri alır ve sonra istediğiniz kadar katkı işlemenizi ve kısmen izlemlemenizi sağlar. Örneğin, üç katkınızın ortasındaki katkıyı bölmek istediğinizi varsayalım. updated README formatting and added blame katkısını; ilki updated README formatting ve ikincisi added blame olan iki katkıya bölmek istiyorsunuz. Bunu, rebase -i betiğinde, bölmek istediğiniz katkıdaki talimatı edit olarak değiştirerek yapabilirsiniz::

pick f7f3f6d changed my name a bit
edit 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

Sonra, komut dosyası sizi komut satırına bıraktığında, o katkıyı sıfırlarsınız; sıfırlanan değişiklikleri alır ve bunlardan birden fazla katkı oluşturursunuz. Düzenleyiciyi kaydedip çıktığınızda, Git listenizdeki ilk katkının önceline geri sarar, ilk katkıyı (f7f3f6d) uygular, ikinciyi (310154e) uygular ve sizi konsola bırakır. Burada, o katkının karışık sıfırlamasını git reset HEAD^ ile yaparsınız, bu da o katkıyı geri alır ve değiştirilmiş dosyaları izlemlenmemiş olarak bırakır. Şimdi, birkaç katkı elde edene kadar dosyaları izleme alabilir ve katkılayabilirsiniz, ve işiniz bittiğinde git rebase --continue komutunu çalıştırabilirsiniz:

$ git reset HEAD^
$ git add README
$ git commit -m 'updated README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'added blame'
$ git rebase --continue

Git, komut dosyasındaki son katkıyı (a5f4a0d) uygular ve geçmişiniz şuna benzer:

$ git log -4 --pretty=format:"%h %s"
1c002dd added cat-file
9b29157 added blame
35cfb2b updated README formatting
f3cc40e changed my name a bit

Bir kez daha, bu, listenizdeki tüm katkıların SHA-1 karmalarını değiştirir, bu nedenle paylaşılan bir repoya zaten işlediğiniz bir katkının bu listede görünmediğinden emin olun.

Nükleer Seçenek: süzme-dalı (filter-branch)

E-posta adresinizi global olarak değiştirmek veya her katkıdan bir dosyayı kaldırmak gibi birçok katkıyı betikle değiştirebilmenizi sağlayacak başka bir yeniden tarih yazma seçeneği daha var. filter-branch komutu, tarih geçmişinizin büyük bir bölümünü yeniden yazabilir; bu nedenle projeniz henüz halka açık değilse ve diğer insanlar yeniden yazacağınız katkılardan henüz yararlanmadıysa, muhtemelen kullanmamalısınız. Ancak, yine de çok faydalı olabilir. Bu komutla yapabilecekleriniz hakkında bir fikir edinmek için bazı yaygın kullanımları öğreneceksiniz.

Bir Dosyayı Her Katkıdan Kaldırma

Bu oldukça yaygın bir durumdur. Birisi düşünmeden git add . komutunu kullanarak büyük bir ikili dosyayı yanlışlıkla katkıya işler ve bu dosyayı her yerden kaldırmak istersiniz. Belki de yanlışlıkla parola içeren bir dosyayı katkıladınız ve projenizi açık kaynaklı hale getirmek istiyorsunuz. Tüm geçmişinizi temizlemek için muhtemelen filter-branch aracını kullanmak istersiniz. passwords.txt adlı bir dosyayı tüm geçmişinizden kaldırmak için filter-branch 'e --tree-filter seçeneğini ekleyebilirsiniz:

$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten

--tree-filter seçeneği, projenin her kontrol edilmesinden sonra belirtilen komutu çalıştırır ve ardından sonuçları yeniden katkı olarak işler. Burada, passwords.txt adlı dosyayı, olsa da olmasa da, her pozun içinden kaldırırsınız. Eğer kazara katkılanmış tüm düzenleyici yedek dosyalarını kaldırmak istiyorsanız, git filter-branch --tree-filter 'rm -f *~' HEAD gibi bir komut dizesi çalıştırabilirsiniz.

Git’in ağaçları ve katkıları yeniden yazmasını izleyebilecek ve ardından dal işaretçisini en sona taşıyabileceksiniz. Bunu bir test dalında yapmak ve sonucun gerçekten istediğiniz şey olduğuna karar verdikten sonra ana dalınızı hard-reset yapmak genellikle iyi bir fikirdir. Tüm dallarınızda filtre-branch komutunu çalıştırmak için komuta --all bayrağını ekleyebilirsiniz.

Bir Alt Dizini Yeni Kök Yapma

Diyelim ki başka bir kaynak kontrol sistemi üzerinden bir içe aktarma yaptınız ve hiçbir anlam ifade etmeyen alt dizinlere sahipsiniz (trunk, tags vb.). Her bir işlem için trunk alt dizinini yeni proje kökü yapmak istiyorsanız, filter-branch bunu yapmanıza yardımcı olabilir:

$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten

Artık yeni proje kökü, önceden trunk alt dizininde bulunmakta olan şeydir. Git ayrıca otomatik olarak alt dizini etkilemeyen katkıları da kaldıracaktır.

E-Posta Adresini Global Olarak Değiştirmek

Başka yaygın bir durum, çalışmaya başlamadan önce adınızı ve e-posta adresinizi ayarlamak için git config komutunu çalıştırmayı unutmanızdar. Veya belki de sadece işyerinde bir projeyi açık kaynak yapmak istiyorsunuz ve tüm iş e-posta adreslerinizi kişisel adresinizle değiştirmek istiyorsunuz. Her durumda, filter-branch ile birden çok işlemde e-posta adreslerini değiştirebilirsiniz. Sadece kendi e-posta adreslerinizi değiştirdiğinize dikkat etmelisiniz, bu yüzden --commit-filter kullanırsınız:

$ git filter-branch --commit-filter '
        if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
        then
                GIT_AUTHOR_NAME="Scott Chacon";
                GIT_AUTHOR_EMAIL="schacon@example.com";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD

Bu işlem, her bir katkıyı yeni adresinizle yeniden yazar. Çünkü katkılar, öncellerinin SHA-1 değerlerini içerir. Bu komut, sadece eşleşen e-posta adreslerine sahip olanları değil, geçmişinizdeki her katkının SHA-1 karmasını değiştirir.

scroll-to-top