Git çakışmalarını (conflict) gidermenin etkili yolları

Yaklaşık 2.5 sene önce şirketçe TFS’ten GIT’e geçiş yapmıştık. Dolayısıyla, birlikte çalıştığım hiç kimse bu sürüm yöneticisine aşina değildi. TFS’in basitliğinden sonra gereksiz karmaşık gelmişti bana ne yalan söyleyeyim. Bir süre sonra çakışmalar başımızı ağrıtmaya başladı. Bu çakışmalar genelde bir yazılımcının öbür yazılımcının yazdıklarının üzerine yazılmasıyla sonuçlandı. Kimileri de “Arkadaşlar bu dosyayla uğraşan var mı? Önümüzdeki 2 saat boyunca ellemeyin, bir şey güncelleyeceğim” gibi işe yarayan ancak yalnızca günü kurtarmaya yarayan çağ dışı tekniklere yöneldi. Kimileri ise kodlaması 2 saat süren işi git’e işlemek için “bir 2 saat” daha uğraştı.

Gel zaman git zaman GIT sürüm yöneticisine aşina olmaya başladık. Ya da biz öyle olduğunu sandık. Git hakkında neredeyse hiçbir şey bilmediğimi anlamam için işimi değiştirmem gerekecekti. PR (pull request) denen çekme isteklerinin art arda geldiği, evden çalışların olduğu dolayısıyla tüm iletişimin git üzerinden olduğu bir şirkete geçtiğimde kod yazarken bir problem yaşamadım. Ancak, ne zaman ki kodumu sisteme göndermem gerekti işte o zaman büyük stres yaşamaya başladım. Kendi özellik dalımı (feature branch) yarattıktan sonra, kodlamayı bitirip ana kod deposuna (genelde master) birleştireceğim zaman gelinceye kadar üzerinde çalıştığım dosyalar çoktan değişmiş oluyordu. Haliyle çekme isteklerim (PR) uzun süre askıda kalıyordu. Kod yazmaktan çok, git ile uğraşmak beni daha çok yoruyordu. Ancak bu sektörde öğrendiğim pek çok şeyi, aslında sorun yaşadıktan sonra öğrendim. İnternet'teki alıştırmalar genellikle en iyi durum senaryolarına göre hazırlanır. Sizin gerçek hayatta karşılaştığınız sorunlara ise ancak StackOverflow gibi gerçek insanların gerçek sorunlarını tartıştığı yerlerde rastlayabilirsiniz. Ben de sorunlarımı bu sitenin yardımıyla çözmeye başladım.

Git çok kullanışlı bir araç. Ancak çok kapsamlı olması, TFS gibi basit araçlardan geçiş yapanları canından bezdirebiliyor. Hemen herkesin aynı problem için farklı yöntemleri var. Büyük kod yığınlarından ziyade, küçük küçük fakat daha sık kod girişi yapılan bir takımla çalıştığım için aşağıdaki yöntemleri ben çok kullanışlı buldum.

Öncelikle kullandığımız anahtar komutları sıralayalım:

  1. stash: Değişikliklerimizi depoya göndermeden saklama işini yapan komut.
  2. rebase: Dallar(branch) arasında kodları eşitlemeye yarayan komut. merge gibi ancak ters yönde çalışıyor.

Web uygulamamıza profil sayfası ekleyeceğimizi varsayalım. Kendi özellik dalımızı(feature branch) yaratalım ve çalışmaya başlayalım.

git branch profil-sayfasi

git checkout profil-sayfasi

Bu dal üzerinde belirli bir süre çalıştık diyelim. Ancak bu süreç içerisinde takım arkadaşlarımız master üzerine kendi özellik dallarını birleştirdiler. Artık master o eski bildiğimiz master değil. Eğer aynı dosya üzerinde çalışılmadıysa master ne kadar çok değişirse değişsin herhangi bir çalışma sorunu olmayacaktır. Hatta aynı dosya üzerinde bile çalışsanız, çoğu zaman git çakışma yaratmadan değişiklikleri birleştirecektir. Ancak kimi zaman aynı satırlar üzerinde değişiklikler yaptığınız zaman, git doğal olarak bunu nasıl birleştireceğini bilemiyor. Bu durumda seçimi size bırakmak üzere “conflict” yani çakışma uyarısı veriyor. Çakışma olan bir özellik dalını master ile birleştiremezsiniz. Ancak panik yapılacak bir durum yok.

