Kısım II · İnşaat · Bölüm 28

Git

Kodun hafızası (Version control with Git)

Neden burada · Neye dayanıyor

Neden burada: Kod dosyalara ve modüllere bölündü (Bölüm 27). Dosyalar çoğalınca iki yeni soru çıkıyor: dün çalışan kod bugün neden çalışmıyor, ve aynı dosyaya iki kişi (ya da iki agent) aynı anda dokunursa ne olacak? Git bu iki sorunun cevabıdır: kodun zaman içindeki değişimini kayıt altına alır, kaydı paylaşmayı mümkün kılar. Emre sana commit'in ne olduğunu anlattı; bu bölümde o tek fikrin etrafındaki bütün sistemi (dal, birleştirme, uzak depo, çekme isteği, çalışma ağacı) kuruyoruz. Kısım II'nin geri kalanı (npm, TypeScript, React, Expo) ve Bölüm 46'daki agent'larla çalışma, buradaki fikirlerin üstüne oturur.

Neye dayanıyor: Bölüm 16: dosya, klasör ve yol; Git bir klasörün (ve içindeki dosyaların) geçmişini tutar. Bölüm 18: terminal; Git komutları terminalde yazılır. Bölüm 27: modüller; iyi ayrılmış dosyalar Git'te çakışmayı azaltır. Ayrıca Bölüm 2.4'ten onaltılık yazım: Git'in commit adları onaltılık rakamlardan oluşur.

  1. Kat 10Bizim uygulamamızBizim uygulamamızın kodu: Git bu katın geçmişini ve ekip çalışmasını yönetir.
  2. Kat 9Web ve tarayıcı: HTML, CSS, JavaScriptTarayıcı denen programın anladığı diller: iskelet, görünüm, davranış. Bölüm 20, 23–26.
  3. Kat 8Ağ ve internetBilgisayarların birbirine bit göndermesi; IP, TCP, DNS, HTTP. Bölüm 19–20.
  4. Kat 7İşletim sistemi (macOS, Windows, Linux)Git, işletim sisteminin dosya ve klasörleri üzerinde çalışan bir araçtır; terminalden kullanılır. Bölüm 16, 18.
  5. Kat 6Gömülü yazılım (firmware) ve açılışDüğmeye basınca ilk çalışan program. Bölüm 12.
  6. Kat 5Programlama dilleri ve derleyiciİnsanın okuduğu kodu makine diline çeviren araçlar. Bölüm 9–11, 21.
  7. Kat 4Makine dili ve assemblyİşlemcinin doğrudan anladığı komutlar. Bölüm 7.
  8. Kat 3İşlemci ve bellek (CPU, RAM)Komutları çalıştıran makine ve verinin durduğu yer. Bölüm 6, 8.
  9. Kat 2Devreler: hesap ve hafızaToplayıcılar ve hatırlayan devreler. Bölüm 4–5.
  10. Kat 1Transistör ve mantık kapılarıElektrikle açılıp kapanan anahtarlar. Bölüm 3.
  11. Kat 0Elektrik ve bit (zemin)Var ya da yok, 1 ya da 0. Bölüm 2.

Bu bölümün sonunda şunları bileceksin: Git'in neden icat edildiğini ve diğer sürüm kontrol sistemlerinden farkını; bir dosyanın Git'te üç durumdan (değiştirildi, hazırlandı, kaydedildi) hangisinde olduğunu nasıl okuyacağını; commit'in bir anlık görüntü olduğunu ve neden küçük, tek amaçlı commit'lerin iyi alışkanlık olduğunu; dalın aslında bir işaretçi olduğunu; iki dalı nasıl birleştireceğini ve çakışma çıkınca ekranda ne göreceğini; uzak depo ve GitHub'ın rolünü; çekme isteğinin ne olduğunu; ve bölümün ağırlık merkezi olarak, çalışma ağacı (git worktree) ile aynı depoda birden çok dalın aynı anda nasıl yaşayabildiğini. Kitabın ilerisinde agent'larla çalışırken en çok işine yarayacak bölümlerden biri budur.

28.1 Sürüm kontrolü neden var; depo, çalışma dizini, hazırlık alanı

Kopya klasörler çağı

Pro Git kitabı, sürüm kontrolünü anlatmaya o tanıdık alışkanlıkla başlar: birçok kişi sürüm kontrolünü dosyaları başka bir klasöre kopyalayarak yapar, zeki olanlar klasöre tarih de koyar. Kitabın tespiti: bu yöntem çok yaygındır çünkü çok basittir, ama aynı zamanda hataya çok açıktır; hangi klasörde olduğunu unutup yanlış dosyanın üstüne yazmak kolaydır.1 Kendi bilgisayarında proje, proje-son, proje-son-gercek, proje-son-gercek-2 klasörleri gördüysen aynı sorunu yaşamışsındır.

Bunun çözümü sürüm kontrol sistemi (version control system, VCS)dir: dosyalardaki değişikliklerin hepsini tutan bir veritabanı. Aynı kitaba göre bu sistemler üç kuşakta gelişti:1

  1. Yerel sistemler (ör. RCS): değişiklik kayıtlarını diskte basit bir veritabanında tutar. Sorun: yalnızca senin bilgisayarında; başkalarıyla çalışamazsın.
  2. Merkezî sistemler (CVS, Subversion, Perforce): tüm dosyalar tek bir sunucuda, herkes oradan dosya çeker. İşbirliğini mümkün kıldı; ama bedeli tek hata noktasıdır: sunucu bir saat çökerse o saat kimse çalışamaz ve kaydedemez; sunucunun diski bozulursa ve yedek yoksa projenin bütün geçmişi gider.1
  3. Dağıtık sistemler (Git, Mercurial, Darcs): istemciler yalnızca son dosya anlık görüntüsünü çekmez, depoyu bütün geçmişiyle birlikte yansılar. Sunucu ölürse herhangi bir istemcinin kopyası sunucuyu geri yükler; her kopya aslında tam bir yedektir.1

Git'in bir sonucu da şu: Pro Git'e göre Git'teki neredeyse her işlem yalnızca yerel dosyalara ihtiyaç duyar; bütün geçmiş diskinde olduğu için geçmişe bakmak neredeyse anlıktır ve uçakta ya da internetsiz yerde bile kaydedebilirsin.2

Git'in üç alanı

Pro Git'in "Git hakkında hatırlaman gereken ana şey" dediği kısım, dosyaların üç durumundan oluşur ve üç yere karşılık gelir.2 İlk önce dosyaların bulunabileceği üç durum:

Bu üç duruma üç yer karşılık gelir:2

