Git
Chapters ▾ 2nd Edition

3.6 Git Dalları - Yeniden Temelleme (rebase)

Yeniden Temelleme (rebase)

Git’te, bir dalın değişikliklerini diğerine birleştirmenin iki ana yolu vardır: merge (birleştirme) ve rebase (yeniden temelleme). Bu bölümde, yeniden temellemenin ne olduğunu, nasıl yapıldığını, neden oldukça etkili bir araç olduğunu ve hangi durumlarda kullanmak istemeyeceğinizi öğreneceksiniz.

Kısaca Yeniden Temelleme

Eğer önceki örneğe, Birleştirme başlıklı bölüme geri dönerseniz, işinizi dallandırdığınızı ve iki farklı dalda katkı işlediğinizi görebilirsiniz.

Basit bir ayrışma geçmişi.
Görsel 35. Basit bir ayrışma geçmişi

Dalları birleştirmenin en kolay yolu, zaten ele aldığımız gibi merge komutudur. Bu komut, iki dalın (C3 ve C4) ve bunların en son ortak öncelinin (C2) son pozlarını üçyönlü birleştirerek, yeni bir poz (ve katkı) oluşturur.

Ayrık çalışma geçmişini birleştirmek.
Görsel 36. Ayrık çalışma geçmişini birleştirmek

Ancak, başka bir yol daha vardır: C4 ile tanıtılan değişikliğin yamasını alabilir ve bunu C3 'ün üzerine yeniden uygulayabilirsiniz. Git’te buna yeniden temelleme (rebasing) denir. rebase komutu ile bir dalda işlenmiş tüm değişiklikleri alabilir ve bunları farklı bir dala aktararak bir temel olarak kullanabilirsiniz.

Bu örnekte, experiment dalına geçer ve ardından onu şu şekilde master dalına aktarabilirsiniz:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

Bu işlem, iki dalın (üzerinde bulunduğunuz dal ve yeniden temellediğiniz dal) ortak önceline gitmesiyle başlar. Ardından, üzerinde bulunduğunuz dalın her katkısının getirdiği farkı alır ve bu farkları geçici dosyalara kaydeder. Mevcut dalı, üzerine yeniden temelleme yaptığınız dalın katkıları ile aynı katkıya sıfırlar ve son olarak her değişikliği sırayla uygular.

`C4` 'teki değişikliği `C3` üzerine temelleme.
Görsel 37. C4 'teki değişikliği C3 üzerine temelleme

Bu noktada, master dalına geri dönebilir ve bir ileri-sarma birleştirmesi (fast-forward merge) yapabilirsiniz.

$ git checkout master
$ git merge experiment
Master dalına ileri-sarma.
Görsel 38. Master dalına ileri-sarma

Şu anda, C4' tarafından işaret edilen poz, the merge example örneğinde C5 tarafından işaret edilen poz ile tam olarak aynıdır. Birleştirme çıktısında fark yoktur, ancak yeniden temelleme daha temiz bir geçmiş sunar. Eğer git log komutuyla temellenmiş bir dalın günlüğünü incelerseniz, doğrusal bir geçmiş görürsünüz. Başlangıçta eşzamanlı gerçekleşmiş olsa bile, tüm işin sırayla gerçekleştiği izlenimini verir.

Çoğu zaman bunu, katkılarınızın temiz bir şekilde bir uzak dala uygulanmasını sağlamak için yaparsınız, belki de bir süre üzerinde çalıştığınız ancak sürdürmediğiniz bir projede. Bu durumda, işinizi bir dalda yapar ve yamalarınızı ana projeye göndermeye hazır olduğunuzda işinizi origin/master üzerine yeniden temellersiniz. Bu şekilde, bakım yapan kişinin herhangi bir birleştirme çalışması yapmasına gerek kalmaz - sadece bir ileri-sarma veya temiz bir uygulama yapması yeterlidir.

Unutmayın ki, bu işlem sonunda elde ettiğiniz poz, ister bir yeniden temelleme işlemi için alınmış son temelleme katkısı, isterse birleştirme işlemi sonrası elde ettiğiniz son birleştirme katkısı olsun, aynı pozdur - farklı olan sadece tarihtir. "Yeniden Temelleme" değişiklikleri tanımlandıkları sırayla bir iş çizgisinden başka bir iş çizgisine aktarırken, "birleşim" uç noktaları alır ve bunları birleştirir.

