04

Git katmanı

Depo kontrolleri, commit listesi, diff'ler, blob okuma, geliştirici kimliklerinin birleştirilmesi

4 dk okuma7 bölüm

Tüm Git erişimi src/git/ içindedir. Her komut execFile("git", args, { cwd: root }) üzerinden çalışır. Arada bir kabuk (shell) yoktur; bu yüzden boşluk veya özel karakter içeren yollar güvenlidir. Windows'ta execFile, git → git.exe çözümlemesini yapar.

CleanLens'in çalıştırdığı komutlar

KomutYerAmaç
git rev-parse --is-inside-work-treegitService.tsKlasör bir depo mu?
git rev-parse HEADgitService.tsEn az bir commit'i var mı?
git log --no-merges --format=%aN|%aE|%aIgitService.tsHer commit'in yazar adı, e-postası, tarihi → geliştirici listesi
git log --no-merges --format=%H␟%P␟%aN␟%aE␟%aI [--since=…] [-n N]commitHistory.tsYeniden oynatılacak commit'ler
git diff --no-color --unified=0 -w -M -C --diff-filter=ACDMRT <parent> <commit>diffService.tsDeğişen dosyalar, satır aralıkları, blob kimlikleri — commit başına tek çağrı
git cat-file --batchdiffService.tsBir commit'in ihtiyaç duyduğu tüm blob'ların içeriği — commit başına tek çağrı
git show <sha>diffService.tsToplu okuma başarısız olursa tek bir blob için yedek yol

%aN ve %aE, .mailmap dosyasını uygular; bu yüzden depodaki bir .mailmap dosyası, kimlikleri Git düzeyinde düzeltmenin en basit yoludur.

gitService.ts

GitService üç metoda sahiptir:

  • isRepository() → herhangi bir hatada false.
  • hasCommits() → HEAD çözümlenemezse (boş depo) false.
  • listCommits() → birleştirme olmayan tüm commit'ler için RawCommit[] (authorName, authorEmail, authorDate). Yalnızca geliştirici listesini ve etkinlik istatistiklerini oluşturmak için kullanılır. maxCommits ile sınırlı değildir.

commitHistory.ts — hangi commit'ler yeniden oynatılır

listCommitsDetailed(cwd, { maxCommits, since }):

  • Her zaman --no-merges. Birleştirme commit'leri kendilerine ait yeni kod eklemez; içerikleri birleştirilen commit'ler tarafından kapsanır.
  • since → --since=<value> (Git'in kabul ettiği her değer: 2026-01-01, "6 months ago", …).
  • maxCommits > 0 → -n <maxCommits> (en yeni N). 0 veya since olmadan ayarlanmamış → tüm geçmiş.
  • Sonuç en eskiden başlayacak şekilde ters çevrilir, böylece motor zamanda ileri doğru yürüyebilir.
  • Her commit'in parents listesi vardır. Kök commit'in ebeveyni yoktur ve Git'in boş ağacıyla (4b825dc6…) karşılaştırılır.

Varsayılan pencere en yeni 500 commit'tir (analyzeRepository.ts içinde DEFAULT_MAX_COMMITS).

diffService.ts — bir commit'te ne değişti

commitChanges(parent, commit)

Tek bir git diff çalıştırır ve bunu CommitFileChange[] olarak ayrıştırır:

AlanAnlamı
statusadded, modified, deleted, renamed, copied
path / oldPathSonraki / önceki yol (yeniden adlandırma ve kopyalamalar)
blobBefore / blobAfterBlob kimlikleri; dosya o tarafta yoksa null
addedCommit'in eklediği veya değiştirdiği satır aralıkları (sonraki dosyada)
removedCommit'in sildiği veya değiştirdiği satır aralıkları (önceki dosyada)
binaryİkili dosya — atlanır

Bayraklar adalet için seçilmiştir:

  • --unified=0 — bağlam satırı yok; böylece hunk başlıkları değişen aralıkları tam olarak verir.
  • -w — boşlukları yok say. Yeniden biçimlendirme (girinti, satır sonu boşlukları) satır değişikliği sayılmaz, dolayısıyla sorumluluğu aktaramaz.
  • -M -C — yeniden adlandırma ve kopyalamaları tespit et. Bir dosyayı taşımak, taşıyanı içeriğinin yazarı yapmaz.
  • --diff-filter=ACDMRT — eklenen, kopyalanan, silinen, değiştirilen, yeniden adlandırılan, tipi değişen.

Ayrıştırıcı diff --git, new file mode, deleted file mode, rename from/to, copy from/to, index <a>..<b>, ---/+++ ve @@ hunk başlıklarını okur. Metni \r?\n ile böler; bu yüzden Windows Git'inin CRLF çıktısı doğru ayrıştırılır.

blobs(shas)

Birçok blob'u tek bir git cat-file --batch süreciyle okur: tüm kimlikleri stdin'e yazar ve ham baytlardan <sha> blob <size>\n<content>\n kayıtlarını ayrıştırır. Bir şey yanlış görünürse (eksik kayıt, hatalı boyut), sonucu atar ve her blob için git show <sha> yoluna döner. Bu yol daha yavaştır ama her zaman doğrudur.

Yardımcılar

  • rangeLineCount(ranges) — aralıklardaki toplam satır (analiz edilen satırlar için kullanılır).
  • lineInRanges(line, ranges), spanOverlapsRanges(start, end, ranges) — atama güvenine karar vermek için kullanılır.

contributors.ts — geliştiriciler kimler

aggregateContributors(commits, { identities, mergeSameName }) commit'leri geliştiricilere dönüştürür.

1. adım — e-postaya göre gruplama

Her commit küçük harfe çevrilmiş e-postasına göre gruplanır. E-posta yapılandırmadaki developers listesinde varsa, bunun yerine o girdinin grubuna sabitlenir; böylece listelenen tüm e-postalar tek bir kişi olur.

CleanLens her grup için şunları izler: görülen adlar, e-postalar, aktif günler (commit içeren farklı UTC takvim günleri), ilk ve son commit tarihleri ve commit sayısı. Birincil ad, gruptaki en eski commit'teki addır.

2. adım — olası kopyaları birleştirme (varsayılan açık)

mergeSameNameAuthors false değilse, herhangi bir birleştirme anahtarını paylaşan gruplar birleştirilir (union-find):

AnahtarNeyden oluşturulurYakaladığı örnek
exact:Görünen ad; kırpılmış, küçük harfe çevrilmiş, boşlukları daraltılmışİki farklı e-postada Ayman Khalil
name:Harf/rakam olmayan her şey silinmiş ve sondaki rakamlar atılmış ad; yalnızca ≥ 5 karakterseMUSAALAHMED4 ↔ MUSAALAHMED ↔ musa alahmed
mail:E-posta tanıtıcısı: yerel kısımdaki son + işaretinden sonraki metin veya yerel kısmın tamamı; yalnızca ≥ 3 karakterse ve genel değilseayman@work.com ↔ ayman@gmail.com; 1234+ayman@users.noreply.github.com

Yanlış birleştirmelere karşı korumalar:

  • 5 karakterden kısa daraltılmış adlar yok sayılır (user1 / user2, Anas ile Anas Daas).
  • Genel tanıtıcılar yok sayılır: noreply, no-reply, git, github, gitlab, admin, dev, me, hello, info, contact, mail, email, user, users, name, test.

3. adım — etiket

  • Yapılandırmadaki displayName her zaman kazanır.
  • Tek adı olan grup o adı kullanır.
  • Farklı adları birleştiren grup hepsini, en eskisi önce olacak şekilde gösterir: "Primary Name - Other Name".

Geliştiriciler commit sayısına göre sıralanır ve bu sırayla dev-1, dev-2, … kimliklerini alır. Kimlikler yalnızca tek bir rapor içinde sabittir.

blameService.ts (eski)

BlameService ve parsePorcelain(), git blame --line-porcelain -w -M çıktısını ayrıştırır. Önceki "HEAD blame" motoruna aittirler ve artık işlem hattında kullanılmazlar. Testler onları kapsadığı ve gelecekteki özellikler için yararlı oldukları için duruyorlar.

Bu katmanı özelleştirmek

  • Kimlikleri birleştirmek → yapılandırmada developers veya bir .mailmap (11 — Özelleştirme kılavuzu).
  • Yeniden oynatma penceresini değiştirmek → analysis.maxCommits, analysis.since, --full-history.
  • Otomatik birleştirmeyi kapatmak → mergeSameNameAuthors: false.