From aedcf87338bc66df6cf03bfc101e63bf5d66cf01 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 16 Dec 2025 19:11:25 +0000 Subject: [PATCH] Ignore file rename / remove operations as they don't contribute to the codebase. (#1) --- internal/diff/analyzer.go | 7 +++++++ internal/diff/analyzer_test.go | 35 ++++++++++++++++++++++++++++++++++ internal/git/repository.go | 5 +++++ 3 files changed, 47 insertions(+) diff --git a/internal/diff/analyzer.go b/internal/diff/analyzer.go index f0b8e11..f93c59c 100644 --- a/internal/diff/analyzer.go +++ b/internal/diff/analyzer.go @@ -63,3 +63,10 @@ func IsDocumentationFile(filename string) bool { func IsMeaningfulLine(line string) bool { return !IsWhitespaceLine(line) && !IsCommentLine(line) } + +// IsRenameOrMove checks if a file change represents a rename or move operation +// rather than actual content modification. A rename/move is detected when both +// the source (fromName) and destination (toName) paths exist and differ. +func IsRenameOrMove(fromName, toName string) bool { + return fromName != "" && toName != "" && fromName != toName +} diff --git a/internal/diff/analyzer_test.go b/internal/diff/analyzer_test.go index 4da20cc..f90480b 100644 --- a/internal/diff/analyzer_test.go +++ b/internal/diff/analyzer_test.go @@ -167,3 +167,38 @@ func TestIsMeaningfulLine(t *testing.T) { }) } } + +func TestIsRenameOrMove(t *testing.T) { + tests := []struct { + name string + fromName string + toName string + expected bool + }{ + // Rename/move operations - should return true + {"simple rename", "old.go", "new.go", true}, + {"move to subdirectory", "file.go", "pkg/file.go", true}, + {"move from subdirectory", "pkg/file.go", "file.go", true}, + {"rename in subdirectory", "pkg/old.go", "pkg/new.go", true}, + {"move between directories", "src/file.go", "lib/file.go", true}, + {"complex path rename", "internal/api/v1/handler.go", "internal/api/v2/handler.go", true}, + + // NOT rename/move - should return false + {"new file", "", "new.go", false}, + {"deleted file", "old.go", "", false}, + {"modify same file", "file.go", "file.go", false}, + {"both empty", "", "", false}, + {"same path different case is not rename", "File.go", "File.go", false}, + + // Edge cases + {"whitespace in path rename", "my file.go", "my-file.go", true}, + {"deeply nested rename", "a/b/c/d/e/f.go", "a/b/c/d/e/g.go", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsRenameOrMove(tt.fromName, tt.toName) + assert.Equal(t, tt.expected, result, "IsRenameOrMove(%q, %q)", tt.fromName, tt.toName) + }) + } +} diff --git a/internal/git/repository.go b/internal/git/repository.go index 4531616..94785bf 100644 --- a/internal/git/repository.go +++ b/internal/git/repository.go @@ -397,6 +397,11 @@ func (r *Repository) getCommitStats(c *object.Commit, testPatterns []string) com filesSet := make(map[string]bool) for _, change := range changes { + // Skip rename/move operations - they don't represent actual code contribution + if diff.IsRenameOrMove(change.From.Name, change.To.Name) { + continue + } + // Get the file path var filePath string if change.To.Name != "" {