Daha Detaylıca Yeniden Temelleme

Yeniden temelleme işlemi için hedef aldığınız dal dışında bir dala da temellemenizi uygulayabilirsiniz. Mesela Bir tematik daldan ayrılan başka bir tematik dalın geçmişi şeklindeki bir tarihçeyi ele alalım: Projeye sunucu tarafı işlevselliği eklemek için server adında tematik bir dal oluşturdunuz ve bir katkı işlediniz. Ardından, istemci tarafındaki değişiklikleri yapmak için bu daldan ayrılıp client dalında birkaç katkı işlediniz. Son olarak, sunucu dalına geri dönüp birkaç katkı daha işlediniz.

Bir tematik daldan ayrılan başka bir tematik dalın geçmişi.
Görsel 39. Bir tematik daldan ayrılan başka bir tematik dalın geçmişi

Diyelim ki, bir sürüm için kullanıcı tarafındaki değişiklikleri ana dalınıza birleştirmeye karar verdiniz, ancak sunucu tarafındaki değişiklikleri daha fazla test edilene kadar bekletmek istiyorsunuz. Bu durumda, sunucu tarafında olmayıp (C8 and C9) kullanıcı tarafında olan değişiklikleri alabilir ve onları git rebase komutunun --onto seçeneğini kullanarak master dalınıza yeniden temelleyebilirsiniz:

$ git rebase --onto master server client

Bu temel olarak şunu ifade eder: "client dalını al, bu dalın server dalından ayrıldığından bu yana yapılan yamaları belirle ve bu yamaları client dalında, sanki doğrudan master dalından temellenmiş gibi yeniden temelle." Biraz karmaşık gibi görünebilir, ancak sonuç oldukça etkileyicidir.

Tematik bir daldan yeni bir tematik dala yeniden temelleme.
Görsel 40. Tematik bir daldan yeni bir tematik dala yeniden temelleme

Artık master dalınızı ileri sarabilirsiniz (bkz. master dalını client dalındaki değişiklikleri içerecek şekilde ileri sarmak):

$ git checkout master
$ git merge client
`master` dalını `client` dalındaki değişiklikleri içerecek şekilde ileri sarmak.
Görsel 41. master dalını client dalındaki değişiklikleri içerecek şekilde ileri sarmak

Diyelim ki sunucu dalını da eklemeye karar verdiniz. git rebase <temel-dalı> <tema-dalı> komutunu çalıştırarak, öncesinde dalınızı değiştirmeden, sunucu dalını master dalına tekrar temelleyebilirsiniz. Bu komut, ilgili tema dalına (burada server) geçiş yapar ve onu ana dal üzerine (burada master) yeniden temeller.

$ git rebase master server

Bu, server dalını master dalının üzerine yeniden temelleme diyagramında gösterildiği gibi server çalışmanızı master çalışmanızın üzerine yeniden temeller.

`server` dalını `master` dalının üzerine yeniden temelleme.
Görsel 42. server dalını master dalının üzerine yeniden temelleme

Ardından, ana dalınıza (master) ileri sarabilirsiniz:

$ git checkout master
$ git merge server

Tüm çalışmalar birleştirildiği için artık client ve server dallarını silebilirsiniz, çünkü bu sürecin tüm geçmişi zaten Nihai katkı geçmişi şeklinde görünecektir:

$ git branch -d client
$ git branch -d server
Nihai katkı geçmişi.
Görsel 43. Nihai katkı geçmişi

Yeniden Temellemenin Riskleri

Yeniden temelleme getirdiği kolaylıkların yanında malesef bazı dezavantajları da içerir. Bu riskleri tek cümlede özetlemek mümkündür:

Repo dışında var olan ve üzerine çalışılmış olabilecek katkıları yeniden temellemeyin.

Eğer bu kılavuzu takip ederseniz, sorun yaşamazsınız. Ama anlatacağımız yönergelere uymazsanız, insanlar sizi sevmeyecek; dostlarınız ve aileniz tarafından hor görüleceksiniz.

Birşeyleri yeniden temellediğinizde, mevcut katkıları terk ediyor ve benzer ancak farklı olan yeni katkılar oluşturuyorsunuz. Eğer katkılarınızı bir yere iterseniz ve başkaları bunları çekip), üzerine çalışma yaparsa, ve daha sonra bu katkıları git rebase ile değiştirip tekrar iterseniz, çalışma arkadaşlarınız kendi çalışmalarını tekrar birleştirmek zorunda kalacak ve onların çalışmalarını kendi projenize çekmeye çalıştığınızda bir karışıklık ortaya çıkacaktır.

