Git
Chapters ▾ 2nd Edition

7.13 Git Araçları - Git Nesnesini Değiştirme

Git Nesnesini Değiştirme

Daha önce vurguladığımız gibi, Git’in veritabanındaki nesneleri değişmezdir, ancak Git veritabanındaki nesneleri başka nesnelerle değiştiriyormuş gibi yapmak için ilginç bir yol sunar.

replace komutu, Git’te bir nesneyi belirtmenize ve "bu nesne her çağrıldığında, o sanki farklı bir nesneymiş gibi davran" demenize izin verir. Bu, özellikle tüm geçmişi yeniden oluşturmak zorunda kalmadan, geçmişinizdeki bir katkıyı başka bir katkı ile değiştirmeniz gerektiğinde (ör. git filter-branch) çok kullanışlıdır.

Örneğin, büyük bir kod geçmişiniz var ve bu geçmişi yeni geliştiriciler için kısa bir geçmiş ve veri madenciliğiyle ilgilenen kişiler için çok daha uzun ve büyük bir geçmişe bölmek istiyorsunuz. Yeni tarih çizginizdeki en erken katkıyı, eski olanın en son katkısıyla "değiştirerek" bir geçmişi diğerine ekleyebilirsiniz. Bu, genellikle onları birleştirmek için yapmanız gerektiği üzere (çünkü öncellik SHA-1’leri etkiler), yeni geçmişteki her katkıyı yeniden yazmanızı gerektirmeyeceği için güzel bir yöntemdir.

Hadi bunu deneyelim. Mevcut bir repoyu alalım, onu biri kısa ve yeni, diğeri ise daha uzun, köklü bir proje geçmişine sahip olan iki repoya bölelim. Ve sonra bu geçmişleri replace kullanarak, yeni repoların SHA-1 değerlerini değiştirmeden nasıl birleştirebileceğimize bakalım.

Bunu beş katkısı olan basit bir repo üzerinde deneyelim:

$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Bu repoyu iki tarih çizgisine bölmek istiyoruz. Bir çizgi, birinci katkıdan dördünca katkıya kadar gider (bu uzak geçmiş olacaktir). İkinci çizgide sadece dört ve beş numaralı katkılar olacaktır (bu yakın geçmiş olacaktır).

replace1

Tarihi geçmişi oluşturmak kolaydır, geçmişe bir dal ekleyebilir ve sonra bu dalı yeni bir uzak repo’nun ana dalına (master dalına) itebiliriz.

$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
replace2

Şimdi, yeni history dalını yeni repomuzun master dalına itebiliriz:

$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
 * [new branch]      history -> master

Artık, geçmişimiz yayınlandı. Şimdi geriye işin daha zor olan kısmı, yani yakın tarihli geçmişimizi kırpmak kaldı. Bir yerde örtüşme olması gerekiyor ki birindeki bir katkıyı, diğerindeki eşdeğer bir katkı ile değiştirebilelim. Bu yüzden bu geçmişi sadece dört ve beş numaralı katkılara indirgiyoruz (böylece dört numaralı katkı örtüşecektir).

$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Bu durumda, tarihçenin nasıl genişletileceği hakkında talimatları olan bir temel katkı oluşturmak faydalıdır. Böylece diğer geliştiriciler, kırpılmış geçmişin ilk katkısına ulaşırlar ve daha fazlasına ihtiyaç duyarlarsa ne yapacaklarını bilirler. Bu yüzden yapacağımız şey, talimatlar içeren bir başlangıç katkısı oluşturmak ve sonra geriye kalan katkıları (dört ve beş) bunun üzerine yeniden düzenlemektir.

Bunu yapmak için, bölme noktası olmak üzere bir nokta belirlememiz gerekiyor ki bu bizim için üçüncü katkı olacaktır: yani SHA dilinde 9c68fdc dir. Bu yüzden temel katkımız o ağaç üzerinde olacaktır. Sadece bir ağaç alıp, bize öncelsiz ve yepyeni bir katkı nesnesi SHA-1’i veren commit-tree komutunu kullanarak, temel katkımızı oluşturabiliriz.

$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Not

commit-tree komutu, genellikle plumbing (tesisat) komutları olarak adlandırılan bir dizi komuttan biridir. Bunlar, genellikle doğrudan kullanılmak için değil, daha küçük işleri yapmak için diğer Git komutları tarafından kullanılırlar. Bu tür sıradışı durumlarda, günlük kullanımdan daha ziyade gerçekten düşük seviyeli şeyler yapmamıza izin verirler. Tesisat komutları hakkında daha fazla bilgiyi Tesisat ve Döşeme (Plumbing ve Porcelain) bölümünde bulabilirsiniz.

replace3

Artık temel bir katkımız olduğuna göre, geriye kalan tarihçemizi bunun üzerine git rebase --onto komutu ile tekrar düzenleyebiliriz. --onto argümanı, commit-tree ile aldığımız SHA-1; ve üçüncü katkımız (saklamak istediğimiz ilk katkının önceli olan, 9c68fdc) geriye dönük temel noktamız olacaktır:

$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
replace4

Artık, yeniden oluşturulabilir bir tarihçe hakkında talimatlar içeren temel katkımızı işleyip, üzerine yakın geçmişimizi yeniden yazdık. Eğer bu yeni geçmişi yeni bir projeye gönderirsek; artık insanlar o repoyu kopyaladıklarında, en son iki katkıyı ve sonra da talimatları içeren temel katkıyı göreceklerdir.

Şimdi rolleri değiştirerek, projeyi ilk kez kopyalayan ve tüm tarihçeyi isteyen birinin yerine geçelim. Bu kırpılmış repoyu kopyaladıktan sonra tarihçeyi almak için, tarihi (uzak geçmişli) repomuz için ikinci bir uzak repo ekleyip, çekmemiz lazım:

$ git clone https://github.com/schacon/project
$ cd project

$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah

$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
 * [new branch]      master     -> project-history/master

Şimdi çalışma arkadaşımızın en son katkıları master dalında ve tarihi (uzak geçmişli) katkıları project-history/master dalında olacaktır.

$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah

$ git log --oneline project-history/master
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Onları birleştirmek için, sadece değiştirmek istediğiniz katkıyı ve yerine koymak istediğiniz katkıyı belirterek git replace komutunu çağırabilirsiniz. Burada, master dalındaki "dördüncü" katkı, project-history/master dalındaki "dördüncü" kattı ile değiştirmek istiyoruz.

$ git replace 81a708d c6e1e95

Şimdi, master dalının geçmişine baktığınızda, şöyle görünmektedir:

$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit

Harika, değil mi? Üst akımdaki tüm SHA-1’leri değiştirmeden, geçmişimizdeki bir katkıyı tamamen farklı bir katkı ile değiştirebildik ve tüm normal araçlar (bisect, blame, vb.) onlardan beklediğimiz gibi çalışacaktır.

replace5

İlginçtir ki, 81a708d hala SHA-1 olarak görünüyor, ancak aslında onu değiştirdiğimiz c6e1e95 katkısının verilerini kullanıyor. cat-file gibi bir komutu çalıştırsanız bile, değiştirilmiş verileri görürsünüz:

$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700

fourth commit

Unutmayın ki, 81a708d katkısının asıl önceli, burada belirtildiği gibi 9c68fdce değil, yer tutucu katkınızdır (622e88e).

Bir başka ilginç nokta da bu verilerin referanslarımızda tutulmasıdır:

$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/replace/81a708dd0e167a3f691541c7a6463343bc457040

Bu, değiştirdiğimiz veriyi başkalarıyla paylaşmanın kolay olduğu anlamına gelir. Çünkü bunu sunucumuza yükleyebiliriz ve diğerleri de kolayca indirebilir. Burada ele aldığımız tarihçe aşılaması senaryosu (herkes zaten her iki tarihçeyi de indirebilecekse, onları niye ayırdık ki?) çok yardımcı olmasa da, diğer bazı durumlarda faydalı olabilir.

scroll-to-top