calculateCommitAttribution method
Future<AttributionData>
calculateCommitAttribution({
- required List<
AttributionState> states, - required List<
String> stagedFiles,
Calculate final attribution for staged files.
Implementation
Future<AttributionData> calculateCommitAttribution({
required List<AttributionState> states,
required List<String> stagedFiles,
}) async {
final cwd = getAttributionRepoRoot();
final files = <String, FileAttribution>{};
final excludedGenerated = <String>[];
final surfaces = <String>{};
final surfaceCounts = <String, int>{};
var totalNeomageChars = 0;
var totalHumanChars = 0;
// Merge file states from all sessions.
final mergedFileStates = <String, FileAttributionState>{};
final mergedBaselines = <String, BaselineEntry>{};
for (final s in states) {
surfaces.add(s.surface);
for (final entry in s.sessionBaselines.entries) {
mergedBaselines.putIfAbsent(entry.key, () => entry.value);
}
for (final entry in s.fileStates.entries) {
final existing = mergedFileStates[entry.key];
if (existing != null) {
mergedFileStates[entry.key] = FileAttributionState(
contentHash: entry.value.contentHash,
neomageContribution:
existing.neomageContribution +
entry.value.neomageContribution,
mtime: entry.value.mtime,
);
} else {
mergedFileStates[entry.key] = entry.value;
}
}
}
// Process files.
for (final file in stagedFiles) {
if (isGeneratedFileChecker?.call(file) ?? false) {
excludedGenerated.add(file);
continue;
}
final absPath = '$cwd/$file';
final fileState = mergedFileStates[file];
final baseline = mergedBaselines[file];
final fileSurface = states.isNotEmpty ? states.first.surface : 'cli';
var neomageChars = 0;
var humanChars = 0;
final deleted = await isFileDeleted(file);
if (deleted) {
if (fileState != null) {
neomageChars = fileState.neomageContribution;
} else {
final diffSize = await getGitDiffSize(file);
humanChars = diffSize > 0 ? diffSize : 100;
}
} else {
try {
final fileStat = await FileStat.stat(absPath);
if (fileStat.type == FileSystemEntityType.notFound) continue;
if (fileState != null) {
neomageChars = fileState.neomageContribution;
} else if (baseline != null) {
final diffSize = await getGitDiffSize(file);
humanChars = diffSize > 0 ? diffSize : fileStat.size;
} else {
humanChars = fileStat.size;
}
} catch (_) {
continue;
}
}
neomageChars = max(0, neomageChars);
humanChars = max(0, humanChars);
final total = neomageChars + humanChars;
final percent = total > 0 ? (neomageChars / total * 100).round() : 0;
files[file] = FileAttribution(
neomageChars: neomageChars,
humanChars: humanChars,
percent: percent,
surface: fileSurface,
);
totalNeomageChars += neomageChars;
totalHumanChars += humanChars;
surfaceCounts[fileSurface] =
(surfaceCounts[fileSurface] ?? 0) + neomageChars;
}
final totalChars = totalNeomageChars + totalHumanChars;
final neomagePercent = totalChars > 0
? (totalNeomageChars / totalChars * 100).round()
: 0;
final surfaceBreakdown = <String, SurfaceBreakdownEntry>{};
for (final entry in surfaceCounts.entries) {
final percent = totalChars > 0
? (entry.value / totalChars * 100).round()
: 0;
surfaceBreakdown[entry.key] = SurfaceBreakdownEntry(
neomageChars: entry.value,
percent: percent,
);
}
return AttributionData(
summary: AttributionSummary(
neomagePercent: neomagePercent,
neomageChars: totalNeomageChars,
humanChars: totalHumanChars,
surfaces: surfaces.toList(),
),
files: files,
surfaceBreakdown: surfaceBreakdown,
excludedGenerated: excludedGenerated,
sessions: [sessionId],
);
}