osa1 feed

Az da olsa git sürüm kontrol sistemini anlamak

December 19, 2011 - Tagged as: git, tr.

Aslında uzun süredir yazdığım kodları github’da tutuyor olsam da git’i son birkaç günde çok daha iyi anladığımı söyleyebilirim. Şu ana kadar yaptığım, sürekli tek bir branch üzerinden çalışarak, diğer bilgisayar geçeceğim zaman veya çalışan bir sonraki sürümü yayınlamak istediğimde push yapmaktı. Pardus’da geliştirdiğimiz web projesinde bile sadece stajın son gününde, tek bir commit yaptık hehe(neredeyse tüm projeyi tek bir bilgisayarda pair-programming ile geliştirdiğimizden ihtiyaç duymamıştık, değişiklikleri göstermek istediğimde makinamda kurulu apache’yi çalıştırıyordum ve ağ üzerinden test ediyorlardı. tüm dosya sistemim zaten ağ üzerinden paylaşıma açıktı).

Son günlerde büyük oranda deneyse bir 2d platform oyunu üzerinde çalışıyorum ve sık sık eski haline dönmeyi isteyebileceğim ciddi değişiklikler yapıyorum. Git bana bu konuda nasıl yardımcı olabilir diye biraz araştırdım ve şu ana kadar bunları nasıl farketmedim diye kızdım kendime :P .

Kendime not niteliğinde, bazı olmazsa olmaz(benim için) komutları açıklayacağım.

Başlangıç, depo oluşturma veya mevcut depoyu ekleme

Anladığım kadarıyla bir depoya yazma izninin belirlenmesi biraz da sunucu tarafıyla alakalı. Bizim yapmamız gereken git config --global user.name "isim" ve git config --global user.email "email" ile commitlerimizde gözükecek ismi ve maili belirlemek(mail nerede gözüküyor bilmiyorum). Daha sonra eğer github kullanıyorsak ssh anahtarı oluşturup github’a ekliyoruz. Github’da anladığım kadarıyla ssh key’den kullanıcı adına bakıp depoya yazıp yazamayacağımıza karar veriyor. Bu kısımdan emin değilim açıkçası ama aklıma benim neden Clojure github deposuna push yapamadığımı açıklayacak başka bir senaryo gelmedi aehueah.

Git sunucunuzda depoyu bir şekilde oluşturduktan sonra(Github’da web sayfasından tıklayarak falan) git init ile bilgisayarımızda git deposu oluşturup, buna uzak sunucudaki depoyu git remote add ile ekliyoruz:

➜  ~  mkdir git-test
➜  ~  cd git-test 
➜  git-test  git init
Initialized empty Git repository in /home/sinan/git-test/.git/
➜  git-test git:(master) git remote add git-test git@github.com:osa1/git-tests.git

(Bu arada not: zsh kullanıyorum, oh-my-zshda süper eklentiler var, örneğin benim kullandığım git eklentisinde yukarıda da görebileceğiniz gibi bulunduğum branch’ı gösteriyor, commitlenmemiş değişiklikler varsa bir sembolle belli ediyor, git <tab> yaptığınızda komutları, komutlardan sonra <tab> yaptığınızda branch adlarını vs. tamamlıyor, mükemmel birşey, bir sürü de tema var ayrıca.)

Depoyu oluşturduktan sonra hiç branch’ımız yok aslında. Ama eğer branch oluşturmadan direkt commit yaparsak master diye bir branch oluşturuyor. master adının bir esprisi yok anladığım kadarıyla. Direkt olarak başka isimli bir branch da oluşturulabilir.

git-status ile commitlenmemiş değişiklik olup olmadığını görebiliyoruz. git-log tüm branchlara yapılan tüm commitleri bir 53ea667012c2741e7620e093e05132cd08265c06 benzeri kodla gösteriyor. Bu kodun tam olarak ne olduğunu anlamasam da, önceki bir commit’e dönmem gerektiğinde kullanıyorum.

Branchlar ile alakalı işlemler

➜  git-test git:(master) git checkout -b "branch2"
Switched to a new branch 'branch2'
➜  git-test git:(branch2) git branch           
* branch2
  master

checkout normalde zaten olan bir brancha geçer. Ama eğer “yeni oluştur ve hemen geç” diyorsanız, -b ile bu şekilde kullanılıyor. git branch ile de hangi branchda olduğunuzu görürsünüz(benim yaptığım gibi bir zsh eklentisi kullanıyorsanız zaten gözüküyor gerçi).

Branchlar hakkında yazmak istediğim birkaç şey var. Bir tanesi hangi branchda olursanız olun, başka bir brancha merge yapmadan da o brancha push yapabilirsiniz. Şu şekilde:

➜  git-test git:(branch2) git push git-test branch2:master

Bu syntax’a dikkat: branch2’yi master’a pushla diyorum. Gerekli merge’leri kendisi yapıyor, yapamıyorsa push da yapmıyor. Tabii sonra master branch’ına geçip fetch(master) + merge veya direkt merge(branch2’den) yapmak lazım.

Bunu kullanarak şöyle bir çakallık yapabiliyorsunuz, diyelim ki sunucudan bir branchı uçurmak istiyorsunuz:

➜  git-test git:(branch2) git push git-test :branch2

Bu aslında “branch2’ye hiçbir şeyi pushla” gibi bir anlama geliyor(ben uydurdum aehuaeh). branch2’yi sunucudan siliyor yani. Bilgisayarınızdaki depodan da şu şekilde siliyoruz:

➜  git-test git:(master) git branch -D branch2

Ha neden bir branch’ı sunucudan silmek isteyesiniz bilmiyorum. Geri dönmek isteyebilirsiniz belki, sunucuda durmasında bir zarar yok gibi.

Push ve pull

Şimdi, yukarıda bir branchdan başka bir brancha push yapılabildiğinden bahsetmiştim. Ama pratikte bu bir kolaylık sağlamıyor(sonuçta diğer brancha geçtiğinizde yine fetch + merge(veya sadece merge) yapmanız lazım.

pull ve fetch + merge’den bahsetmek lazım. fetchin yaptığı aslında sunucunuzdaki depodaki istediğiniz branchı depo-adi/branch-adi adlı bir brancha çekmek. Daha sonra bunu branch-adi ile merge etmek istiyorsanız git merge depo-adi/branch-adi ile merge ediyorsunuz. İşte git pull depo-adi branch-adi tam olarak bunu yapıyor.

➜  git-test git:(master) git fetch git-test master
From github.com:osa1/git-tests
 * branch            master     -> FETCH_HEAD
➜  git-test git:(master) git merge git-test/master 
Auto-merging test.markdown
CONFLICT (content): Merge conflict in test.markdown
Automatic merge failed; fix conflicts and then commit the result.

İşte şu anda yaptığım git pullun yapacağının aynısı.

Yanlız burda anlamadığım bir nokta, eğer bu depo-adi/branch-adi branchına geçersek şöyle bir mesaj alıyoruz:

➜  git-test git:(master) git checkout git-test/master 
Note: checking out 'git-test/master'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 53ea667... Merge remote branch 'git-test/master'

Ne demeye çalıştığını çözemedim.

Son olarak hayatımda gördüğüm en güzel şey(abarttım), eski commite geri dönme:

➜  git-test git:(branch3) git checkout 692832aa7a4a3a092e35f6955676be3c6e54e89e -b branch4 
Switched to a new branch 'branch4'

Burdaki commit kodundan ilk başta bahsetmiştim. Yeni bir branch oluşturup eski commite dönüyorum.

Commit hakkında birkaç şey

commit -a, daha önceden takipe aldığınız(git add ile) dosyalardan değişiklik olanları otomatik olarak commite ekler. Teker teker yeniden git add yapmanıza gerek kalmaz(ben ilk başta -a tüm dosyaları ekliyor sanıyordum mesela). commit -m, commit mesajı için $EDITOR çevre değişkenindeki editorü çalıştırmak yerine(bu kısım sanırım sadece bash için geçerli) komut satırından mesaj girmenizi sağlar. Ben commitlerimi şöyle yapıyorum yani:

➜  git-test git:(master) git commit -a -m "mesaj"

Stash

Bu özellik yeni başlayanlar için çok kritik değil sanırım, ama bir arkadaşla bir proje üzerinde çalışırken(ne yaptığımız hakkında hiçbir fikrimiz yok tabi aehueah) git bize “git stash yap istersen” gibi uyarılar veriyordu. Biz de anlam veremiyorduk.

stashın olayı şuymuş, diyelim ki bir branchda değişiklikler yaptınız, bir bug gözünüze çarptı düzelteceksiniz, sadece o kısım düzelmiş olarak commit yapacaksınız. Hemen git stash save ile o ana kadar yaptığınız commitlenmemiş değişiklikleri kaydediyorsunuz ve hemen son commitli haline dönüyor branch. Bugfix yapıp commit/pushluyorsunuz, sonra git stash pop ile son kaydettiğiniz stashe dönüyorsunuz. Birden fazla stash olabiliyor, git stash list ile listeliyorsunuz. İşiniz bittiyse git stash drop ile stashı siliyorsunuz falan. Henüz kullanma fırsatım olmadı, okuduklarımı yazıyorum. Kullandığımda örneklerle düzenleyebilirim.


Biraz dağınık oldu sanırım, amacım burda git’e veya sürüm kontrolüne yeni başlayanlara öğretmek değil de(o kadar bilmiyorum zaten), benim gibi bazı şeylerin ne kadar kolay/kullanışlı olduğunun farkında olmayanlara birşeyler göstermek ve kendime not düşmekt, o yüzden idare edin :P .