Git ve Feature Branchler

Tolgahan Tutar
Huawei Developers - Türkiye
9 min readJan 13, 2023
Kaynak

Giriş

Merhaba, günümüz yazılım dünyasında artık projeler kalabalık ekipler halinde geliştiriliyor ve ekipteki her bir geliştirici belirli görevler doğrultusunda projeye bazı özellikler ekleyebiliyor. Bir geliştirici bir özellik üzerinde çalışırken yine başka bir geliştirici aynı projede farklı bir özellik üzerinde çalışabiliyor ve birbirlerini beklemeden istedikleri zamanda bu özellikleri projenin reviewerlarına gönderebiliyorlar. Geliştirmeler yapıldıktan sonra artık proje bir ürün haline getirilirken projede minimal düzeyde hata olması bekleniyor ve bu hataların da ürün canlı bir şekilde marketteyken hızlıca çözülmesi bekleniyor.

İşte bütün bu süreçleri optimal şekilde ilerletebilmek için yazılım dünyasının bizlere sunmuş olduğu bazı toollar ve yöntemler mevcut. Bu makalede bu süreçlerin optimize edilmesi için var olan sistem ve yöntemlerden bazıları olan Versiyon Kontrol Sistemi, GitHub ve Feature Branchlerden bahsedicem.

Versiyon Kontrol Sistemleri

Versiyon kontrol sistemleri, proje üzerinde yapılan tüm işleri adım adım kaydeder ve bunun sayesinde projeyi geçmişe yönelik okuyabiliriz, ayrıca ihtiyaç olunması halinde projenin istenen bir aşamasına kolayca geri dönebiliriz. Üstelik birden fazla geliştiricinin aynı proje üzerinde geliştirme yapmasına olanak sağlayarak işbirliğini de kolaylaştırır.

Bu makalede bahsedeceğim Github, günümüzde en popüler versiyon kontrol sistemi olan Git’i kullanır.

Versiyon Kontrol Sisteminlerinin Çalışma Prensibinin Özeti

Versiyon Kontrol Sistemlerinin İş Akışı

Çoğu geliştiricinin kod geliştirirken versiyon kontrol sistemleri üzerinde kullandığı belirli adımlar mevcuttur. Bu adımları 4 maddeye ayırmak gerekirse bunlar;

1- Uzak sunucudaki proje bilgisayarımızda yoksa projeyi uzak sunucudan kendi localimize çekmeliyiz.

2- Kod üzerinde bir bug fixleyeceksek veya bir özellik ekleyeceksek bu doğrultuda değişiklikler yapmalıyız.

3- Kod üzerindeki işimiz bittikten sonra kodu incelemeye yollamalıyız.

4- Reviwerlar kodu inceledikten sonra kod üzerinde herhangi bir problem yoksa, kodumuzu ana koda eklemeli.

Git’in İş Akışı

Bir versiyon kontrol sistemi olan Git’in iş akışı da yine versiyon kontrol sistemlerinin iş akışlarıyla çok benzer fakat burada Git’in kendine özgü terminolojisi ve komutları mevcut. Bu akışı da 5 adımda inceleyebiliriz;

1- Yapılacak olan değişiklik için bir branch oluşturmalıyız ve bu branch’e makalenin ilerleyen kısımda bahsedeceğim isimlendirme standartları çerçevesinde bir isim vermeliyiz.

2- Branch üzerinde yapmış olduğumuz değişiklikleri commitlemeliyiz.

3- Bu branchi uzak sunucudaki repository’e pushlamalıyız.

4- Reviewerlar tarafından branchimizin incelenmesi için pull request oluşturmalıyız.

5- Pull request üzerinde herhangi bir conflict veya düzeltilmesi gereken bir yer varsa bunları düzeltip, pull requestimizi tamamlamalıyız.

Localdeki bir projeyi GitHub’a yükleme

Şimdi lokalimizde sıfırdan bir proje oluşturup bu projeyi GitHub’a nasıl yükleyebileceğimizden bahsedicem.

Ben projemi Android Studio’da geliştireceğim için ilk olarak Android Studio üzerinden bir proje oluşturuyorum. Projemin adı Git And Feature Branches.

Android Studio Üzerinde Projemizi Oluşturuyoruz

Daha sonra GitHub üzerinde bu projeyi saklayacağım repository’i oluşturuyorum ve repositoryimin ismini de yine Git and Feature Branches olarak belirliyorum. Burada isimlendirme konusunda tamamen özgürsünüz fakat tutarlı bir yapı olması açısından oluşturduğunuz projeyle aynı isimde olmasını tavsiye ederim.

GitHub Üzerinden Projeyi Saklayacağımız Repositoryi Oluşturuyoruz

Görselde de göreceğiniz üzere ben ilk aşamada conflict çıkmaması adına bir README dosyası eklemiyorum. Eğer README dosyasını eklersek repositoryimizde otomatik olarak bir history oluşmuş oluyor ve biz projemizi ilk sefer GitHub’a yüklemek istediğimizde GitHub bizden projenin önceki historysini silmemizi istiyor, bu da karışıklığa yol açıyor. Bu sebepten ötürü projeyi ilk oluştururken README eklemiyorum.

Create Repository butonuna bastıktan sonra artık GitHub üzerinde de projem için bir repository oluşturmuş oluyorum. Şimdi tekrar Android Studio’ya dönüp projeme Git’i initialize etmek için git init komutunu terminale yazıyorum.

Projemize Git’i Initialize Ediyoruz

Daha sonra git add . komutunu çalıştırıp projedeki tüm dosyaları repositoryime yollamak istediğimi belirteceğim fakat bunun öncesinde önemli bir konu olan .gitignore dosyasından ve ne işe yaradığından kısaca bahsetmek istiyorum.

Gitignore dosyası içerisine localimizde bulunup uzak sunucuya göndermek istemediğimiz dosyaların proje içerisindeki path’ini giriyoruz. Bu dosyalar şifreleme dosyaları, jks dosyaları veya API key içeren dosyalar olabilir. Projenin sağlıklı bir şekilde geliştirilmesi için bu tür dosyaları güvenilir bir şekilde saklayıp ekipteki developerlara vermeliyiz. Default olarak projemizin gitignore dosyası aşağıdaki gibi gözüküyor:

gitignore

Ben örnek olması açısından projeme Huawei AppGallery üzerinde geliştirme yaptığım bir projemin agconnect-services.json dosyasını ekleyeceğim ve bu dosyanın pathini gitignore’a yazıp uzaktaki repositorye eklenmemesini sağlayacağım.

agconnect-services.json Dosyasını Projeye Ekleyip gitignore İçerisine Dosya Yolunu Yazıyorum.

.gitignore dosyasıyla işim bittikten sonra artık git add . komutunu çalıştırıyorum ve dosyalarımı commit için hazırlıyorum. Daha sonra ilk commit için gerekli komutu yazıyorum:

git commit -m "initial commit"

Şu anda proje dosyalarımız commit’e eklendi ve pushlanmaya hazır hale geldi. Fakat push komutunu çalıştırmadan önce Android Studio’nun bu projeyi nereye pushlayacağını bilmesi gerekiyor. Bunun için GitHub’ı açıyoruz ve projemizin URL’ini kopyalıyoruz. Daha sonra tekrar Android Studio’ya dönüp aşağıdaki komutu çalıştırarak localdeki projemizle uzaktaki repository’i eşleştirmiş oluyoruz.

git remote add origin URL

Bu aşamadan sonra da aşağıdaki komutu çalıştırıp artık localdeki projemizi GitHub’a göndermiş oluyoruz.

Projemizi GitHub’a Ekledik

Artık feature branchler konusuna geçebiliriz ama geçmeden önce ufak bir bilgilendirme yapmak istiyorum:

GitHub’da projemizi ilk aşamada master branchine attık ama bu aşamadan sonra yapacağımız geliştirmeler için ayrı bir development branch’i açmalıyız ve geliştirmelerimize bu branch üzerinden devam etmeliyiz. Bunun sebebi ise master branchindeki kodlar her zaman production’a hazır halde olmalı ve minimum seviyede bug içermeli. Dolayısıyla geliştirme aşamasındaki featureları her zaman development branchi üzerinden eklemeliyiz. Production’a çıkılacağı zaman ise development branch’indeki kodları master branchine eklemeliyiz.

