Atama motoru
Commit'lerin yeniden oynatılması, parmak izleri, atama durumu ve güven düzeyi
Atama motoru her ihlali kimin eklediğine karar verir. src/attribution/ içinde yer alır ve CleanLens'i blame tabanlı araçlardan ayıran şeyin özüdür.
Tek paragrafta fikir
CleanLens analiz edilen her commit için değişen her dosyayı commit'ten önce (ebeveyn sürümü) ve sonra analiz eder, her ihlale bir parmak izi verir ve iki kümeyi karşılaştırır. Yeni parmak izleri commit tarafından eklenmiştir; kaybolanlar onun tarafından düzeltilmiştir; ortak olanlar zaten vardı. Yürüyüş bittikten sonra mevcut koddaki her ihlal, onu ekleyen commit'i (ve geliştiriciyi) bulmak için parmak iziyle aranır.
1. adım — commit'lerde yürümek (commitAttribution.ts)
attributeCommits(root, config, opts):
- Yeniden oynatılacak commit'leri listeler (birleştirme olmayan en yeni N, en eskiden başlayarak; bkz. 04 — Git katmanı).
- 12 commit'e kadar paralel işler (
COMMIT_CONCURRENCY). İşin çoğugit'i beklemektir; bu yüzden paralellik çok yardımcı olur. - Her commit için önbellekte sonuç varsa onu kullanır; yoksa commit'in katkısını hesaplar (2. adım) ve önbelleğe yazar.
- Katkıları kesinlikle en eskiden başlayarak uygular; böylece köken kaydı her zaman en son durumu yansıtır.
- Çalıştırma iptal edilirse ilk eksik katkıda durur ve sonucu
abortedolarak işaretler.
2. adım — tek bir commit (computeCommit)
parent (or empty tree) ──git diff──▶ changed files + added/removed line ranges
│ filter
▼
analyzable, not excluded, not binary, not whitespace-only
│ git cat-file --batch
▼
before blob after blob
│ │
analyzeRevision() analyzeRevision()
│ │
fingerprints (B) fingerprints (A)
\ /
compare the two setsDosyalar şu durumlarda atlanır: ikiliyse, analizörü yoksa, hariç tutma listesiyle eşleşiyorsa, yalnızca boşluk değişikliğiyse (-w altında satır aralığı yok) veya üretilmiş başlığı varsa (excludeGenerated açıkken).
Kalan her dosya için:
- Analiz edilen satırlar += commit'in
addedaralıklarındaki satır sayısı. Puanların bölündüğü "geliştiricinin yazdığı kod" budur. - Önceki ve sonraki blob'lar analiz edilir. Sonuçlar tüm çalıştırma boyunca blob kimliğine göre önbelleğe alınır; böylece ardışık commit'lerin paylaştığı bir dosya sürümü yalnızca bir kez analiz edilir.
- Ardından kümeler karşılaştırılır:
| Parmak izi nerede… | Sınıflandırma | Etkisi |
|---|---|---|
| yalnızca sonrasında | introduced (eklendi) | Yazara yazılır (güven high/medium ise) |
| önce ve sonra | existing (mevcut) | Sayılır, asla kimseye yazılmaz |
| yalnızca öncesinde | fixed (düzeltildi) | Yazarın hanesine artı yazılır (fixedWeighted) |
Sonraki sürüm analiz edilemezse (ayrıştırma hatası, eksik blob), önceki sürümdeki her ihlal o commit için atanamayan (unattributed) olarak sayılır.
3. adım — atama güveni
Eklenen her ihlal classifyConfidence() ile bir güven düzeyi alır:
| Güven | Ne zaman | Puana girer mi? |
|---|---|---|
| high | Dosya yenidir (eklenmiş, kopyalanmış, kök commit), veya ihlalin başlangıç satırı değişen bir aralık içindedir, veya satır kapsamı değişen bir aralıkla çakışır | ✅ |
| medium | Değişen satırlarda değildir, ama adlandırılmış bir sembole (fonksiyon/sınıf) aittir ve commit dosyada satır değiştirmiştir | ✅ |
| low | Düzenlemenin buna neden olduğuna dair kanıt yok (örn. var olan bir dosyanın ebeveyn sürümü okunamadı) | ❌ yalnızca gösterilir |
Yalnızca high ve medium sayılır (scoringConfig.ts içinde SCORING_CONFIDENCES).
Bunun neden önemli olduğu, örneklerle:
- Mehmet var olan bir fonksiyona 60 satır ekler → fonksiyon artık
maxFunctionLineskuralını ihlal eder. Fonksiyonun kapsamı Mehmet'in eklediği satırlarla çakışır → high, Mehmet'e yazılır. - Zeynep bir fonksiyonun derinlerine tek bir
ifekler ve fonksiyon karmaşıklık sınırını aşar. Bulgu fonksiyon başlığında başlar (değişmemiş), ama kapsamı Zeynep'in satırıyla çakışır → high, Zeynep'e yazılır. - Can bir dosyayı yeniden biçimlendirir.
-waltında hiçbir şey değişmemiştir → dosya atlanır. Kimse bir şey devralmaz. - Elif
utils.jsdosyasını düzenlemedenhelpers.jsolarak yeniden adlandırır.-Myeniden adlandırmayı tespit eder, ama parmak izleri yolu içerir; bu yüzden her ihlal yeni yol altında yeni görünür. Yeniden adlandırma hiçbir satırı değiştirmemiştir (addedaralığı yok); bu yüzden her biri low güven alır ve Elif'e yazılmaz. Mevcut uygulamanın yan etkileri: eski yolun parmak izleri Elif'in gösterilen toplamlarındafixedolarak sayılır (düzeltilen sayısı, net kalite etkisi; puan değil) ve raporda bu ihlaller Elif'in commit'iyleintroduced/lowolarak görünür. Asıl yazarın onlarla bağlantısı kopar.
4. adım — parmak izleri (fingerprint.ts)
Bir parmak izi, satır numaralarını (üstünde biri düzenleme yaptığında değişirler) kullanmadan iki sürümde "aynı ihlali" tanımlar:
fingerprint = FNV-1a( ruleId + normalizedPath + symbolName + normalizedContext )- normalizedPath:
/ayıraçları, başta./yok. - symbolName: biliniyorsa kapsayan fonksiyon/sınıf.
- normalizedContext:
startLineilemin(endLine, startLine + 2)arasındaki kaynak satırları (en çok 3 satır);//ve#yorumları kaldırılmış ve boşluklar daraltılmış.
Sonuçları:
- Bir ihlalin üstüne satır eklemek → aynı parmak izi (hâlâ
existing). - O satırlardaki boşlukları veya yorumları düzenlemek → aynı parmak izi.
- İhlalin ilk satırlarındaki kodu düzenlemek → yeni bir parmak izi: eskisi aynı commit tarafından
fixed, yenisiintroducedolur. Yazara hem eksi hem artı yazılır; net etki kabaca nötrdür. - Kapsayan fonksiyonu yeniden adlandırmak → yeni parmak izi (sembol değişti).
5. adım — mevcut kodla birleştirme (attributeReport.ts)
Yürüyüşten sonra HEAD taramasındaki her ihlal son durumunu alır:
| Koşul | Durum | Güven | developerId |
|---|---|---|---|
| Yol hariç tutma listesiyle eşleşiyor | excluded | — | yok |
| Dosya diskten okunamıyor | unattributed | low | yok |
Parmak izinin son kökeni introduced | introduced | kökeninki | high/medium ise yazar |
| Aksi halde (pencereden eski veya yürüyüşte görülmemiş) | existing | low | yok |
introducedBy, introducedCommit ve introducedAt, köken biliniyorsa düşük güvende bile doldurulur; böylece commit'i yine de görebilirsiniz.
6. adım — geliştirici başına toplamlar
Her geliştirici için (küçük harfli e-posta → geliştirici kimliği ile eşleştirilir) motor commit'leri üzerinden toplar:
| Alan | Anlamı |
|---|---|
analyzedLines | Analiz edilebilir dosyalarda eklediği/değiştirdiği satırlar |
introducedWeighted[category] | Kategori başına, high/medium güvenle eklediği ihlallerin ağırlık toplamı |
fixedWeighted | Commit'lerinin kaldırdığı ihlallerin ağırlık toplamı |
counts.introduced | Eklenenlerin tümü (her güven düzeyi) |
counts.introducedScored | high/medium güvenle eklenenler |
counts.fixed, counts.existing, counts.unattributed | Her commit'te sınıflandırıldığı gibi |
Bunların tarihsel sayılar olduğuna dikkat edin: eklenip sonra düzeltilen ihlalleri de içerir. Bu yüzden bir geliştiricinin "Yeni ihlaller" sayısı, kodda hâlâ duran ihlallerinin sayısından büyük olabilir.
Bu toplamlar puanı besler: bkz. 08 — Puanlama.
Garantiler ve uç durumlar
| Durum | Davranış |
|---|---|
| Kök commit (ebeveyn yok) | Boş ağaçla karşılaştırılır; her ihlal high güvenle introduced |
| Birleştirme commit'leri | Atlanır (--no-merges) |
| Yalnızca boşluk değişikliği | Yok sayılır |
| Yeniden adlandırma | -M ile tespit edilir; yeniden adlandırana yazılmaz (low güven), ama yeniden adlandırma asıl yazarla bağlantıyı koparır ve yeniden adlandırana "fixed" kredisi ekler (yalnızca gösterim) |
| Kopyalama | -C ile tespit edilir; yeni dosya olarak ele alınır, bu yüzden kopyadaki ihlaller kopyalayana yazılır (high güven) |
| Silinen dosya | İhlalleri silen kişi tarafından fixed olur |
| Geliştirici listesinde olmayan yazar | Proje toplamlarında sayılır, hiçbir geliştiricide sayılmaz |
| Pencereden eski ihlal | existing; asla kimseye yazılmaz |
| İptal edilen çalıştırma | Boşluktan sonraki katkılar yok sayılır |
Bu davranışı sabitleyen testler
test/integration.test.ts gerçek geçici Git depoları oluşturur ve şunları doğrular: önceden var olan bir ihlal dosyayı sonradan düzenleyene yazılmaz; değiştirilen satırlardaki bir ihlal yazılır; kaldırılan bir ihlal düzeltildi olarak kaydedilir; boşluk yeniden biçimlendirmesi hiçbir şey aktarmaz; üretilmiş ve hariç tutulmuş dosyalar yok sayılır; kök commit güvenlidir; ve küçük ama temiz bir katkıcı 100 almaz ve sıralamaya girmez.