Bu durumun önüne 2 şekilde geçebiliriz:

  1. Çakışma gerçekleşmeden önce
  2. Çakışma gerçekleştikten sonra

1. Çakışma gerçekleşmeden önce

Çakışma gerçekleşmeden önce bunu önlemenin en iyi yolu, üzerinde çalıştığımız özellik dalını sıklıkla master üzerinden rebase komutuyla güncelleştirmektir. Bunu alışkanlık haline getirmek gerekir. Örneğin ben güne başlamadan önce çayımı yudumlarken bir kez rebase yaparım. Hatta çoğu zaman öğle yemeğinden sonra, Türk kahvemi yudumlarken bir tane daha yaparım. Ve son olarak çekme isteği(PR) yaratmadan önce bir kez daha yaparım. Çoğu durumda daha sık rebase işlemi yaparım. Böylelikle çakışma şansını minimuma indiririm. Ancak çakışma ihtimaline karşılık da rebase işlemi öncesinde kodumu stash ile koruma altına alırım.

git add -A

git stash

git checkout master

git pull

git checkout profil-sayfasi

git rebase master

git stash apply

Her seferinde sorunsuz ya da en az sorunla çalışan git komut kümesi benim için budur. Ne yaptığımızı açalım:

git add -A : Üzerinde çalıştığım tüm dosyaları stash edeceğimi bildiriyorum. Eğer bunu kullanmazsanız “untracked” olarak işaretli dosyalarınız (yeni eklediğiniz dosyalar) stash ile kaydedilmeyecektir.

git stash: Çalıştığımız dosyaları daha sonra kullanılmak üzere saklıyor. TFS’te “shelve” komutu vardı. Ona benziyor ancak stash değişiklikleri yerel deponuzda saklıyor. Sunucuya göndermiyor. Bu komutu kullandıktan sonra tüm değişikliklerinizin kaybolduğunu göreceksiniz. Ancak telaş etmeye gerek yok. Bunu zaten bilerek yaptık. Tüm kodumuzu sakladık. “git stash apply” ya da “git stash pop” diyerek tekrar yükleyebiliriz. “apply” kodu tekrar yüklerken stash üzerindeki kopyayı silmiyor. “pop” kodu geri yüklüyor ve stash üzerinden kaldırıyor.

git checkout master: master’a geçiş yaptık. Büyük ihtimalle çalışkan iş arkadaşlarımız bu depoyu en az bir kaç kez güncellemiştir. git status diyerek bunu öğrenebilirsiniz. Ya da direk pull işlemine geçebilirsiniz.

git pull: Şu anda master üzerinde olduğumuz için master’ı son haline güncelliyoruz.

git checkout profil-sayfasi: Üzerinde çalıştığımız özellik dalına tekrar geçiş yaptık. Rebase işlemine hazırız.

git rebase master: master ve profil-sayfasi’nı eşitliyoruz bu komutla. Normalde rebase korkulu bir komuttur ve git’e aşina olmayanlar bu komuttan kaçınır. Aslında ben de pek sevmezdim. Ancak ikinci adımda “stash” kullanmamızın sebebi rebase’in çakışmalarının önüne geçmek (aslında ertelemek). stash kullandığımda zaten tüm değişikliklerimi kaldırmıştım. Dolayısıyla bu adımda hiçbir çakışma gerçekleşmeyecek. Böyle bir durum varsa eğer son adımda gerçekleşecek.

git stash apply: Şu adıma kadar master ve profil-sayfasi birbirine eşit hale geldi. Şimdi, daha önce kaldırdığım değişikliklerimi profil-sayfasi’na tekrar almam gerekiyor. Ben genellikle pop yerine apply kullanıyorum. İşim bittikten sonra “git stash clear” ile tüm stash’i temizliyorum. Neyse, apply komutunu kullandıktan sonra master ile eşit hale gelen profil-sayfasi dalına, yaptığım tüm değişiklikleri ekliyorum.

DİKKAT: stash apply işleminden sonra çakışma gerçekleşebilir. Bu durumda çakışma olan dosyayı elle düzeltmemiz gerekiyor. Ben vscode kullanıyorum ancak herhangi bir editör hatta Notepad ile bile bunu yapabiliriz. Genellikle çakışmanın sebebi iş arkadaşınızla aynı satır üzerinde değişiklikler yapmanızdır. Hangi değişikliği seçeceğinize karar veremiyorsanız elbette bunu iş arkadaşınızla tartışmanız gerekir. Çakışmayı giderdikten sonra bunu git’e de bildirmeniz gerekiyor. Bunun için özel bir komut yok.

git reset

Bu işlemden sonra eğer vscode kullanıyorsanız “Source Control” sekmesindeki “C” (Conflict) ikonunun “M”’ye (Modified) döndüğünü göreceksiniz. Yani çakışma giderildi ve çekme isteği yapmaya hazırız.

2. Çakışma gerçekleştikten sonra

Ancak bazı durumlarda PR yaratıldıktan sonra başka PR’lar da master ile birleştirilmiş olabilir. Bu durumda da çakışmalar gerçekleşebilir. Bu durumda ise bir kaç tane ekstra komuta ihtiyacım var.

git reset HEAD~1

Bu işlem ile yaptığım değişiklikleri geri alıyorum. Burada dikkat etmemiz gereken “HEAD~1" seçeneği. Ben genelde tek commit ile işimi görüyorum. Anca eğer PR yaratmadan önce 2 commit yaptıysak “git reset HEAD~2” komutunu (dolayısıyla 3, 4… için de benzer şekilde)kullanmamız gerekiyor. Daha sonra yukarıda çakışma gerçekleşmeden önce uyguladığım tüm adımları tek tek tekrar uyguluyorum. Daha sonra:

git add -A

git commit -m “Çakışma giderildi.”

git push --force

Force ilk bakışta biraz ürkütücü gelebilir ancak eğer ne yaptığınızı biliyorsanız, force komutu aslında çok kullanışlı bir komut. Reset komutunu kullandıktan sonra üzerinde çalıştığımız yerel kod, sunucudaki kodun 1 commit gerisinde kaldı. Git sizin “pull” etmenizi bekliyor. Ancak pull ile bu kodu çekersek edersek tekrar başa dönmüş olacağız. “Pull” etmeden “push” komutunu kullanmaya kalkarsak da git buna izin vermeyecek. Git’e “sen rahat ol, ben ne yaptığımın farkındayım” anlamına gelen --force seçeneğini kullanmamız gerekiyor. Bu adımdan sonra çakışma tekrar giderildi. Şimdi yapmam gereken takım arkadaşlarıma kodumu inceletmek (code review) ve sonrasında özellik dalımı master üzerine “merge” etmek. Bitbucket ya da GitLab gibi sistemler kullanıyorsanız PR yaratıldıktan sonra “Merge” işlemini gerçekleştiren düğmenin olduğunu göreceksiniz. Ancak eğer bu tip bir sistem kullanmıyorsanız

git checkout master

git merge profil-sayfasi

komutlarını kullanabilirsiniz.

Bitirirken

Dediğim gibi, git üzerinde aynı sorunu çözen onlarca değişik yaklaşım var. Benim için en kolay ve anlaşılır yol yukarıda bahsettiğim şekli ile oldu. Sizin daha farklı yöntemleriniz olabilir. Ancak her halükarda sıklıkla “rebase” yapmak hem yazılımcının hem de iş arkadaşlarınızın yararına olacaktır.

Cüneyt Aliustaoğlu

Written by

http://cuneyt.aliustaoglu.biz/

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade