From 57b1b4d5f4d5a7829c7c87464fe52b7af1363002 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:47:26 +0000 Subject: [PATCH 1/3] Initial plan From cc529aae8b70dff0ca8e5244e07568e5e140c2c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:55:40 +0000 Subject: [PATCH 2/3] Add .mailmap support to git-metrics - Changed GetContributors() to use %aN and %cN format specifiers - Changed GetRateOfChanges() to use %aN format specifier - Added test case TestMailmapSupport to verify functionality - Format specifiers %aN and %cN automatically respect .mailmap files Co-authored-by: steffen <6301+steffen@users.noreply.github.com> --- pkg/git/git.go | 6 ++- pkg/git/git_test.go | 99 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/pkg/git/git.go b/pkg/git/git.go index cb7fd0c..fca947b 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -255,7 +255,8 @@ func ShellToUse() string { // GetContributors returns all commit authors and committers with dates from git history func GetContributors() ([]string, error) { // Execute the git command to get all contributors with their commit dates - command := exec.Command("git", "log", "--all", "--format=%an|%cn|%cd", "--date=format:%Y") + // Using %aN and %cN format specifiers to respect .mailmap files for consistent author/committer names + command := exec.Command("git", "log", "--all", "--format=%aN|%cN|%cd", "--date=format:%Y") output, err := command.Output() if err != nil { return nil, err @@ -444,7 +445,8 @@ func GetRateOfChanges() (map[int]models.RateStatistics, string, error) { } // Get all commits from current branch with timestamps, merge info, and authors - command := exec.Command("git", "log", currentBranch, "--format=%ct|%P|%an", "--reverse") + // Using %aN format specifier to respect .mailmap files for consistent author names + command := exec.Command("git", "log", currentBranch, "--format=%ct|%P|%aN", "--reverse") output, err := command.Output() if err != nil { return nil, "", fmt.Errorf("failed to get commit log: %v", err) diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go index 618b02a..98895ef 100644 --- a/pkg/git/git_test.go +++ b/pkg/git/git_test.go @@ -110,3 +110,102 @@ func TestGetGitDirectory(t *testing.T) { func mockRunGitCommand(_ bool, _ ...string) ([]byte, error) { return []byte("git version 2.35.1"), nil } + +func TestMailmapSupport(t *testing.T) { + // Create a temporary directory for the test repository + tempDir, err := os.MkdirTemp("", "git-repo-mailmap-test") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + defer os.RemoveAll(tempDir) + + // Save current directory + originalDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get current directory: %v", err) + } + defer os.Chdir(originalDir) + + // Change to temp directory + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Failed to change to temp directory: %v", err) + } + + // Initialize git repository + if err := exec.Command("git", "init").Run(); err != nil { + t.Fatalf("Failed to initialize git repository: %v", err) + } + + // Configure git user for commits + exec.Command("git", "config", "user.email", "test@example.com").Run() + exec.Command("git", "config", "user.name", "Test User").Run() + + // Create a commit with one author name/email + if err := os.WriteFile("file1.txt", []byte("content1"), 0644); err != nil { + t.Fatalf("Failed to create file1.txt: %v", err) + } + exec.Command("git", "add", "file1.txt").Run() + exec.Command("git", "-c", "user.name=John Doe", "-c", "user.email=john@example.com", "commit", "-m", "First commit").Run() + + // Create another commit with a different name/email for the same person + if err := os.WriteFile("file2.txt", []byte("content2"), 0644); err != nil { + t.Fatalf("Failed to create file2.txt: %v", err) + } + exec.Command("git", "add", "file2.txt").Run() + exec.Command("git", "-c", "user.name=J. Doe", "-c", "user.email=jdoe@example.com", "commit", "-m", "Second commit").Run() + + // Test without .mailmap - should see two different authors + contributors, err := GetContributors() + if err != nil { + t.Fatalf("GetContributors() failed: %v", err) + } + + // Count unique authors from the output + authorsWithoutMailmap := make(map[string]bool) + for _, line := range contributors { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Split(line, "|") + if len(parts) >= 1 { + authorsWithoutMailmap[parts[0]] = true + } + } + + // Create .mailmap file to map both identities to one + mailmapContent := "John Doe J. Doe \n" + if err := os.WriteFile(".mailmap", []byte(mailmapContent), 0644); err != nil { + t.Fatalf("Failed to create .mailmap: %v", err) + } + + // Test with .mailmap - should see only one author + contributors, err = GetContributors() + if err != nil { + t.Fatalf("GetContributors() with mailmap failed: %v", err) + } + + // Count unique authors with mailmap + authorsWithMailmap := make(map[string]bool) + for _, line := range contributors { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Split(line, "|") + if len(parts) >= 1 { + authorsWithMailmap[parts[0]] = true + } + } + + // With mailmap, we should have only 1 unique author (John Doe) + // Without checking this would fail if git doesn't respect --use-mailmap + if len(authorsWithMailmap) != 1 { + t.Errorf("Expected 1 unique author with mailmap, got %d: %v", len(authorsWithMailmap), authorsWithMailmap) + } + + // Verify the consolidated name is "John Doe" + if !authorsWithMailmap["John Doe"] { + t.Errorf("Expected author 'John Doe' to be present, got: %v", authorsWithMailmap) + } +} From bdbea4eaf752ba223c3dfb52e47c8b85708e9712 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:57:17 +0000 Subject: [PATCH 3/3] Improve error handling in TestMailmapSupport - Add error checks for all git commands in the test - Provide clear error messages if setup steps fail Co-authored-by: steffen <6301+steffen@users.noreply.github.com> --- pkg/git/git_test.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go index 98895ef..63d35f4 100644 --- a/pkg/git/git_test.go +++ b/pkg/git/git_test.go @@ -137,22 +137,34 @@ func TestMailmapSupport(t *testing.T) { } // Configure git user for commits - exec.Command("git", "config", "user.email", "test@example.com").Run() - exec.Command("git", "config", "user.name", "Test User").Run() + if err := exec.Command("git", "config", "user.email", "test@example.com").Run(); err != nil { + t.Fatalf("Failed to configure git user email: %v", err) + } + if err := exec.Command("git", "config", "user.name", "Test User").Run(); err != nil { + t.Fatalf("Failed to configure git user name: %v", err) + } // Create a commit with one author name/email if err := os.WriteFile("file1.txt", []byte("content1"), 0644); err != nil { t.Fatalf("Failed to create file1.txt: %v", err) } - exec.Command("git", "add", "file1.txt").Run() - exec.Command("git", "-c", "user.name=John Doe", "-c", "user.email=john@example.com", "commit", "-m", "First commit").Run() + if err := exec.Command("git", "add", "file1.txt").Run(); err != nil { + t.Fatalf("Failed to add file1.txt: %v", err) + } + if err := exec.Command("git", "-c", "user.name=John Doe", "-c", "user.email=john@example.com", "commit", "-m", "First commit").Run(); err != nil { + t.Fatalf("Failed to create first commit: %v", err) + } // Create another commit with a different name/email for the same person if err := os.WriteFile("file2.txt", []byte("content2"), 0644); err != nil { t.Fatalf("Failed to create file2.txt: %v", err) } - exec.Command("git", "add", "file2.txt").Run() - exec.Command("git", "-c", "user.name=J. Doe", "-c", "user.email=jdoe@example.com", "commit", "-m", "Second commit").Run() + if err := exec.Command("git", "add", "file2.txt").Run(); err != nil { + t.Fatalf("Failed to add file2.txt: %v", err) + } + if err := exec.Command("git", "-c", "user.name=J. Doe", "-c", "user.email=jdoe@example.com", "commit", "-m", "Second commit").Run(); err != nil { + t.Fatalf("Failed to create second commit: %v", err) + } // Test without .mailmap - should see two different authors contributors, err := GetContributors()