Git Bundle ile Offline Depo Aktarımı
Havayı koklayabilirsiniz: üretim ortamı internet erişimi yok, güvenlik duvarı her şeyi kesiyor, GitLab sunucunuz DMZ’nin öte yanında ve birinin acilen bir depoyu geçirmesi gerekiyor. İşte tam bu noktada git bundle devreye giriyor. Yıllarca bu tür senaryolarla uğraşmış biri olarak söyleyebilirim ki bu komut, Git’in en az konuşulan ama en çok hayat kurtaran özelliklerinden biri.
Git Bundle Nedir ve Neden Önemlidir?
git bundle, bir Git deposunun tamamını veya belirli bir kısmını tek bir ikili dosyaya paketleyen bir mekanizmadır. Bu dosya daha sonra başka bir makinede sanki uzak bir Git sunucusuymuş gibi kullanılabilir. Yani USB bellek, SCP, SFTP, hatta e-posta eki olarak gönderebileceğiniz bir dosyadan tam anlamıyla git fetch, git pull veya git clone yapabilirsiniz.
Özellikle şu senaryolarda vazgeçilmez olur:
- Air-gapped sistemler: İnternete erişimi olmayan güvenli ortamlar (bankacılık, savunma, kritik altyapı)
- Bant genişliği kısıtlamaları: Yavaş WAN bağlantısı üzerinden büyük depo transferleri
- Yedekleme stratejileri: Deponun belirli bir anının arşivlenmesi
- Offline ekip çalışması: Sahaya çıkan ekipler için kod tabanının hazırlanması
- Ağ izolasyonu olan CI/CD ortamları: Pipeline’ların internete çıkamadığı kurumsal yapılar
Temel Kullanım
Tam Depo Paketleme
En basit haliyle, mevcut bir depoyu tamamen paketlemek için:
# Depo dizininin içinde
git bundle create ../myproject.bundle --all
# Ya da belirli bir yolu hedefleyerek
git bundle create /tmp/myproject.bundle --all
--all bayrağı tüm branch’leri, tag’leri ve referansları dahil eder. Sonuçta elimizde myproject.bundle adında tek bir dosya var.
Bundle’ın Doğrulanması
Dosyayı karşı tarafa göndermeden önce bütünlüğünü kontrol edin:
git bundle verify myproject.bundle
Bu komut çıktısında şöyle bir şey görmelisiniz:
The bundle contains these 2 refs:
abc1234def5678 refs/heads/main
98765fedcba321 refs/heads/develop
The bundle requires these 0 refs:
myproject.bundle is okay
Eğer “requires these X refs” kısmı boş değilse, bu incremental (artımlı) bir bundle’dır ve hedef sistemde o referansların zaten mevcut olması gerekir. Buna dikkat edin.
Hedef Sistemde Klonlama
Bundle dosyasını hedef sisteme taşıdıktan sonra normal bir uzak depodan klonlar gibi kullanabilirsiniz:
# Bundle'dan klonlama
git clone myproject.bundle myproject-dir
# Ya da belirli branch'i belirterek
git clone -b develop myproject.bundle myproject-dir
Gerçek Dünya Senaryosu 1: Fabrika Ortamı
Geçen yıl bir üretim tesisinde çalışırken yaşadım bunu. Fabrikanın üretim hattını kontrol eden sistemler, kurumsal ağdan tamamen izole edilmişti. Yazılım güncellemelerini USB ile taşıyorduk. Eski yöntemimiz: zip’le, taşı, aç, elle kopyala. Sorun: Git geçmişi gitmiyordu, branch’ler karışıyordu, kim ne değiştirmiş anlamak imkansızlaşıyordu.
Bundle’a geçtikten sonra süreç şu hale geldi:
# Ofiste, her sprint sonunda
git bundle create /media/usb/sprint-$(date +%Y%m%d).bundle --all
sync
umount /media/usb
# Fabrikada, USB takıldıktan sonra
git clone /media/usb/sprint-20240315.bundle /opt/manufacturing-control
cd /opt/manufacturing-control
git log --oneline -10
Tam Git geçmişiyle, tüm branch’leriyle, tüm tag’leriyle. Üç satır komut.
Artımlı Bundle’lar: Bant Genişliğini Koruma
Tam depoyu her seferinde paketlemek büyük depolar için pratik değil. Artımlı bundle’lar burada devreye giriyor. Yani “son seferden bu yana ne değişti” mantığıyla çalışan paketler.
# İlk tam bundle (başlangıç noktası)
git bundle create v1.bundle --all
# Gönderilen son commit'i kaydet
git rev-parse HEAD > last-sent.txt
# ya da bir tag ile işaretle
git tag bundle-checkpoint-v1
# Sonraki seferde, sadece yeni commit'leri pakitle
git bundle create v2.bundle bundle-checkpoint-v1..HEAD
# Yeni checkpoint'i güncelle
git tag bundle-checkpoint-v2
git tag -d bundle-checkpoint-v1
Artımlı bundle’ın hedef sistemde uygulanması:
# Var olan depodan fetch
cd /path/to/existing-repo
git fetch /path/to/v2.bundle main:main
# Ya da tüm referansları çek
git fetch /path/to/v2.bundle 'refs/heads/*:refs/heads/*'
Burada kritik bir nokta: artımlı bundle’ı uygulamaya çalıştığınız deponun, o bundle’ın baz aldığı commit’lere sahip olması gerekiyor. Yoksa şu hatayı görürsünüz:
error: Repository lacks these prerequisite commits:
error: abc123...
Gerçek Dünya Senaryosu 2: Banka Veri Merkezi
Bir bankada danışman olarak çalıştığımda, geliştirme ortamından üretim ortamına kod taşıma süreci inanılmaz derecede bürokratikti. Her değişiklik için IT güvenlik onayı, değişiklik yönetim sistemi kaydı ve fiziksel imza gerekiyordu. Üstüne bir de ağlar arası doğrudan bağlantı yoktu.
Çözüm olarak şunu kurguladık:
#!/bin/bash
# deploy-bundle.sh - Geliştirme tarafında çalışır
RELEASE_TAG=$1
BUNDLE_DIR="/secure-transfer/outbound"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
if [ -z "$RELEASE_TAG" ]; then
echo "Kullanim: $0 <release-tag>"
exit 1
fi
# Sadece ilgili tag'i ve ana branch'i paketle
git bundle create
"${BUNDLE_DIR}/release-${RELEASE_TAG}-${TIMESTAMP}.bundle"
"tags/${RELEASE_TAG}"
main
# SHA256 checksum oluştur (güvenlik ekibi için)
sha256sum "${BUNDLE_DIR}/release-${RELEASE_TAG}-${TIMESTAMP}.bundle"
> "${BUNDLE_DIR}/release-${RELEASE_TAG}-${TIMESTAMP}.bundle.sha256"
echo "Bundle hazır: release-${RELEASE_TAG}-${TIMESTAMP}.bundle"
echo "Checksum:"
cat "${BUNDLE_DIR}/release-${RELEASE_TAG}-${TIMESTAMP}.bundle.sha256"
Üretim tarafında ise:
#!/bin/bash
# apply-bundle.sh - Üretim tarafında çalışır
BUNDLE_FILE=$1
TARGET_DIR="/app/repos/myproject"
# Checksum doğrula
sha256sum -c "${BUNDLE_FILE}.sha256" || {
echo "HATA: Checksum doğrulaması başarısız!"
exit 1
}
# Bundle bütünlüğünü kontrol et
git bundle verify "${BUNDLE_FILE}" || {
echo "HATA: Bundle geçersiz!"
exit 1
}
# Uygulamayı durdur, fetch et, ayağa kaldır
systemctl stop myapp
cd "${TARGET_DIR}"
git fetch "${BUNDLE_FILE}" 'refs/tags/*:refs/tags/*'
git checkout "$(git tag -l 'v*' | sort -V | tail -1)"
systemctl start myapp
echo "Deployment tamamlandı."
Çoklu Branch ile Çalışma
Sadece belirli branch’leri paketlemek için:
# Sadece main ve release branch'lerini paketle
git bundle create selective.bundle
refs/heads/main
refs/heads/release/1.x
refs/heads/release/2.x
refs/tags/v1.0
refs/tags/v2.0
# Sadece tag'leri paketle
git bundle create tags-only.bundle
--tags
# Belirli bir tarihten sonrasını paketle
git bundle create recent.bundle
--since="2024-01-01"
--all
--since parametresi özellikle ilginç; belirli bir tarihten itibaren olan commit’leri paketle anlamına geliyor. Büyük ve eski depolar için faydalı olabilir ama dikkatli kullanın; tarih bazlı kesme noktası bazı commit bağımlılıklarını kopurabilir, her zaman git bundle verify ile kontrol edin.
Bundle’dan Remote Gibi Kullanım
Bundle’ları geçici bir remote olarak tanımlayabilirsiniz. Bu özellikle tekrarlı işlemler için kolaylık sağlar:
# Bundle'ı remote olarak ekle
git remote add offline-remote /path/to/myproject.bundle
# Normal remote gibi fetch yap
git fetch offline-remote
# Branch'leri gör
git branch -r
# İhtiyaç kalmadığında kaldır
git remote remove offline-remote
Bu yaklaşımın güzel yanı şu: bundle dosyasını güncellenmiş versiyonuyla değiştirip aynı git fetch offline-remote komutunu çalıştırdığınızda, yeni değişiklikleri çekmiş olursunuz. Scriptlerde kullanıma çok uygun.
Otomasyon: Periyodik Bundle Yedekleme
Birçok kurumda depolar için düzenli yedek almak gerekiyor ama çoğu zaman bu işlem ya hiç yapılmıyor ya da yetersiz bir şekilde yapılıyor. Şu script bunu halleder:
#!/bin/bash
# git-bundle-backup.sh
# Cron ile çalıştırılmak üzere tasarlanmıştır
# 0 2 * * * /usr/local/bin/git-bundle-backup.sh
REPOS_BASE="/var/git"
BACKUP_BASE="/mnt/backup/git-bundles"
RETENTION_DAYS=30
LOG_FILE="/var/log/git-bundle-backup.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
for repo_path in "$REPOS_BASE"/*.git; do
repo_name=$(basename "$repo_path" .git)
backup_dir="${BACKUP_BASE}/${repo_name}"
mkdir -p "$backup_dir"
bundle_file="${backup_dir}/${repo_name}-$(date +%Y%m%d).bundle"
log "Paketleniyor: ${repo_name}"
if git -C "$repo_path" bundle create "$bundle_file" --all; then
git bundle verify "$bundle_file" >> "$LOG_FILE" 2>&1
log "Başarılı: ${bundle_file}"
# Eski yedekleri temizle
find "$backup_dir" -name "*.bundle" -mtime "+${RETENTION_DAYS}" -delete
log "Eski yedekler temizlendi (${RETENTION_DAYS} günden eski)"
else
log "HATA: ${repo_name} paketlenemedi!"
fi
done
log "Yedekleme tamamlandı."
Bu scripti /etc/cron.d/git-bundle-backup dosyasına ekleyip her gece saat 02:00’da çalıştırabilirsiniz.
Büyük Depolar için LFS Uyarısı
Git LFS (Large File Storage) kullanıyorsanız dikkat etmeniz gereken kritik bir nokta var: git bundle, LFS nesnelerini içermez. Bundle sadece Git’in kendi nesne veritabanındaki şeyleri paketler, LFS nesneleri ise ayrı bir depoda tutulur.
LFS’li depolar için ek adımlar:
# LFS nesnelerini ayrıca dışa aktar
git lfs fetch --all
git lfs export --lfs-objects /path/to/lfs-objects/
# Veya LFS nesnelerini tar'la
cd .git/lfs/objects
tar -czf /tmp/lfs-objects.tar.gz .
# Hedef sistemde
cd /path/to/new-repo/.git/lfs/objects
tar -xzf /tmp/lfs-objects.tar.gz
Bunu atlarsanız hedef sistemde git lfs pull çalıştırdığınızda “Object does not exist on the server” hatalarıyla karşılaşırsınız.
Bundle İçeriğini İnceleme
Bir bundle dosyasının içinde ne olduğunu merak ediyorsanız:
# Hangi referansları içerdiğini gör
git bundle list-heads myproject.bundle
# Daha detaylı bilgi
git bundle verify myproject.bundle
# Bundle'ı açmadan log'a bak
git log --oneline myproject.bundle
Son komut ilginç; bundle’ı açmadan doğrudan commit geçmişini görebiliyorsunuz. Yanlış dosyayı mı aldınız kontrol etmek için birebir.
Sık Yapılan Hatalar
Birkaç yılda öğrendiğim acı tecrübeler:
Prerequisite kontrolünü atlamak: Artımlı bundle oluşturmadan önce her zaman baz commit’in hedef sistemde var olduğundan emin olun. git bundle verify bu konuda size net bir çıktı verir.
–all yerine branch adı belirtmek: git bundle create foo.bundle main yazarsanız sadece main branch’i alırsınız, tag’ler ve diğer referanslar gitmez. Tam kopya istiyorsanız --all şart.
Bundle’ı zip’lemek: Gereksiz. Bundle zaten ikili formatta ve oldukça iyi sıkıştırılmış geliyor. Üstüne gzip uygulamak çok az kazandırır, nadiren %5’in üstüne çıkar.
Dosya izinleri: Bundle dosyasını oluştururken /tmp gibi herkese açık bir yer kullanıyorsanız ve hassas kod içeriyorsa, izinlere dikkat edin:
git bundle create /tmp/myproject.bundle --all
chmod 600 /tmp/myproject.bundle
Alternatif: git archive ile Karşılaştırma
Zaman zaman git archive ile karıştırılıyor ama ikisi çok farklı şeyler yapıyor:
- git bundle: Tam Git deposunu, tüm geçmişiyle, branch’leriyle, tag’leriyle taşır. Hedef sistemde
git clone/fetch/pullyapılabilir. - git archive: Sadece dosya içeriğini, belirli bir commit anındaki halini tar veya zip olarak çıkarır. Git geçmişi yoktur.
Eğer sadece “şu an kod nasıl görünüyor” sorusunun cevabını taşıyorsanız git archive, eğer “tüm depoyu taşıyorum” diyorsanız git bundle kullanın.
Sonuç
git bundle Docker imajlarından önce var olan, internet öncesi dönemin ruhunu taşıyan bir araç ama hâlâ güncelliğini koruyor. Air-gapped ortamlar hiçbir yere gitmiyor; aksine güvenlik gereksinimlerinin arttığı bugünün dünyasında bu tür izole ortamlar daha da yaygınlaşıyor.
Özellikle Türkiye’deki kamu kurumları, bankalar ve üretim tesislerinde çalışan sistem yöneticileri için bu teknik gerçekten değerli. Bir sonraki ağ izolasyonu probleminizde ya da “nasıl taşıyacağız bu depoyu” sorusunun sorulduğu o toplantıda, cevabınız hazır olsun.
Kısaca söylersek: git bundle create, git bundle verify, git clone bundle-file dir. Üç adım, tüm depo, sıfır ağ bağlantısı.
