Blog'a Dön
GitDevOpsTeam LeadershipAzure DevOpsMigration

TFS'ten Git'e Geçiş: Bir Takım Liderinin El Kitabı

Umut Korkmaz2025-07-107 min

Geçen yıl, takımımızın kaynak kontrolünü Team Foundation Version Control'den (TFVC/TFS) Azure DevOps üzerinde Git'e taşıma sürecini yönettim. Bu yalnızca teknik bir geçiş değildi — on yılı aşkın süredir merkezi sürüm kontrolü kullanan bir takım için kültürel bir dönüşümdü. İşte izlediğim el kitabı, karşılaştığım tuzaklar ve farklı yapardım dediğim şeyler.

Neden Geçtik?

TFS işini yapıyordu. Bunu net söyleyeyim. Yıllarca takımımıza iyi hizmet etti. Ama DigiFlow tek bir eski uygulamadan React, .NET 10 ve React Native içeren modern bir yığına büyüdükçe, sınırlamalar sancılı hale geldi:

  1. Özellik-başına-branch imkânsızdı. TFVC'de branch'ler ağır (heavyweight) kopyalardır. Her özellik ya da hata düzeltmesi için bir branch oluşturmak basitçe yapılmıyordu. Geliştiriciler tek bir branch üzerinde çalışıp en iyisini umuyordu.
  2. Pull request iş akışı yoktu. Kod incelemeleri birinin omzunun üzerinden ya da hiç yapılmıyordu. Değişiklikleri ana kod tabanına ulaşmadan önce gözden geçirmenin yapılandırılmış bir yolu yoktu.
  3. Çevrimdışı çalışmak imkânsızdı. TFVC, neredeyse her şey için bir sunucu bağlantısı gerektirir. Hafta sonları VPN'deki ya da dizüstü bilgisayarlarla işe gidip gelen geliştiriciler commit yapamıyordu.
  4. CI/CD entegrasyonu sınırlıydı. Modern CI/CD pipeline'ları Git kavramları — branch'ler, tag'ler, webhook'lar — etrafında kurulur. Azure Pipelines'taki TFVC desteği sonradan eklenmiş gibi hissettiriyordu.

Aşama 1: Hazırlık (2 Hafta)

Önce Eğitim, Sonra Geçiş

Takımların yaptığını gördüğüm en büyük hata, önce depoyu taşıyıp insanları sonra eğitmektir. Ben tam tersini yaptım. Tek bir satır kaynak kontrolü yapılandırmasına dokunmadan önce, iki haftamı takımı eğiterek geçirdim:

  • 1.-2. Gün: Git temelleri — commit'ler, branch'ler, merge'ler, staging alanı. Slaytlar üzerinden değil, bir alıştırma deposuyla uygulamalı olarak.
  • 3.-4. Gün: Kullanacağımız branch'leme stratejisi (yayın döngümüze uyarlanmış Git Flow).
  • 5. Gün: Azure DevOps'ta pull request'ler, kod incelemeleri ve onay iş akışları.
  • 2. Hafta: Herkesin kritik olmayan bir projede Git kullandığı bir alıştırma sprint'i.

Bu önden yapılan yatırım, bize sonradan haftalarca karışıklıktan tasarruf ettirdi.

Depo Denetimi

Geçişten önce TFS depolarımızı denetledim:

TFS Workspace
├── $/DigiFlow/Main          # 2.3 GB (binary'ler dahil!)
├── $/DigiFlow/Dev           # Main'den branch
├── $/DigiFlow/Release       # Main'den branch
├── $/DigiFlow/Archive       # 5 yıllık arşivlenmiş yayınlar
└── $/Shared/Components      # Paylaşılan kütüphaneler

Belirlenen sorunlar:

  • Kaynak kontrolündeki büyük binary'ler: derlenmiş DLL'ler, NuGet paketleri, hatta üçüncü taraf kütüphanelerin ZIP dosyaları. Git bunlarla boğulurdu.
  • 5 yıllık geçmiş: 15.000'den fazla changeset. Tüm geçmişi taşımak devasa bir depo oluştururdu.
  • Paylaşılan bileşenler: Birden fazla proje tarafından referans alınıyordu, bir NuGet paketine dönüşmesi gerekiyordu.

Aşama 2: Geçişten Önce Temizlik

Binary Kaldırma

Bir .gitignore şablonu oluşturdum ve kaldırılması gereken her binary'yi belirledim:

gitignore
# Build çıktıları
[Bb]in/
[Oo]bj/
*.dll
*.exe
*.pdb

# NuGet paketleri (restore edilir, commit edilmez)
packages/
*.nupkg

# Üçüncü taraf kütüphaneler (bunun yerine NuGet/npm kullanın)
lib/external/

# IDE dosyaları
.vs/
*.user
*.suo

Daha önce DLL olarak commit edilen paylaşılan bileşenler, özel Azure Artifacts NuGet feed'imize yayınlandı. Bu tek başına depo boyutunu 2.3 GB'tan 180 MB'a düşürdü.

Geçmiş Kararı

Tüm geçmişi taşıyıp taşımayacağımızı tartıştık. Pragmatik bir yaklaşıma karar verdim: son 2 yıllık geçmişi taşı, geri kalanı arşivle. Gerekçe:

  • Geliştiriciler nadiren birkaç aydan eski değişikliklere bakar
  • Eski geçmiş, artık var olmayan dosyalara giden yollar içeriyordu
  • Temiz bir başlangıç noktası, kusursuz bir geçmişten daha değerlidir

Arşiv için, tüm TFS geçmişinin salt okunur bir Git aynasını oluşturmak üzere git-tfs kullandım; aktif depodan ayrı ama erişilebilir.

Aşama 3: Geçiş

Asıl geçiş için git-tfs kullandım:

bash
# Son 2 yıllık geçmişle TFS'ten klonla
git tfs clone https://tfs.company.com/collection $/DigiFlow/Main   --from=CS12000   --ignore-regex="packages/|bin/|obj/"   digiflow-git

cd digiflow-git

# TFS meta verilerini temizle
git filter-branch --msg-filter   'sed "s/^git-tfs-id:.*$//"' -- --all

# .gitignore ekle
cp ../templates/.gitignore .
git add .gitignore
git commit -m "Add .gitignore for Git workflow"

# Azure DevOps Git deposuna push et
git remote add origin https://dev.azure.com/org/DigiFlow/_git/DigiFlow
git push -u origin main

Geçişin kendisi 2 yıllık geçmiş için yaklaşık 4 saat sürdü.

Aşama 4: Branch Stratejisi

Sadeleştirilmiş bir Git Flow benimsedik:

main ──────────────────────────────── Üretim (Production)
  └── develop ─────────────────────── Entegrasyon
       ├── feature/DIGI-123-new-form  Özellik branch'leri
       ├── feature/DIGI-456-api-fix
       └── bugfix/DIGI-789-null-check

Azure DevOps'taki branch politikaları iş akışını zorunlu kıldı:

yaml
# 'main' için branch politikası
- Pull request gerektir: true
- Minimum inceleyen sayısı: 2
- Bağlı  öğesi gerektir: true
- Build doğrulaması: CI pipeline'ı geçmeli
- Yorum çözümü: Tüm yorumlar çözülmeli

# 'develop' için branch politikası
- Pull request gerektir: true
- Minimum inceleyen sayısı: 1
- Build doğrulaması: CI pipeline'ı geçmeli

Aşama 5: Geçiş Anı (Cutover)