Herkese açık bir şekilde yayımladığınız çalışmayı yeniden temellemenin nasıl sorunlara yol açabileceğine dair bir örneğe bakalım. Merkezi bir sunucudan bir repo kopyaladığınızı ve ardından biraz çalışma yaptığınızı düşünelim. katkı geçmişiniz şu şekilde görünüyor:

Bir repo kopyala ve üzerine bazı çalışmalar temelle.
Görsel 44. Bir repo kopyala ve üzerine bazı çalışmalar temelle

Şimdi, başka biri birleştirme içeren bazı çalışmalar yapar ve bu çalışmayı merkezi sunucuya iter. Siz bunu çeker ve yeni uzak dalı kendi çalışmanıza birleştirirsiniz, geçmişiniz şöyle görünebilir:

Daha fazla katkı getir ve onları kendi çalışmana birleştir.
Görsel 45. Daha fazla katkı getir ve onları kendi çalışmana birleştir

Ardından, birleştirilmiş çalışmayı iten kişi, bunun yerine, geriye gidip çalışmaya yeniden temellemeye karar verir. Bunun için git push --force kullanarak sunucudaki geçmişin üzerine yazar. Siz de bu sunucudan çekerseniz, yeni katkıları getirirsiniz.

Birisi çalışmanıza temellediğiniz katkıları terkedip, yeniden temellenmiş katkıları iter.
Görsel 46. Birisi çalışmanıza temellediğiniz katkıları terkedip, yeniden temellenmiş katkıları iter

Şimdi her ikiniz de çıkmazdasınız. Eğer bir git pull yaparsanız, her iki geçmiş çizgisini de içeren birleştirme katkısı oluşturursunuz ve repo şu şekilde görünecektir:

Aynı çalışmayı tekrar yeni bir birleştirme katkısına dahil edersiniz.
Görsel 47. Aynı çalışmayı tekrar yeni bir birleştirme katkısına dahil edersiniz

Eğer geçmişiniz bu şekilde göründüğünde bir git log komutu çalıştırırsanız; aynı yazar, tarih ve mesajı taşıyan iki katkı göreceksiniz ki bu da kafa karıştırıcı olacaktır. Dahası, eğer bu geçmişi tekrar sunucuya iterseniz, tüm bu yeniden temellenmiş katkıları merkezi sunucuya tekrar eklemiş olursunuz ki bu insanların kafasını daha da karıştırabilir. Diğer geliştiricinin (en başta yeniden temelleme yaptığı için), C4 ve C6'nın geçmişte olmasını istemediğini varsayabiliriz.

Yeniden Temelleme Yaparken Dikkat Edilmesi Gerekenler

Eğer kendinizi böyle bir durumda bulunursanız, Git size yardımcı olabilecek bazı ek özellikler sunar. Eğer takımınızdaki birisi, yeniden temellediğiniz bir işin üzerine yazan değişiklikleri zorla iterse (force push), karşılaşacağınız gerçek sorun hangi kodun size ait olduğu ve hangisinin üzerine yeniden yazılan olduğunu bulmaktır.

Git, katkının SHA-1 sağlamasına ek olarak, yalnızca katkı ile tanıtılan yamaya dayalı bir sağlama da hesaplar. Buna ``patch-id`` denir.

Eğer üzerine yeniden yazılan işi çekerseniz ve bu işi meslektaşınızın yeni katkıları üzerine yeniden temellerseniz; Git genellikle sizin çalışmanızın hangisi olduğunu başarılı bir şekilde bulabilir ve bunları yeni dalın üzerine uygulayabilir.

Örneğin, önceki senaryoda, Birisi çalışmanıza temellediğiniz katkıları terkedip, yeniden temellenmiş katkıları iter noktasındayken birleştirme yerine git rebase teamone/master komutunu çalıştırırsak, Git şunları yapacaktır:

  • Dalımıza özgü işleri belirle (C2, C3, C4, C6, C7)

  • Hangilerinin birleştirme katkısı olmadığını belirle (C2, C3, C4)

  • Hangilerinin hedef dalda üzerine yeniden yazılmadığını belirle (C4, C4' ile aynı yama olduğu için sadece C2 ve C3)

  • Tüm bu katkıları teamone/master'ın üstüne uygula

Zorla itilmiş yeniden temelleme üzerine temelleme
Görsel 48. Zorla itilmiş yeniden temelleme (force-push rebase) üzerine temelleme

Bu sadece meslektaşınızın yaptığı C4 ve C4' katkılarının neredeyse tamamen aynı yama olması durumunda çalışır. Aksi takdirde, yeniden temelleme bunun bir kopya olduğunu anlayamaz ve başka bir C4 benzeri yama ekler (değişiklikler zaten en azından bir dereceye kadar mevcut olacağı için, bu muhtemelen düzgün bir şekilde uygulanamaz).

Bunu normal bir git pull yerine git pull --rebase komutunu çalıştırarak daha da basitleştirebilirsiniz. Veya bu durumda bir git fetch ve ardından git rebase teamone/master komutu kullanarak manuel olarak da gerçekleştirebilirsiniz.

Eğer git pull kullanıyorsanız ve --rebase varsayılan olsun istiyorsanız, git config --global pull.rebase true gibi bir komutla pull.rebase yapılandırmasını ayarlayabilirsiniz.

Eğer sadece kendi bilgisayarınızdan hiç ayrılmamış katkıları yeniden temelliyorsanız, sorun yaşamazsınız. Eğer itilmiş ancak kimsenin bunlardan temellemediği katkıları yeniden temelliyorsanız, yine sorun yaşamazsınız. Ancak eğer zaten herkese açık bir şekilde itilmiş ve birileri bu katkılardan temelleme yapmışsa, o zaman sıkıntılı bir durumla karşılaşabilir ve takım arkadaşlarınızın tepkisini çekebilirsiniz.

Eğer siz veya bir meslektaşınız bunu gerekli görürse, bu sıkıntıyı biraz daha basitleştirmeye çalışmak için, herkesin git pull --rebase komutunu çalıştırmayı bildiğinden emin olun.

Yeniden Temelleme ve Birleştirme

Yeniden temelleme ve birleştirmeyi öğrendiğinize göre hangisinin daha iyi olduğunu merak ediyor olabilirsiniz. Bunu yanıtlayabilmek için biraz geriye gitmek ve geçmişin ne anlama geldiğini konuşmak gerekiyor.

Bu konudaki bir bakış açısı, repo katkı geçmişinin aslında nelerin gerçekleştiğinin bir kaydı olduğudur. Bu oynanmaması gereken, çok değerli ve tarihi bir belgedir. Bu açıdan bakıldığında, katkı geçmişini değiştirmek neredeyse şirktir. Çünkü tarihi tahrif ederek, gerçekte neler olduğu hakkında yalan söylemiş olursunuz. Yani karışık bir dizi birleştirme katkısı varsa ne olmuş ki? İşte öyle olduysa bile repo bunu gelecek nesiller için korumalıdır.

Karşıt bakış açısı, katkı geçmişinin projenizin nasıl yapıldığını anlatan bir hikaye olduğudur. Bir kitabın ilk taslağını yayımlamazsınız ve yazılımınızı nasıl sürdüreceğiniz konusundaki kılavuz, dikkatli bir düzenlemeyi hak eder. Bu görüştekiler "yeniden temelleme" ve "dal filtreleme" gibi araçları kullanarak hikayeyi gelecekteki okuyucular için en iyi şekilde anlatan kişilerdir.

Şimdi, birleştirmenin mi yoksa yeniden temellemenin mi daha iyi olduğu sorusuna gelince: bunun o kadar da basit bir soru olmadığını anladığınızı ümidediyorum. Git geçmişinizle ilgili birçok şey yapmanıza izin veren güçlü bir araçtır, ancak her ekip ve her proje de farklıdır. Her iki işlemin de nasıl çalıştığını bildiğinize göre, hangisinin sizin özel durumunuz için en iyi olduğuna siz karar vereceksiniz.

Genel olarak, her iki yaklaşımdan da en iyi şekilde faydalanmanın yolu, henüz paylaşmadığınız yerel değişiklikleri itmeden önce yeniden temelleme yaparak geçmişi temizlemektir, ancak herhangi bir sunucuya ittiğiniz bir şeyi asla yeniden temelleme yapmamaktır.

scroll-to-top