Bu konuyla alakalı daha iyi anlaşılması açısından şöyle bir örnek verilebilir; Bütün geliştirmeleri master branchi üzerinden yaptığımızı düşünelim ve bu geliştirmeler sonucunda artık projeyi canlıya aldığımızı düşünelim. Proje canlıdayken biz master branchi üzerinden bir sonraki sürüm için featurelar eklemeye devam ediyoruz. Tam bu aşamada planlanmayan bir şekilde canlıdaki üründe kritik bir bug tespit ediliyor ve acilen fixlenmesi gerekiyor. Bu bug’ı fixlemek için master üzerinden değişiklik yapıp canlıdaki kodu güncellemeliyiz fakat master üzerinde aynı zamanda bir sonraki versiyon için planlanan birden fazla feature var. İşte tam bu noktada bu featureları yok sayarak bugı fixlemek çok meşakkatli bir iş olacağından production ve development kodlarını iki ayrı branchte tutmalıyız.

Şimdi Android Studio’ya dönüp projem için development branchini oluşturuyorum ve branch ismini “dev” olarak giriyorum. Bu işlem için aşağıdaki komutu terminale yazmalıyız:

git checkout -b dev

Master branchindeki kodların aynısını birebir olarak dev branchine atmak içinde aşağıdaki komutu çalıştırıyorum:

git push origin dev
Master Branchindeki Tüm Kodlar Dev Branchine de Eklendi

Feature Branch

Feature branch yapısındaki ana mantık, her bir feature’in kendine özel branchler üzerinden geliştirilmesidir. Bu yapının bize sağladığı faydalardan bir tanesi, birden fazla developerın ana kodu değiştirmeden aynı anda birden fazla branch üzerinde çalışmasını sağlamasıdır. Bir diğer faydası ise feature branch yapısı otomatik olarak pull requestleri zorunlu kıldığından ve bu pull requestler reviewerlar tarafından inceleneceğinden, proje içerisinde hatalı, istenmeyen kod olasılığını minimuma indirmesidir. Yine bir feature branch üzerinde geliştirme yaparken eğer bir yerde takılırsak bu branch için bir pull request açıp takım arkadaşlarımızdan request’i incelemelerini isteyip yardım talep edebiliriz.

Örnek Bir Feature Branch Yapısı

Şimdi feature branch yapısını gerçek hayattan güzel bir örnekle açıklamaya çalışacağım:

Mary isminde bir developerımız var ve geliştirdiği projeye yeni bir feature eklemek istiyor. Feature branch yapısını kullanacak olan Mary dev branch’i üzerinden kendi feature’ı için aşağıdaki komutu kullanarak bir branch açıyor:

git checkout -b marys-feature
Mary Kendi Feature’ı İçin Yeni Bir Feature Branch Oluşturuyor

Mary bu branch üzerinden ana branchde yapabileceği her şeyi yapabilir. Commit atmak, pushlamak vs. gibi.

Daha sonra birkaç değişiklik yaptıktan sonra, öğlen yemeği için bilgisayar başından ayrılmadan önce, uzaktaki repositorye kendi feature branchini aşağıdaki komutla pushluyor:

git push origin marys-feature

Bunu yapmasının sebebi, yazdığı kodların kaybolmasını önlemek için uzak bir sunucuda back-up oluşturmak.

Öğle Yemeğinden Önce Mary Branchini Uzaktaki Repositorye Pushluyor

Tekrar bilgisayar başına döndükten sonra Mary üzerinde çalıştığı feature’ı bitiriyor ve deve mergelemek istiyor. Bunun için kendi branchinin son halini push komutuyla uzak sunucuya gönderiyor ve GitHub üzerinden bir pull request oluşturuyor.

Mary Pull Request Oluşturuyor

Projenin reviewerı olan Bill, Mary’nin pull requestini görüyor ve incelemeye başlıyor. İncelemesi bittikten sonra Mary’den kod üzerinde birkaç değişiklik yapmasını istiyor ve bunu pull request üzerinden yorum olarak belirtiyor.

Bill Pull Request’i İnceliyor

Daha sonra Mary tekrar feature branch’e geçiyor ve pull request’i kapatmadan Bill’in istediği düzenlemeleri yapıp kodu tekrar pushluyor. Bu sayede tek bir pull request üzerinden bütün geçmiş görüntülenebiliyor.

Mary Gerekli Değişiklikleri Yapıp Son Güncellemeleri Aktif Pull Request Üzerinden Pushluyor.

İstenilen değişiklikler yapıldıktan sonra Bill pull requesti kabul ediyor ve dev branchine mergeliyor.

Mary’nin Branchi Dev Branchine Mergeleniyor

Branch isimlendirmeleri

Burada branch isimlendirmelerinden de bahsetmek istiyorum. Aslında bunun bir standartı veya best practe’i yok fakat buradaki önemli kısım proje geliştirilirken takım içerisinde belirli bir isimlendirme kuralı koymak ve proje boyunca bu kurallara uymak. Bunun faydalarından bir tanesi de sadece branch ismine bakarak dahi o branchde ne yapıldığına dair fikir oluşmasıdır. Aşağıda bazı isimlendirme örnekleri mevcut:

  • users/username/description
  • users/username/workitem
  • bugfix/description
  • feature/feature-name
  • feature/feature-area/feature-name
  • hotfix/description

Kod Mergeleme Kuralları

Dev branchindeki kodun sağlıklı şekilde production’a aktarılabilmesi için dev branchindeki kod tüm testlerden geçmiş olmalı, build edilebilmeli ve güncel olmalı. Bu sayede yeni bir feature geliştirmek isteyen bir developer hatasız ve temiz bir kod üzerinden geliştirme yapabilir.

Aşağıdaki gibi bir branch policy belirleyerek yukarıdaki yapıyı kurabiliriz:

Kodun mergelenebilmesi için kesinlikle bir pull request oluşturulması gerekiyor. Bu sayede direkt olarak dev branchinde değişiklik yapılmasını önlemiş oluyoruz.

Pull request açıldıktan sonra projenin reviewerları kodu dikkatli bir şekilde incelemeli. Ancak başarılı bir reviewdan sonra kod deve mergelenmeli. Herhangi bir conflict, bug vs. çıkması durumunda ilgili yeri developera bildirmeli.

Pull request’in başarılı olmasının kriterlerinden bir tanesi de başarılı bir build olmalı. Dev branch’e eklenecek olan kod sorunsuz bir şekilde build edilebilmeli.

Release Branches

Release branchler projemizin belirli bir sürümdeki değişikleri koordine edebilmek için kullanılır. Feature branchlerin aksine uzun bir yaşam döngüsüne sahiptirler. Herhangi bir release branch yayında olduğu sürece bu bizim o branch üzerindeki yapılacak her geliştirmeden aktif olarak sorumlu olduğumuz anlamına gelir. Yeni bir release branch çıkana kadar bu böyle devam etmeli. Daha önceden oluşturulmuş bir release branch’e artık destek vermeyeceksek bu branchi lockluyoruz ve desteğimizi kesmiş oluyoruz. Release branchler main branchlerden üzerinden oluşturulur. Versiyonlarımıza göre release branchlerimizin isimlendirmelerini yönetebiliriz örneğin release/20 gibi.

Relase Branch Akışından Örnek Bir Şema

Cherry Picking

Cherry picking yaklaşımının avantajı aşağıdaki gibidir:

Örneğin oluşturulan bir release branch üzerinden bir değişiklik veya bugfix yapmak istiyoruz. Aynı zamanda bu bir bugsa değişiklikleri main branch’e de aktarmak istiyoruz çünkü bir sonraki release branch yine main branch üzerinden oluşturulacağı için aynı problemleri tekrar yaşamak istemiyoruz. Burada cherry picking yaklaşımını kullanabiliriz. Bunun anlamı bugfix için release branch’den bir branch oluşturup değişiklik yapmak, bu değişiklikleri release atmak ve aynı değişiklikleri main branche aktarmaktır. Bu yaklaşım için aşağıdaki adımları izlemeliyiz:

Relase branchinden bir branch açıp bug’ı fixliyoruz.

Daha sonra main branchi üzerinden bir branch açıyoruz ve cherry picking komutu sayesinde release branchteki commitleri maine çekebiliyoruz. Burada cherrry picking kullanma sebebimiz cherry picking komutunun commit özelinde kopyalama yapması. Bu sayese bütün commitleri değil istediğimiz commitleri alabiliriz.

Sonuç

Bu yazıda versiyon kontrol sistemlerini ve bu versiyon kontrol sistemlerinden birisi olan Git’i kullanan GitHub’ı nasıl kullanacağımızı öğrendik. Bu kullanım esnasında feature branch yapısını nasıl entegre edeceğimizi görerek, bu yapının avantajlarını öğrenmiş olduk. Son kısımda da branch isimlendirmeleri, mergeleme kuralları, release branch ve cherry picking’den bahsettik. Siz de kendi projelerinize bu yapıları kurarak daha verimli bir geliştirme ortamı elde edebilirsiniz.

Kaynak

--

--