Geçiş bir Cuma akşamı yapıldı (evet, biliyorum — ama aktif geliştirmenin en az olduğu tek pencere buydu):

  1. 17:00: TFS dondurması duyuruldu — artık check-in yok
  2. 17:15: Son dakika değişikliklerini yakalamak için son git tfs fetch çalıştırıldı
  3. 17:30: Nihai durum Azure DevOps Git'e push edildi
  4. 18:00: Tüm geliştiricilerin klonlayıp build edebildiği doğrulandı
  5. 18:30: TFS deposu salt okunur yapıldı
  6. Pazartesi sabahı: Takım Git ile çalışmaya başladı

Herhangi bir sorun için hafta sonu iki geliştiriciyi hazırda beklettim. Yalnızca biri çıktı — bir geliştiricinin TFS'te, bir Git branch'ine elle uygulanması gereken commit edilmemiş değişiklikleri vardı.

Neler Ters Gitti

Merge Karışıklığı

Eğitime rağmen, ilk iki hafta sık sık merge çakışması yaşandı. TFS'te çakışmalar nadirdir, çünkü herkes aynı branch üzerinde çalışır ve kontroller sıralıdır. Git'te ise özellik branch'leri ayrışır ve merge düzenli bir etkinliktir. Özellik branch'lerini kısa ömürlü ve entegrasyon branch'ine yakın tutmak için günlük bir "develop'tan merge et" alışkanlığı ekledik.

Büyük Dosya

Bir geliştirici 200 MB'lık bir test veritabanı dosyasını commit etti. TFS'te bu can sıkıcı olurdu ama basit bir silme ile düzeltilebilirdi. Git'te ise o dosya geçmişte sonsuza kadar kalır. Dosyayı kaldırmak için git filter-repo kullanmak zorunda kaldım, bu da herkesin yeniden klonlaması gerektiği anlamına geliyordu. Bunun ardından pre-commit hook'ları ve depo boyutu uyarıları kurduk:

bash
#!/bin/sh
# .git/hooks/pre-commit - 10MB üzeri dosyaları reddet
MAX_SIZE=10485760  # bayt cinsinden 10MB

for file in $(git diff --cached --name-only); do
  size=$(wc -c < "$file" 2>/dev/null || echo 0)
  if [ "$size" -gt "$MAX_SIZE" ]; then
    echo "ERROR: $file is $(($size / 1048576))MB. Max allowed is 10MB."
    echo "Use Azure Artifacts or file storage instead."
    exit 1
  fi
done

6 Ay Sonra Sonuçlar

  • Pull request benimsenmesi: Değişikliklerin %100'ü PR'lardan geçiyor. Kod incelemesi artık kültürün bir parçası.
  • Branch sayısı: Herhangi bir anda ortalama 8-12 aktif özellik branch'i. Her biri 2-5 gün yaşıyor.
  • Deployment sıklığı: Daha iyi branch yönetimiyle aylık yayınlardan haftalığa.
  • Merge çakışmaları: İlk öğrenme eğrisinden sonra nadir. Kısa ömürlü branch'ler işin anahtarı.
  • Geliştirici memnuniyeti: Takımın her bir üyesi Git'i tercih ediyor. Tek bir kişi bile geri dönmek istemedi.

Takım Liderleri İçin Tavsiyeler

  1. Taşımadan önce eğitin. İki haftalık eğitim, iki aylık karışıklıktan tasarruf ettirir.
  2. Geçişten önce binary'leri temizleyin. Git, büyük binary'ler için tasarlanmamıştır. Onları artifact feed'lerine taşıyın.
  3. Tüm geçmişi taşımayın. İki yıl yeterlidir. Geri kalanını ayrıca arşivleyin.
  4. Branch politikalarını ilk günden zorunlu kılın. Katı başlayıp gevşemek, tersinden daha kolaydır.
  5. Merge karışıklığı bekleyin. En büyük kültür şoku budur. Sabırlı olun ve geliştiricilere ilk birkaç merge'leri boyunca eşlik edin.
  6. Pre-commit hook'ları kurun. Sorunları, kalıcı geçmiş olmadan önce engelleyin.