YerTürkçesiNe
Working treeçalışma dizini (working directory, working tree)Projenin tek bir sürümünün diskte açılmış hali: dosyalarını düzenlediğin yer. Git dizinindeki sıkıştırılmış veritabanından çıkarılıp sana verilir.
Staging area (index)hazırlık alanı (staging area)Bir sonraki kaydın içine ne gireceğini tutan bir dosya. Teknik adı "index"; "staging area" de aynı şeydir.
Git directorydepo (repository), .git klasörüProjenin üstverisini ve nesne veritabanını tuttuğu yer. Git'in en önemli parçası; bir depoyu başka bilgisayardan klonladığında kopyalanan şey bu.

Temel iş akışı Pro Git'te şöyle özetlenir: çalışma dizininde dosyaları değiştirirsin; yalnızca bir sonraki kayda girmesini istediğin değişiklikleri seçerek hazırlarsın (böylece hazırlık alanına yalnızca onlar eklenir); sonra kaydedersin: hazırlık alanındaki dosyalar olduğu gibi kalıcı bir anlık görüntü olarak depoya yazılır.2 Neden araya "hazırlık alanı" konmuş? Çünkü bir saat boyunca beş farklı şey yaptın; hepsini tek bir kayıt yapmak zorunda değilsin. Hazırlık alanı, yaptıklarından hangisinin hangi kayda gireceğini seçebileceğin bir tezgâhtır. Bunun neden önemli olduğunu 28.2'de göreceğiz.

İlk komutlar: depo kurmak ve durumu okumak

Bir klasörü depoya çeviren komut git init. Durumu okuyan komut git status; Pro Git bunu dosyaların hangi durumda olduğunu belirlemenin ana aracı olarak sunar.3 Bir deneme klasöründe yaptım (Bölüm 18'deki terminalde, $ işareti yazdığımız komut, altındaki çıktı):

$ git init -q -b main uygulama     # boş bir depo; ilk dal adı: main
$ cd uygulama
$ echo 'const TEMEL_PUAN = 10;' > puan.js
$ printf 'node_modules/\n.env\n' > .gitignore
$ echo 'SECRET=1' > .env
$ git status --short
?? .gitignore
?? puan.js

Satır satır: init gizli bir .git klasörü açar; işte depo budur. Sonra üç dosya yarattım. status --short kısa bir özet verir; ?? "izlenmeyen" (untracked) demektir: Git bu dosyaları görüyor ama onları henüz takip etmiyor. .env'in listede olmadığına dikkat et: onu 28.7'de açıklayacağız. İlk dosyaları hazırlayalım:

$ git add puan.js .gitignore
$ git status --short
A  .gitignore
A  puan.js

git add dosyaları hazırlık alanına alır: baştaki A (added) harfi "bir sonraki kayda eklenecek" anlamına gelir. Pro Git'in uyarısı önemlidir: kaydettiğin an, git add yaptığın andaki dosya hali kayda girer; ondan sonra dosyayı değiştirirsen o değişikliği yeniden hazırlamalısın.3

master mı main mi?

Pro Git, GitHub'ın varsayılan dal adını 2020 ortasında master'dan main'e çevirdiğini, diğer barındırıcıların da ardından geldiğini; Git'in kendisinin ise hâlâ master'ı varsayılan olarak kullandığını belirtir.3 Yani bir depoda main, başka birinde master görebilirsin; ikisi de sıradan bir dal adıdır ve değiştirilebilir. Biz bu kitapta main yazacağız.

28.2 Commit: anlık görüntü, mesaj, geçmiş; küçük ve tek amaçlı commit'ler

Commit bir fark değil, fotoğraftır

Commit (commit), hazırlık alanındakileri depoya kalıcı olarak yazan işlemdir; ve yazılan şeye de commit denir. Burada Git'in ayırt edici fikri var. Pro Git'e göre çoğu sistem bilgiyi dosya değişiklikleri listesi (delta) olarak saklar: her dosya için zaman içinde yapılan değişikliklerin dizisi. Git öyle yapmaz; verisini küçük bir dosya sisteminin bir dizi anlık görüntüsü (snapshot) gibi görür: her commit'te Git o anda bütün dosyaların nasıl göründüğünün bir resmini çeker ve o resme bir referans saklar. Verimli olmak için, değişmeyen dosyayı yeniden saklamaz; daha önce sakladığı özdeş dosyaya bir bağlantı koyar.2

commit 1          commit 2          commit 3
┌─────────┐       ┌─────────┐       ┌─────────┐
│ puan.js │       │ puan.js'│ ◄─┐   │ puan.js'│
│ seri.js │ ◄─┐   │ seri.js │ ──┘   │ seri.js'│
└─────────┘   └── └─────────┘       └─────────┘
(değişmeyen dosya: yeniden saklanmaz, öncekine bağlanır)

Bunu Bölüm 16'daki dosya sistemine bağla: bir commit, projenin klasör ağacının tam bir kopyasını gösteren bir kayıttır; zaman içindeki kayıtlar zincirdir. Pro Git, Git'in bu yüzden basit bir sürüm kontrol aracından çok, üzerine güçlü araçlar eklenmiş küçük bir dosya sistemine benzediğini söyler.2

Her commit'in bir adı var: özet değer (hash)

Her commit, içeriğinden hesaplanan bir özet değerle (hash) adlandırılır. Pro Git'e göre Git'teki her şey saklanmadan önce sağlama toplamından geçirilir ve o sağlama toplamıyla anılır; bu da Git'in bilmediği bir yerde herhangi bir dosyanın içeriğini değiştirmenin imkânsız olması demektir. Kullanılan yöntem 40 karakterlik, onaltılık rakamlardan (0–9, a–f) oluşan bir SHA-1 özetidir: 24b9da6552252987aa493b52f8696cd6d3b00373 gibi.2 Onaltılık yazımı Bölüm 2.4'te gördün: iki rakam bir byte, yani 40 rakam 20 byte (160 bit). Pratikte ilk birkaç karakter yeter: benim deneme commit'imi 3247184 diye kısa gördüm. Bu "adın içerikten üretilmesi" fikri hem bütünlük (içerik değişirse ad değişir) hem de kimlik sağlar.

Pro Git'in bir başka güven verici gözlemi: Git'teki işlemlerin hemen hepsi veritabanına yalnızca ekleme yapar; bir şeyi geri alınamaz hale getirmek ya da silmek zordur; commit'ini yaptığın bir anlık görüntüyü kaybetmek çok zordur, özellikle de depoyu düzenli olarak başka bir yere gönderiyorsan.2 Bu, deneme yapmayı güvenli kılar: kaydettin mi, geri dönebilirsin.

Kaydetmek: git commit ve mesaj

Aşağıdaki, yukarıdaki deneme deposunda yaptığım ilk kayıt:

$ git commit -q -m "Puan hesabı için TEMEL_PUAN sabitini ekle"
$ git log --oneline
3247184 Puan hesabı için TEMEL_PUAN sabitini ekle

-m mesajı doğrudan komuta yazar (yazmazsan Git bir metin düzenleyici açar ve boş mesajla kaydı iptal eder3). git log --oneline geçmişi bir satır bir commit olarak gösterir: kısa özet değer ve mesaj. Her commit ayrıca yazarını, tarihini ve kendisinden önceki commit'i (ebeveynini) tutar; geçmiş, bu ebeveyn bağlantılarıyla geriye doğru bir zincirdir.

Küçük ve tek amaçlı commit'ler: iyi alışkanlık

Emre'nin "çok küçük, tek amaçlı commit" sevdiğini biliyorum; şimdi nedenini kitaptan dinle. Pro Git'in "Commit Guidelines" bölümü şunu söyler: her commit'i mantıksal olarak ayrı bir değişiklik kümesi yapmaya çalış. Bütün hafta sonu beş ayrı konuda kod yazıp pazartesi hepsini tek bir devasa commit olarak gönderme; hafta sonu kaydetmediysen bile pazartesi hazırlık alanını kullanıp işi en az konu başına bir commit'e böl, her birine anlamlı bir mesaj yaz. Aynı dosyada birden çok konunun değişikliği varsa git add --patch ile dosyayı kısmen hazırlayabilirsin. Üstelik bir commit de olsa beş commit de olsa dalın ucundaki son anlık görüntü aynıdır; yani küçük commit'ler son sonucu değiştirmez, ama başkalarının (ve senin) işini kolaylaştırır; ve gerekirse bir değişiklik kümesini ayıklamak ya da geri almak da kolaylaşır.8

Neden bu kadar önemli, ayrıntıyla:

İyi bir commit mesajı

Pro Git'in önerdiği biçim: bir ilk satır (yaklaşık 50 karakteri aşmayan, değişikliği kısaca anlatan özet), sonra boş bir satır, sonra gerekirse ayrıntılı açıklama. Mesajı emir kipinde yaz: "Fix bug" yaz, "Fixed bug" ya da "Fixes bug" yazma; çünkü bu biçim git merge ve git revert'in ürettiği mesajlarla uyumludur. Boş satır önemlidir: araçlar özetle gövdeyi ayırmak için ona bakar.8 Türkçede karşılığı: "Seri sıfırlama hatasını düzelt", "Puan hesabına bonus ekle" (emir kipi); "Düzeltildi" ya da "Bonus eklendi" değil. Benim deneme mesajım da bu kalıpta: "Puan hesabı için TEMEL_PUAN sabitini ekle".

Zayıf mesajİyi mesajNeden
düzeltmelerYanlış cevapta puanı sıfırlaNe değiştiğini mesajdan anlarsın.
güncelleme 2Seri 7 günü bulunca 5 bonus verTek amaç, tek cümle.
seri, puan, ekran, bir de leaderboardDört ayrı commitMesaj "ve" ile uzuyorsa commit iki işe karışmıştır.
Uygulamamızda

Uygulamamızın ilk geçmişi şöyle okunabilir olsun: Soru verisini sorular.js'e taşı, Puan kurallarını puan.js'e ayır, Seri hesabına bonus ekle, Leaderboard sayfasının iskeletini ekle. Her satır tek bir olay; git log --oneline yazdığında uygulamanın hikâyesini okursun. Bir agent'a iş verirken "küçük, tek amaçlı commit'ler yap" demek, ondan alacağın en değerli talimatlardan biridir; Bölüm 46'daki AGENTS.md dosyasına bunu yazacağız.

28.3 Dal, birleştirme, çakışma, yeniden temellendirme

Dal bir işaretçidir

Şu sorunu düşün: uygulamada seri özelliğini geliştiriyorsun (yarım, bozuk), ama bu sırada acil bir puan hatasını düzeltip yayına vermen gerekiyor. Yarım işi nereye koyacaksın? Cevap dal (branch): ana çizgiden ayrılan bir yan çizgi. Pro Git'in tanımı çok sadedir: Git'te dal, bir commit'e işaret eden hafif, taşınabilir bir işaretçidir. Commit yaptıkça dal işaretçisi kendiliğinden ileri kayar.4 Bir dal "dosyaların bir kopyası" değildir; bir etikettir. Bu yüzden dal oluşturmak neredeyse bedavadır.

Yeni dal oluşturmak yalnızca yeni bir işaretçi yaratır: git branch testing, şu anda bulunduğun commit'e yeni bir işaretçi koyar. Git hangi dalda olduğunu HEAD denen özel bir işaretçiyle bilir: HEAD, şu anda üzerinde çalıştığın yerel dalı gösterir.4 Dal değiştirmek (git checkout testing ya da yeni komut git switch testing) HEAD'i taşır; ve çalışma dizinindeki dosyaları o dalın gösterdiği anlık görüntüye geri çevirir.4 Kendi denememde iki dalın bir commit'i nasıl paylaştığı --decorate ile görülüyor (bu bölümdeki worktree denememden):

$ git log --oneline --all --decorate --graph
* f5e08b8 (seri-ozelligi) Seri takibi için seriGuncelle fonksiyonunu ekle
* 3247184 (HEAD -> main, leaderboard) Puan hesabı için TEMEL_PUAN sabitini ekle

İki dal (main ve leaderboard) aynı commit'i gösteriyor; seri-ozelligi bir commit ilerde. HEAD -> main, şu anda main'de olduğumuzu söyler.

Birleştirme

Seri özelliği bitince onu ana dala birleştiririz (merge). Birleştirmek istediğin dalın içine geçersin ve git merge yazarsın: git checkout main sonra git merge seri-ozelligi. İki durum var. Birincisi, ana dalın ucu zaten seri dalının atası ise (ana dalda bu arada yeni commit yok), Git yalnızca işaretçiyi ileri kaydırır ("fast-forward"). İkincisi, iki dal ortak bir atadan ayrıldıktan sonra ikisinde de commit varsa Git biraz iş yapmak zorundadır: Pro Git'in anlattığı üç yollu birleştirme (three-way merge): iki dalın uçlarındaki iki anlık görüntü ve ortak ata kullanılarak yeni bir anlık görüntü üretilir ve ona işaret eden yeni bir commit kendiliğinden oluşur. Buna birleştirme commit'i (merge commit) denir; özelliği birden fazla ebeveyni olmasıdır.5 İşi biten dalı git branch -d seri-ozelligi ile silebilirsin; silinen yalnızca işaretçidir, commit'ler zaten birleşik geçmişin içindedir.5

Çakışma

Aynı dosyanın aynı yerini iki dalda farklı değiştirdiysen Git hangisinin doğru olduğunu bilemez; bu durumda birleştirme duraklar ve buna çakışma (merge conflict) denir.5 Bunu deneyimle göstereyim. puan.js'te TEMEL_PUAN'ı bir dalda 12, ana dalda 15 yaptım ve birleştirdim:

$ git merge a1
Auto-merging puan.js
CONFLICT (content): Merge conflict in puan.js
Automatic merge failed; fix conflicts and then commit the result.

$ cat puan.js
<<<<<<< HEAD
const TEMEL_PUAN = 15;
=======
const TEMEL_PUAN = 12;
>>>>>>> a1

$ git status --short
UU puan.js

Dosyada Git üç işaret bıraktı: <<<<<<< HEAD ile ======= arası, bulunduğun dalın (main) versiyonu; ======= ile >>>>>>> a1 arası, getirdiğin dalın versiyonu. Pro Git'e göre çözmek için ya bir tarafı seçersin ya da içeriği kendin birleştirirsin; işaret satırlarının hepsini silersin, dosyayı git add ile "çözüldü" diye işaretlersin, sonra kaydedersin.5 UU, "iki taraf da değiştirdi, birleştirilmemiş" anlamına gelir. Çakışma bir hata değil, Git'in seni çağırmasıdır: iki insanın (ya da agent'ın) aynı satıra dokunduğunu söylüyor; hangisinin doğru olduğuna yalnızca ürünü bilen biri karar verebilir.

Çakışma ihtimalini azaltan şeyler 27. bölümdeki ilkelerle aynı: iyi ayrılmış, gevşek bağlı modüller ve küçük commit'ler. İki kişi farklı dosyalara dokunuyorsa Git çakışma bulmaz.

Yeniden temellendirme

Aynı ayrılmış iki geçmişi birleştirmenin ikinci yolu yeniden temellendirme (rebase)dir: bir dalda yapılan commit'lerin değişikliklerini alıp başka bir dalın ucuna yeniden oynatırsın. Pro Git'e göre git bunu şöyle yapar: iki dalın ortak atasına gider, senin dalındaki her commit'in getirdiği farkı çıkarır ve geçici dosyalara kaydeder, mevcut dalı hedef dalın ucuna sıfırlar ve farkları sırayla uygular.6 Sonuç: sanki işi baştan, hedef dalın en güncel halinin üzerinde yapmışsın gibi. Birleştirmeyle son anlık görüntü aynıdır; fark, geçmişin şeklinde: rebase doğrusal, temiz bir geçmiş verir, her şey arka arkaya olmuş gibi görünür.6

merge:                       rebase:
main:   A──B──────M          main:   A──B──C
         \      /                              \
feature:  C──D──                feature:  C'──D'   (yeniden oynatıldı)
Rebase'in altın kuralı

Pro Git'in bu konuda tek cümlelik uyarısı: deponun dışında var olan ve başkalarının üzerine iş kurmuş olabileceği commit'leri yeniden temellendirme.6 Nedeni: rebase mevcut commit'leri terk edip, benzer ama farklı yeni commit'ler yaratır. Başkaları eski commit'ler üzerine çalışmışsa onların geçmişi senin yeni geçmişinle uyuşmaz; ikisi de yeniden birleştirme yapmak zorunda kalır ve işler karışır.6 Kısaca: yalnızca henüz kimseyle paylaşmadığın yerel işini yeniden temellendir. Emin değilsen birleştir (merge): güvenlidir, çünkü hiçbir commit'i yeniden yazmaz.

Uygulamamızda

Üç parça düşün: seri-ozelligi, leaderboard ve puan-hatasi-duzeltmesi dalları. Her biri ana dalın aynı noktasından başlar; her biri kendi hızında ilerler; biten dal ana dala birleşir. Bu, Duolingo benzeri uygulamamızın ekip çalışma biçimi olacak. Çakışma çıkarsa yukarıdaki üç işaretli bloğu göreceksin; artık ne anlama geldiğini biliyorsun. Çakışma çözümünü agent'a bırakmak mümkündür, ama hangi tarafın doğru olduğunu sen söyleyebilmelisin: puan 12 mi olsun, 15 mi?

28.4 Uzak depo: GitHub, push / pull / clone; çatal

Uzak depo ve GitHub

Şimdiye kadar her şey senin bilgisayarında. İşbirliği için depoyu başka bir yere de koymak gerekir. Uzak depo (remote), internette ya da ağda bir yerde duran, aynı projenin başka bir kopyasıdır. GitHub (GitHub), bu uzak depoları barındıran bir hizmettir; ama GitHub Git'in kendisi değildir: Git, yerelde çalışan sürüm kontrol aracıdır; GitHub onun üzerine ekip özellikleri (çekme isteği, inceleme, sorun takibi) ekleyen bir site. (GitHub'ın bir tek seçenek olmadığını, Pro Git'in sunucu bölümünün GitLab gibi alternatifleri de anlattığını not edelim.) Uzak depoyla konuşma Bölüm 19–20'deki ağ katmanında olur; Git bunu sana soyutlar.

clone, fetch, pull, push

Akış şöyle: clone bir kez, sonra döngü: dal aç, küçük commit'ler yap, pull ile uzaktaki yeniliği al, push ile işini gönder.

Çatal

Yazma iznin olmayan bir projeye katkıda bulunmak istersen çatal (fork) kullanırsın. Pro Git'e göre bir projeyi çatalladığında GitHub, tamamen sana ait, senin ad alanında yaşayan ve gönderebildiğin bir kopya yapar.9 GitHub Docs'a göre çatal, başka bir depo olan "upstream" deponun kopyası olarak başlayan bir depodur; kendi ayarları ve izinleri vardır ama upstream'e bağlı kalır. Dal, bir deponun parçasıdır; çatal ise ayrı bir depodur ve kendi dalları, sorunları, çekme istekleri vardır.12 Kendi projemizde (iki kişi, ikisi de yazar) çatala çoğunlukla gerek olmaz: ikimiz aynı depoya dal göndeririz. Çatal, açık kaynak projelere katkı verirken karşına çıkar.

Uygulamamızda

Uygulamamızın deposu GitHub'da duracak; sen ve Emre her biri bilgisayarına git clone yapacaksınız. Agent'lar da aynı depoyu kendi çalışma alanlarına klonlar ya da (28.6'da göreceğin gibi) aynı klonun içinde ayrı çalışma ağaçlarında çalışır. İşin bitince git push ile dalı gönderir, çekme isteği açarsın (sıradaki bölüm). Bu aynı zamanda yedeklemedir: bilgisayarın bozulursa kodun GitHub'da durur.

28.5 Çekme isteği ve kod incelemesi

Çekme isteği (pull request, PR), GitHub'ın anlatımıyla, kod değişikliklerini bir projeye birleştirme önerisidir; birleştirmeden önce değişiklikleri tartışmayı ve incelemeyi mümkün kılar, böylece ekipler sorunları erken yakalar ve kod kalitesini korur.10 Adı "çekme" isteğidir çünkü şu anlama gelir: "şu dalımdaki değişiklikleri senin ana dalına çek". Git'in kendi bir özelliği değil, GitHub'ın (ve benzer sitelerin) ekleme özelliği; Git'te iki dalı birleştiren şey hâlâ merge.

Çekme isteğinin parçaları

GitHub Docs'a göre bir çekme isteği, incelemecinin değişikliği anlaması için gereken bağlamı sekmeler halinde toplar:10

Bir çekme isteğini taslak (draft) olarak açabilirsin: taslaklar birleştirilemez ve henüz birilerinden resmî inceleme istemeden yarım işi paylaşmak için kullanılır; hazır olunca "incelemeye hazır" diye işaretlenir.10

Kod incelemesi

Kod incelemesi (code review), değişiklik birleşmeden önce başka birinin onu okuyup yorumlamasıdır. GitHub Docs'a göre inceleyen kişi üç karardan birini verir: Comment (onay ya da değişiklik talebi olmadan genel geri bildirim), Approve (değişiklikler birleştirmeye hazır), Request changes (birleştirmeden önce yazarın ele alması gereken geri bildirim). İncelemeci belirli satırlara da yorum yazabilir, tam bir değişiklik önerebilir.11 İnceleme, hataları yakalamanın yanında bilgi paylaşımıdır: kod tabanını bilen herkes neyin değiştiğini öğrenir.

Uygulamamızda

Agent'larla çalışırken çekme isteği senin kontrol noktandır. Agent bir dalda işi bitirir; sen "Files changed" sekmesinde ne değiştiğine bakarsın. Bölüm 27'deki ölçülerle sor: yalnızca beklenen dosyalara mı dokunmuş? Bağlaşım artmış mı? Commit'ler küçük ve anlaşılır mı? "Checks" sekmesinde testler geçiyor mu? Ancak bundan sonra birleştirirsin. Agent'ın yazdığını okuyamıyorsan ve dalı birleştirmek zorunda hissediyorsan, o sırada "sürüklenme" başlamıştır; PR'ı küçük tutmak (tek amaçlı commit'ler, tek amaçlı dal) bunun en iyi sigortasıdır.

28.6 Çalışma ağacı (git worktree): aynı depoda birden çok dalda aynı anda çalışmak

Bu, bölümün en önemli kısmı ve Bölüm 46'daki agent'larla çalışmanın temeli; yavaş ilerleyelim.

Sorun: bir klasör, bir dal

Hatırla: çalışma dizini, projenin tek bir sürümünün diskte açılmış halidir (28.1). Bir dalda çalışırken git checkout başka-dal dersen, Git klasördeki dosyaları o dalın anlık görüntüsüne çevirir (28.3). Yani bir klasör aynı anda yalnızca bir dalı gösterebilir. Şimdi senaryo: seri-ozelligi dalında yarım işin var, dosyalar karışık. Acil bir hata için puan-hatasi dalına geçmek istiyorsun. Üç seçenek var: yarım işi kaydet (kötü, yarım iş kaydedilmemeli), yarım işi geçici bir rafa kaldır (git stash; Pro Git'in ayrı bir konusu) ya da projeyi başka bir klasöre baştan klonla (disk ve zaman israfı, ayrıca iki ayrı depo).

Şimdi iki agent'ın aynı anda çalıştığını düşün: biri seriyi, öteki leaderboard'u yazıyor. Aynı klasörde çalışırlarsa birinin dosyaları ötekinin ayaklarının altında kayar; biri checkout yaptığında diğerinin dosyaları aniden değişir. Her biri kendi klasöründe çalışmalı. Ama iki ayrı klon yerine tek bir depo ve iki ayrı çalışma klasörü olabilir mi? Evet: git worktree.

Çalışma ağacı nedir

Resmî belgeye göre bir Git deposu birden çok çalışma ağacı (working tree, git worktree) destekleyebilir ve böylece birden fazla dalı aynı anda çıkarabilirsin. git worktree add ile depoya yeni bir çalışma ağacı bağlanır; bu ağaç, ve onu diğerlerinden ayıran üstveri birlikte "worktree" diye anılır.13 Depoda bir ana çalışma ağacı (git init ya da git clone'un hazırladığı) ve sıfır ya da daha fazla bağlı (linked) çalışma ağacı olur. Yeni bağlı ağaç, HEAD ve hazırlık alanı (index) gibi çalışma ağacına özgü dosyalar dışında her şeyi depoyla paylaşır.13

Bunu yeniden söyleyelim, çünkü kritik: commit geçmişi, dallar, etiketler, uzak depo ayarları: hepsi ortak, tek kopya. Ayrı olan: o klasörün dosyaları, HEAD'i ve hazırlık alanı. Yani her ağaç kendi "ben şu dalda, şu dosyalar açık, şu değişiklikleri hazırladım" durumunu taşır. Bir benzetme: bir kütüphanenin tek kataloğu (depo: geçmiş) ve birkaç okuma masası (çalışma ağaçları); herkes aynı kataloğa bakar, ama her masada farklı kitap açıktır.

Denemem: iki bağlı çalışma ağacı

$ git worktree add -b seri-ozelligi ../uygulama-seri
Preparing worktree (new branch 'seri-ozelligi')
HEAD is now at 3247184 Puan hesabı için TEMEL_PUAN sabitini ekle
$ git worktree add -b leaderboard ../uygulama-lb
Preparing worktree (new branch 'leaderboard')
HEAD is now at 3247184 Puan hesabı için TEMEL_PUAN sabitini ekle

$ git worktree list
.../gdemo/uygulama       3247184 [main]
.../gdemo/uygulama-lb    3247184 [leaderboard]
.../gdemo/uygulama-seri  3247184 [seri-ozelligi]

Satır satır: git worktree add -b seri-ozelligi ../uygulama-seri şunu söyler: "../uygulama-seri yolunda yeni bir klasör aç, seri-ozelligi adıyla yeni bir dal oluştur ve onu o klasörde çıkar." (-b yeni dal oluşturur; belgeye göre -b verilmezse, yeni ağaç yolun son bileşeni adlı yeni bir dalla ilişkilendirilir.13) list ise her ağacı, hangi commit'te olduğunu ve hangi dalı çıkardığını gösterir; ana ağaç ilk sırada. Klasörlerin yan yana durduğuna dikkat et: uygulama, uygulama-lb, uygulama-seri.

Şimdi seri klasöründe bir commit yaptım ve ana klasörden geçmişe baktım:

$ cd ../uygulama-seri
$ git add seri.js && git commit -m "Seri takibi için seriGuncelle fonksiyonunu ekle"
$ cd ../uygulama
$ git log --oneline --all --decorate --graph
* f5e08b8 (seri-ozelligi) Seri takibi için seriGuncelle fonksiyonunu ekle
* 3247184 (HEAD -> main, leaderboard) Puan hesabı için TEMEL_PUAN sabitini ekle
$ ls
puan.js

Seri klasöründe yapılan commit, ana klasörden de görünüyor, çünkü geçmiş ortak. Ama ls ana klasörde hâlâ yalnızca puan.js gösteriyor: seri.js yalnızca seri ağacının dosyası, ana ağacın dosyaları değişmedi. İşte ayrışma: tek geçmiş, ayrı çalışma klasörleri.

Bir de güvenlik kuralı: seri dalı başka bir ağaçta çıkarılmışken ana klasörden git checkout seri-ozelligi denedim ve Git reddetti:

$ git checkout seri-ozelligi
fatal: 'seri-ozelligi' is already used by worktree at '.../gdemo/uygulama-seri'

Belgeye göre de dal başka bir yerde çıkarılmışsa komut ağaç oluşturmayı reddeder (--force kullanılmadıkça).13 Bu, iki agent'ın yanlışlıkla aynı dala yazmasını önleyen bir emniyet: bir dal aynı anda yalnızca bir çalışma ağacında çıkarılabilir.

İş bitince ağacı temizlemek için:

$ git worktree remove ../uygulama-lb
$ git worktree list
.../gdemo/uygulama       3247184 [main]
.../gdemo/uygulama-seri  3247184 [seri-ozelligi]
$ git branch -a
  leaderboard
* main
+ seri-ozelligi

remove klasörü ve ağaç kaydını siler, ama dal (leaderboard) hâlâ duruyor: çünkü dal geçmişe aittir, klasöre değil. (branch -a çıktısındaki +, o dalın başka bir çalışma ağacında çıkarılı olduğunu gösterir.) Belgeye göre bir ağacı git worktree remove kullanmadan elle silersen, ilişkili yönetim dosyaları sonunda otomatik temizlenir ya da git worktree prune ile temizlersin.13 Bir ağaç kalıcı bir cihazda ya da her zaman bağlı olmayan bir yerdeyse git worktree lock onu silinmekten korur.13

Neden agent'larla çalışmanın temeli

  1. Yalıtım. Her agent kendi klasöründe ve kendi dalında çalışır; birinin yarım kalmış, bozuk dosyaları ötekini etkilemez. 27. bölümdeki "kendi kapsamı olan modül" fikrinin klasör-dal düzeyindeki karşılığı.
  2. Paralellik. Üç iş, üç klasör, aynı anda. Her biri bağımsız commit'ler üretir.
  3. Ortak geçmiş. Her klonda ayrı ayrı bütün geçmişi tutmaya gerek yok; hepsi bir depoyu paylaşır. İş bitince dalları (28.3'teki gibi) birleştirirsin ya da çekme isteği olarak gönderirsin (28.5).
  4. Tek dal kuralı. Aynı dalın iki ağaçta çıkarılamaması çakışmaları kaynağında keser.
  5. Atılabilirlik. Bir agent yanlış yola saptıysa ağacı silersin (remove), dalı atarsın; ana klasörün hiçbir şeyden etkilenmez.

Belgenin kendi örneği de bu mantıkta: bir yeniden düzenleme oturumunun ortasındayken acil bir düzeltme gerekiyor; git stash ile işi rafa kaldırmak yerine, dağınık çalışma dizinine dokunmadan geçici bir bağlı ağaç açıp düzeltmeyi orada yaparsın, bitince ağacı silersin.13

Sınırlar

Belge dürüstçe şunu da söyler: birden çok çıkarma (multiple checkout) genel olarak hâlâ deneysel; alt modül (submodule) desteği eksiktir ve bir üst projenin birden çok çıkarılması tavsiye edilmez.13 Ayrıca çalışma ağaçları aynı dosyaları çakışmadan değiştirmeyi garanti etmez: iki agent aynı dosyada farklı yerleri değiştirirse, birleştirirken yine 28.3'teki çakışmayla karşılaşırsın. Ağaçlar çakışmayı önlemez, yalnızca çalışma sırasında birbirini bozmayı önler; çakışma birleştirme anında ortaya çıkar. Bu yüzden Bölüm 27'deki iyi modül ayrımı yine önemli: agent'lara farklı dosya kümeleri vermek çakışmayı azaltır.

Uygulamamızda

Gelecekte şöyle çalışacağız (Bölüm 46'da ayrıntılı): ana klasör uygulama. Her görev için bir bağlı ağaç: git worktree add -b seri-ozelligi ../uygulama-seri. Agent o klasörde çalışır, küçük commit'ler yapar, dalı gönderir; sen çekme isteğini okursun; onayladıysan birleştirirsin; sonra git worktree remove ../uygulama-seri. Aynı anda ../uygulama-lb ağacında başka bir agent leaderboard üzerinde çalışabilir. Her ağaç kendi klasöründe olduğundan, node_modules gibi büyük klasörlerin her ağaçta ayrıca kurulması gerekeceğini aklında tut (npm'i Bölüm 29'da göreceğiz): ağaç "yalnızca git'in izlediği dosyaları" paylaşır, kurulu paketleri değil.

28.7 .gitignore, sırlar (secrets) neden depoya girmez

İzlememek istediklerimiz

Her dosya geçmişe girmemeli. Pro Git'e göre sıklıkla Git'in otomatik olarak eklemesini ve hatta izlenmeyen dosya olarak göstermesini istemediğin bir dosya sınıfı olur: genellikle otomatik üretilen dosyalar, günlük (log) dosyaları ya da derleme sisteminin ürettiği dosyalar.3 Bunlar için, adı .gitignore olan ve eşleşme desenlerini listeleyen bir dosya yazılır. Bizim denemedeki dosya:

node_modules/
.env

Satır satır: node_modules/ (sondaki / bu desenin yalnızca klasörle eşleşeceğini söyler14) npm'in kuracağı paketlerin klasörüdür (Bölüm 29); bunları sen yazmadın, yeniden kurulabilir ve çok büyüktür; geçmişe girmemeli. .env ise ayarların ve sırların tutulduğu dosyadır; aşağıda anlatıyorum. Desen kuralları resmî belgedeki gibi: # ile başlayan satır yorumdur, * eğik çizgi dışında her şeyle eşleşir, başa / koyarsan desen .gitignore dosyasının bulunduğu klasöre göre yorumlanır, ! bir deseni tersine çevirir (önceden dışlanmış bir dosyayı yeniden dahil eder).14 Denememde .env dosyasını yarattığım halde git status onu listelemedi, çünkü .gitignore onu görünmez kıldı.

Önemli bir nüans, belgede yazıyor: Git'in zaten izlediği dosyalar .gitignore'dan etkilenmez; izlemeyi bırakmak için git rm --cached kullanılır.14 Yani .gitignore, "bunu hiç ekleme" demektir; "bunu geçmişten sil" demek değildir.

Sırlar neden depoya girmez

Sır (secret), bilinirse başkasının senin adına işlem yapmasına olanak veren gizli değerdir: parola, API anahtarı, veritabanı bağlantı şifresi, bir hizmete erişim jetonu. Bunları kodun içine ya da depoya koymamak güvenlik kuralı olmanın ötesinde, Git'in doğasından gelen bir zorunluluktur. Nedeni üç parçalı bir akıl yürütme:

  1. Git neredeyse yalnızca veri ekler ve kaydedilmiş bir anlık görüntüyü kaybetmek zordur (28.2).2
  2. Dağıtık sistemde depo klonlandığında bütün geçmiş kopyalanır (28.1). Her klon tam bir kopyadır.1
  3. Bu iki gerçeğin sonucu (benim çıkarımım, Pro Git'in cümlesi değil): bir sırrı bir kez commit edip sonra dosyadan silmek yetmez; sır eski commit'te durur ve her klonda, her dalda, geçmişe bakan herkesin erişiminde kalır. Çıkarılması ayrıca geçmişi yeniden yazmayı gerektirir ve yayınlanmış geçmişi yeniden yazmak ise 28.3'teki rebase uyarısının kapsamına girer.

Bu yüzden kural basit: sırlar daha ilk gün .gitignore'a yazılan bir dosyada ya da ortamda tutulur, depoya girmez; hangi ayarların gerektiğini göstermek için sır içermeyen bir örnek dosya (.env.example) girebilir. Sırların güvenliğini, .env dosyaları ve ortam değişkenleri gibi ayrıntılarıyla Bölüm 41'de ve yayına alırken sırların nasıl verildiğini Bölüm 43'te işleyeceğiz. Burada yalnızca refleksi kur: depoya girecek her şey, depoyu görecek herkesin görebileceği şeydir.

Uygulamamızda

Uygulamamızın ilk .gitignore'u şunları içerecek: node_modules/ (Bölüm 29), .env (sırlar), derleme çıktıları (ör. dist/, .expo/; Bölüm 29, 32). Agent'a her iş verdiğinde "sır içeren dosyayı commit'leme" demek yerine, .gitignore'un doğru kurulmuş olması ilk koruma katmanıdır; Bölüm 46'daki AGENTS.md'e de sırlar hakkındaki kuralı yazacağız.

Bölüm 16, 18 ve 27 ile bağlantı

Bölüm 16 (dosyalar): Git, klasör ağacının anlık görüntülerini tutar; .git klasörü de sıradan bir klasördür; depo, dosya sisteminin üstünde kurulmuş bir veri yapısıdır. Çalışma ağacı da diskteki dosya ve klasördür. Bölüm 18 (terminal): bu bölümdeki bütün $ git ... komutları, terminaldeki komut–çıktı döngüsüdür. Bölüm 27 (modüler kod): gevşek bağlaşım ve yüksek uyum yalnızca kodu okunur kılmaz, aynı zamanda birleştirmede çakışmayı azaltır, çünkü iki kişi farklı modüllerde çalışabilir.

Bu bölümde neler kuruldu

Bir sürüm kontrol sistemi, dosyaların geçmişini tutar; Git dağıtıktır: her klon bütün geçmişi taşır. Dosyalar değiştirilmiş, hazırlanmış ve kaydedilmiş olmak üzere üç durumda bulunur; bunlara çalışma dizini, hazırlık alanı ve depo karşılık gelir. Commit bir fark değil, anlık görüntüdür; özet değerle adlanır; küçük ve tek amaçlı commit'ler incelemeyi, geri almayı ve agent denetimini kolaylaştırır; mesaj kısa ve emir kipindedir. Dal, bir commit'e işaret eden bir işaretçidir; HEAD hangi dalda olduğunu gösterir. İki dal birleştirilir (merge commit, çakışma işaretleri) ya da yeniden temellendirilir (paylaşılmış commit'lerde asla). Uzak depo (origin) ile clone, fetch, pull (= fetch + merge), push konuşulur; çatal ayrı bir depodur. Çekme isteği değişikliği inceleme ve tartışmayla birleştirme önerisidir; kod incelemesi üç karardan (comment, approve, request changes) birini verir. Çalışma ağacı tek depoda birden çok klasör ve dalı aynı anda yaşatır; geçmiş ortak, dosyalar, HEAD ve hazırlık alanı ayrıdır; bir dal aynı anda tek ağaçta çıkarılır. .gitignore izlenmeyecek dosyaları ve özellikle sırları dışarıda tutar. Terimler sözlükte, Bölüm 28 başlığı altında.

Birincil okuma

Pro Git, 1.3 What is Git?: bu bölümün 28.1 ve 28.2'sinin özü, kısa. Sonra Pro Git, 3.1 Branches in a Nutshell (dal = işaretçi) ve 3.2 Basic Branching and Merging. Bölümün ağırlık merkezi için git-worktree belgesi: Description ve Examples kısımlarını oku; seçenekleri ezberlemeye çalışma. Derinleşmek için Pro Git, 3.6 Rebasing ve GitHub Docs'taki About pull requests.

Takıldığın her yeri yapay zekâ öğretmenine sor. Bir öneri: boş bir klasörde git init yap, iki dosya yarat, iki ayrı commit at, bir dal aç, ana dalda aynı satırı değiştirip bir çakışma kasten yarat; sonra git worktree add ile iki klasörde iki dal aç. Öğretmenden her adımda "şimdi neden bu çıktıyı gördüm?" diye açıklamasını iste. Çakışmayı bir kez kendi gözünle görmek, ileride korkmamanı sağlar.

Bölüm sonu soruları

Kitaba bakmadan cevapla. Kâğıt kalem serbest.

1. Dağıtık sürüm kontrol sistemi (Git) merkezî sisteme göre hangi sorunu çözer?
Merkezî sistemde sunucu tek hata noktasıdır; Git'te her klon bütün geçmişi taşır, yani her kopya tam bir yedektir. Bkz. 28.1.
2. Bir dosyayı değiştirdin ama git add yapmadın. Git'te hangi durumdadır?
Üç durum: değiştirilmiş (sadece düzenlendi), hazırlanmış (git add ile işaretlendi), kaydedilmiş (commit edildi). Bkz. 28.1.
3. Pro Git'e göre commit, Git'in verisini nasıl saklar?
Git verisini anlık görüntüler dizisi olarak görür; değişmeyen dosyayı yeniden saklamaz, öncekine bağlantı koyar. Bkz. 28.2.
4. Hangisi iyi bir commit mesajıdır?
Kısa, tek amaçlı ve emir kipinde yazılmış mesaj: ne değiştiği anlaşılır, tek işi anlatır ("ver", "eklendi" değil). Bkz. 28.2.
5. Git'te dal (branch) nedir?
Dal bir commit'e işaret eden taşınabilir bir işaretçidir; commit yaptıkça ileri kayar. Bu yüzden dal açmak neredeyse bedavadır. Bkz. 28.3.
6. puan.js'te aynı satırı iki dalda farklı değiştirip birleştirirsen ne olur?
Aynı yeri farklı değiştirdiysen Git karar veremez; birleştirmeyi duraklatıp çakışma işaretlerini bırakır. Sen çözer, git add yapar, kaydedersin. Bkz. 28.3.
7. git pull komutu aslında hangi iki işlemin birleşimidir?
pull, uzak dalın verisini indirir (fetch) ve izlediğin yerel dala birleştirir (merge). Bkz. 28.4.
8. Çekme isteği (pull request) neye yarar?
Çekme isteği, bir dalın değişikliklerini birleştirme önerisidir; birleşmeden önce inceleme ve tartışma imkânı sunar. Bkz. 28.5.
9. git worktree add ile açılan iki ağaç arasında hangisi ortaktır?
Geçmiş, dallar ve uzak depo ayarları ortaktır; HEAD, hazırlık alanı ve çalışma klasörünün dosyaları ağaca özgüdür. Bkz. 28.6.
10. Kendi cümlelerinle: iki agent'a aynı anda iki ayrı özellik yazdıracaksın. Neden aynı klasörde değil de iki ayrı çalışma ağacında çalıştırırsın ve bu, çakışma sorununu tamamen çözer mi?
Örnek cevabı göster

Bir klasör aynı anda tek dalın dosyalarını gösterir; iki agent aynı klasörde çalışırsa birinin dal değişikliği ve yarım dosyaları ötekinin ayağının altından kayar. Çalışma ağaçlarında her biri kendi klasöründe, kendi dalında çalışır; geçmiş ortak olduğundan klonlamaya gerek yoktur ve bir dal aynı anda yalnızca bir ağaçta çıkarılabilir. Ama çakışmayı tamamen çözmez: iki agent aynı dosyanın aynı yerine dokunursa, birleştirme anında yine çakışma çıkar. Ağaçlar çalışma sırasında birbirini bozmayı önler, çakışmayı değil.

11. Kendi cümlelerinle: yanlışlıkla bir API anahtarını commit ettin, sonra dosyadan sildin ve tekrar commit ettin. Neden bu yeterli değildir? Git'in hangi iki özelliği bunu açıklar?
Örnek cevabı göster

Git neredeyse yalnızca veri ekler: ilk commit'teki anlık görüntü geçmişte durur, sonraki commit anahtarı silse bile eski commit'e bakan her kişi anahtarı görebilir. Ayrıca depo dağıtıktır: her klon bütün geçmişi taşır, yani anahtar çoktan başkalarının kopyalarına geçmiş olabilir. Bu yüzden anahtar sızmış sayılır; yenisi üretilmeli ve eskisi geçersiz kılınmalıdır (Bölüm 41). Önlemi baştan .gitignore'dur.

Kaynaklar

  1. Scott Chacon ve Ben Straub, Pro Git, 1.1 About Version Control: kopya klasörler, yerel/merkezî/dağıtık sistemler, tek hata noktası, klonun tam yedek olması.
  2. Pro Git, 1.3 What is Git?: anlık görüntüler ve farklar, yerel işlemler, bütünlük ve SHA-1, yalnızca veri ekleme, üç durum ve üç alan, temel iş akışı.
  3. Pro Git, 2.2 Recording Changes to the Repository: git status, git add, git commit, .gitignore, master/main notu.
  4. Pro Git, 3.1 Branches in a Nutshell: dal = işaretçi, HEAD, checkout, --decorate.
  5. Pro Git, 3.2 Basic Branching and Merging: üç yollu birleştirme, birleştirme commit'i, çakışma işaretleri, çözüm.
  6. Pro Git, 3.6 Rebasing: yeniden temellendirmenin işleyişi, doğrusal geçmiş, "paylaşılmış commit'leri yeniden temellendirme" kuralı.
  7. Pro Git, 2.5 Working with Remotes: origin, fetch, pull, push.
  8. Pro Git, 5.2 Contributing to a Project, "Commit Guidelines": mantıksal olarak ayrı değişiklik kümeleri, mesaj biçimi ve emir kipi.
  9. Pro Git, 6.2 GitHub: Contributing to a Project: çatal (fork) tanımı.
  10. GitHub Docs, About pull requests: öneri olarak çekme isteği, sekmeler, taslak.
  11. GitHub Docs, About pull request reviews: Comment, Approve, Request changes.
  12. GitHub Docs, About forks: çatal ve dal farkı, upstream.
  13. Git belgeleri, git-worktree: ana ve bağlı çalışma ağacı, add, list, remove, prune, lock, paylaşılan ve ağaca özgü dosyalar, örnek, deneysellik notu.
  14. Git belgeleri, gitignore: desen biçimi, izlenen dosyaların etkilenmemesi, git rm --cached.
  15. Yazarın doğrudan denemesi: git 2.53.0 ile boş bir deneme deposunda init, add, commit, worktree add/list/remove ve çakışma. Bölümdeki komut çıktıları bu denemedendir (yalnızca dosya yolları kısaltıldı).