Git Patch ile Değişiklik Paylaşımı ve Uygulama

Git’in patch mekanizması, özellikle pull request altyapısının olmadığı ortamlarda ya da ağ erişiminin kısıtlı olduğu durumlarda hayat kurtarır. Yıllarca farklı müşteri ortamlarında çalışırken “Git repo’ya erişimim yok ama değişikliklerimi paylaşmam lazım” sorunuyla defalarca karşılaştım. Patch dosyaları tam bu boşluğu dolduruyor.

Patch Nedir ve Neden Hala Kullanılır?

Patch kavramı, Git’ten çok önce, Unix dünyasında diff ve patch araçlarıyla başladı. Git bu geleneği kendi formatında sürdürüyor. Temel fikir basit: İki durum arasındaki farkı bir dosyaya yaz, o dosyayı başka birine ver, o da kendi ortamında uygulasın.

Peki GitHub, GitLab, Bitbucket varken neden patch kullanayım diye sorabilirsiniz. Şu senaryolara bakın:

  • Müşteri ortamı internete kapalı, iç ağdaki Git sunucusuna da VPN olmadan erişemiyorsunuz
  • Açık kaynak projesine katkı göndermek istiyorsunuz ama proje hala e-posta tabanlı iş akışı kullanıyor (Linux kernel bunun en bilinen örneği)
  • Bir hotfix’i acilen uygulamak gerekiyor, pipeline bekleyemiyorsunuz
  • Kod review için değişikliği e-postayla paylaşmak istiyorsunuz
  • İki farklı şirkete ait repo arasında değişiklik taşımanız gerekiyor ama doğrudan bağlantı yok

Bu senaryoların en az birini yaşadıysanız, patch mekanizmasını düzgünce öğrenmek size ciddi zaman kazandıracak.

git diff ile Temel Patch Oluşturma

En basit yöntemle başlayalım. git diff çıktısını bir dosyaya yönlendirerek patch oluşturabilirsiniz.

# Çalışma dizinindeki staged olmayan değişiklikler için
git diff > degisiklikler.patch

# Staged (index'e eklenmiş) değişiklikler için
git diff --cached > staged_degisiklikler.patch

# Belirli bir dosya için
git diff HEAD~3 HEAD -- src/config.py > config_patch.patch

# İki branch arasındaki farkı patch olarak kaydet
git diff main..feature/yeni-ozellik > feature_patch.patch

Bu yöntemle oluşturulan patch dosyasını uygulamak için git apply kullanırsınız:

# Patch'i uygula
git apply degisiklikler.patch

# Önce test et, dosyaları değiştirme
git apply --check degisiklikler.patch

# Uygulama sırasında whitespace sorunlarını görmezden gel
git apply --whitespace=nowarn degisiklikler.patch

git diff yöntemi basit ama bir eksikliği var: Commit metadata’sını (yazar, tarih, commit mesajı) taşımıyor. Sadece ham değişiklikleri alıyorsunuz. Bunun için git format-patch devreye giriyor.

git format-patch ile Profesyonel Patch Oluşturma

git format-patch komutu commit geçmişini koruyarak patch dosyaları oluşturur. Her commit için ayrı bir .patch dosyası üretir ve dosyalar e-postayla gönderilebilecek formattadır.

# Son 1 commit için patch oluştur
git format-patch -1 HEAD

# Son 3 commit için patch oluştur (3 ayrı dosya üretir)
git format-patch -3 HEAD

# main branch'ten itibaren tüm commit'leri patch yap
git format-patch main

# Belirli bir commit'ten itibaren
git format-patch abc1234

# Patch dosyalarını belirli bir dizine kaydet
git format-patch -3 HEAD -o ~/patches/

# Tek bir büyük patch dosyası olarak (birden fazla commit'i birleştir)
git format-patch main --stdout > tum_degisiklikler.patch

Üretilen patch dosyası şöyle görünür:

From abc123def456 Mon Sep 17 00:00:00 2001
From: Ali Kaya <[email protected]>
Date: Tue, 15 Oct 2024 14:32:00 +0300
Subject: [PATCH] Redis bağlantı havuzu boyutu artırıldı

Yük testleri sırasında bağlantı havuzunun dolduğu gözlemlendi.
Maksimum bağlantı sayısı 10'dan 50'ye çıkarıldı.

---
 config/database.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Bu format sayesinde kodu kimin yazdığını, ne zaman yazdığını ve neden yazdığını da taşıyorsunuz.

git am ile Patch Uygulama

format-patch ile oluşturulan patch dosyalarını uygulamak için git apply yerine git am kullanmalısınız. am komutu (apply mailbox) commit metadata’sını da korur.

# Tek bir patch dosyasını uygula
git am 0001-Redis-baglanti-havuzu-boyutu-artirildi.patch

# Bir dizindeki tüm patch dosyalarını sırayla uygula
git am ~/patches/*.patch

# Patch'i uygularken orijinal yazarı koru
git am --signoff 0001-fix-critical-bug.patch

# Eğer patch çakışırsa ve devam etmek istiyorsanız
git am --continue

# Patch uygulamasını iptal et ve başa dön
git am --abort

# Çakışmalı commit'i atla
git am --skip

Şu noktaya dikkat edin: git am ile uygulanan commit’lerin hash’leri değişir. Orijinal commit ile aynı hash’i elde edemezsiniz, ancak yazar bilgisi ve tarih korunur.

Gerçek Dünya Senaryosu: Kapalı Ortamda Hotfix Dağıtımı

Şimdi gerçekçi bir senaryo üzerinden gidelim. Bir bankacılık uygulamasında kritik bir bug düzelttiniz, değişikliği production’a taşımanız gerekiyor ancak production ortamı kapalı bir ağda, doğrudan Git repo erişiminiz yok.

# Geliştirme ortamında - değişiklikleri commit et
git add src/payment/processor.py
git commit -m "Fix: Çift ödeme işlemini önleyen kontrol eklendi"

# Patch dosyasını oluştur
git format-patch -1 HEAD -o /tmp/hotfix/

# Dosyayı güvenli kanaldan production sunucusuna taşı
# (SCP, SFTP veya USB ile)
scp /tmp/hotfix/*.patch deploy@prod-server:/tmp/

# Production sunucusunda
ssh deploy@prod-server

cd /opt/uygulama/repo

# Önce patch'i test et
git apply --check /tmp/0001-Fix-Cift-odeme-islemini-onleyen-kontrol-eklendi.patch

# Her şey temizse uygula
git am /tmp/0001-Fix-Cift-odeme-islemini-onleyen-kontrol-eklendi.patch

# Değişikliği doğrula
git log --oneline -5
git show HEAD

Bu iş akışı sayesinde ne deployment pipeline kurmanız ne de production’a doğrudan repo erişimi vermeniz gerekiyor.

Patch Çakışmaları ile Başa Çıkma

Patch uygulanırken çakışma çıkması kaçınılmaz olabilir. Özellikle patch oluşturulduktan sonra hedef branch’te de değişiklikler olmuşsa bu durumla karşılaşırsınız.

# Patch uygulamaya çalışıyoruz, çakışma çıktı
git am 0001-ozellik.patch
# Applying: Yeni özellik eklendi
# error: patch failed: src/main.py:45
# error: src/main.py: patch does not apply

# Seçenek 1: 3-way merge ile dene
git am -3 0001-ozellik.patch

# 3-way merge de başarısız olursa, çakışan dosyaları manuel düzenle
# (Editörde açılacak çakışma işaretleri normal merge gibi görünür)
nano src/main.py

# Düzeltmeleri stage'e ekle
git add src/main.py

# Devam et
git am --continue

-3 parametresi çok değerli. Temel mantığı şu: Patch oluştururken base commit biliniyorsa, Git üç tarafı da karşılaştırarak akıllıca birleştirmeye çalışıyor. Düz patch uygulaması başarısız olduğunda genellikle -3 ile çözülüyor.

Patch İnceleme ve Doğrulama

Patch’i uygulamadan önce içini incelemek iyi bir pratik. Özellikle başkasından aldığınız patch dosyalarında bu adımı atlamamalısınız.

# Patch'in içeriğini görüntüle (uygulamadan)
git apply --stat degisiklik.patch

# Hangi dosyaların değişeceğini özet olarak gör
git apply --numstat degisiklik.patch

# Patch'i verbose modda kontrol et
git apply --check --verbose degisiklik.patch

# format-patch ile oluşturulan patch'in metadata'sını gör
cat 0001-ozellik.patch | head -20

# Birden fazla patch dosyasını aynı anda kontrol et
git apply --check ~/patches/*.patch
# Patch içindeki değişiklikleri renkli görmek için
git apply --stat 0001-ozellik.patch
#  src/config.py  | 8 +++++---
#  tests/test_config.py | 15 +++++++++++++++
#  2 files changed, 20 insertions(+), 3 deletions(-)

Kısmi Patch Uygulama

Bazen bir patch dosyasının sadece bir bölümünü uygulamak istersiniz. Bu durum özellikle büyük patch’lerde işe yarıyor.

# Patch'i interaktif modda uygula (hangi hunks'ların uygulanacağını seç)
git apply -i buyuk_degisiklik.patch

# Belirli dosyalar için patch uygula
git apply degisiklik.patch -- src/config.py

# Patch'i tersine uygula (geri al)
git apply --reverse degisiklik.patch

# format-patch çıktısından belirli commit'leri seç
# Önce patch'leri oluştur
git format-patch main -o /tmp/patches/

# Sonra sadece istediğini uygula
git am /tmp/patches/0003-sadece-bu-commit.patch

Tersine uygulama (--reverse) da çok kullanışlı. Bir değişikliği geri almak için aynı patch dosyasını ters yönde uygulayabilirsiniz. Özellikle commit geçmişi olmayan ortamlarda işe yarıyor.

E-posta Tabanlı İş Akışı

Linux kernel ve bazı büyük açık kaynak projeleri hala e-posta tabanlı patch iş akışı kullanıyor. git send-email ve git am bu iş akışının omurgasını oluşturuyor.

# git send-email kurulumu (Debian/Ubuntu)
sudo apt-get install git-email

# SMTP ayarlarını .gitconfig'e ekle
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpuser [email protected]

# Patch'i e-postayla gönder
git send-email 
  --to="[email protected]" 
  --cc="[email protected]" 
  0001-yeni-ozellik.patch

# Birden fazla patch'i tek komutla gönder
git send-email 
  --to="[email protected]" 
  --compose 
  ~/patches/*.patch

Pek çok kişi e-posta tabanlı iş akışını eski kafalı buluyor ama açıkçası asenkron inceleme süreçleri için hala çok güçlü bir yöntem. Bir pull request gibi tüm ekibin sisteme giriş yapmasını gerektirmiyor.

Patch Dosyası Format Detayları

Kendi araçlarınızı yazıyorsanız ya da patch dosyalarını otomatik işleyecekseniz formatı anlamak önemli.

# Bir patch dosyasının anatomisi
cat 0001-ornek.patch
From 7a8b9c0d1e2f Mon Sep 17 00:00:00 2001
From: Mehmet Demir <[email protected]>
Date: Wed, 16 Oct 2024 09:15:00 +0300
Subject: [PATCH 1/3] Veritabanı bağlantı zaman aşımı güncellendi

Uzun süren sorgularda timeout oluşuyordu. Bağlantı havuzu
parametreleri production değerleriyle güncellendi.

Signed-off-by: Mehmet Demir <[email protected]>
---
 config/db.conf | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/config/db.conf b/config/db.conf
index 1234abc..5678def 100644
--- a/config/db.conf
+++ b/config/db.conf
@@ -10,6 +10,6 @@ [database]
-connect_timeout = 5
-query_timeout = 30
-pool_size = 10
+connect_timeout = 10
+query_timeout = 60
+pool_size = 25
-- 
2.43.0

Dosyanın başındaki uzun sayı (Mon Sep 17 00:00:00 2001) sabit bir tarih ve patch formatı olduğunu belirtiyor. Subject satırındaki [PATCH 1/3] bu patch’in bir serinin parçası olduğunu gösteriyor.

Gelişmiş Kullanım: Patch Serisi Oluşturma

Birbirine bağlı birden fazla commit’i organize bir şekilde paylaşmanız gerektiğinde patch serisi oluşturabilirsiniz.

# Cover letter ile patch serisi oluştur
git format-patch main --cover-letter -o ~/patches/

# Oluşturulan 0000-cover-letter.patch dosyasını düzenle
nano ~/patches/0000-cover-letter.patch
# *** SUBJECT HERE *** yerine serinin başlığını yaz
# *** BLURB HERE *** yerine genel açıklamayı yaz

# Patch serisini tek stdout'a yaz (e-posta veya arşiv için)
git format-patch main --stdout > tum_seri.patch

# Belirli sayıda context satırı ile (varsayılan 3)
git format-patch -U5 main

# Binary dosyaları da dahil et
git format-patch --binary main

Cover letter özellikle çok commit’li feature’ları paylaşırken değerli. Alıcıya “neden bu değişiklikleri yaptım” diye genel bir bağlam sunuyor.

Pratik İpuçları ve Yaygın Hatalar

Yıllar içinde birkaç kez aynı hataları yaptım ve çevremdeki insanların yaptığını gördüm:

Whitespace sorunları: Windows geliştirme ortamından gelen patch’ler CRLF satır sonlarıyla gelir. --whitespace=fix parametresi bu sorunu otomatik düzeltir.

git apply --whitespace=fix degisiklik.patch
git am --whitespace=fix 0001-ozellik.patch

Yanlış base commit: Patch oluşturulurken hangi commit üzerinde çalışıldığını kaydedin. Patch’i uygulayacak kişi önce o commit’e geçmeli.

# Patch oluştururken base commit'i not et
git log --oneline -1
# abc1234 Base commit

# Patch'i oluştur ve base commit bilgisini dosya adına yaz
git format-patch -1 HEAD -o ~/patches/
# Not: Bu patch abc1234 commit'i üzerine uygulanmalı

Binary dosyalar: Standart patch formatı binary dosyaları desteklemez. --binary parametresini kullanmayı unutmayın ve uygulamada da --allow-binary-replacement ekleyin.

git format-patch --binary -1 HEAD
git apply --allow-binary-replacement degisiklik.patch

Sonuç

Git patch mekanizması, modern araçların gölgesinde kalmış ama vazgeçilmez bir beceri. Özellikle erişim kısıtlı ortamlarda, çapraz organizasyon işbirliğinde ve acil hotfix senaryolarında hayat kurtarıyor.

git diff ile basit değişiklik paylaşımından git format-patch ile tam commit geçmişi koruyan profesyonel patch serilerine kadar geniş bir araç seti var elinizde. git am ve git apply arasındaki farkı kavramak, patch çakışmalarını -3 parametresiyle çözmesini bilmek ve whitespace sorunlarının üstesinden gelmek sizi bu alanda yetkin yapıyor.

Linux kernel katkı sürecini merak edenlere özellikle tavsiyerim: Patch tabanlı iş akışını öğrenmek, modern Git platformlarına olan bağımlılığınızı azaltırken kod paylaşımını daha net ve belgelenmiş hale getiriyor. Bir sonraki “Git’e erişimim yok, nasıl paylaşacağım?” sorusuyla karşılaştığınızda artık elinizde doğru araçlar var.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir