Skip to content

Commit dbbd589

Browse files
committed
MAJOR: ignore commit hashes during checks
1 parent c52b603 commit dbbd589

File tree

5 files changed

+122
-4
lines changed

5 files changed

+122
-4
lines changed

aspell/aspell.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log"
77
"os/exec"
8+
"regexp"
89
"slices"
910
"strings"
1011

@@ -121,7 +122,7 @@ func (a Aspell) checkSingle(data string, allowedWords []string) error {
121122
return nil
122123
}
123124

124-
func (a Aspell) Check(subjects []string, commitsFull []string, content []map[string]string, junitSuite junit.Interface) error {
125+
func (a Aspell) Check(subjects []string, commitsFull []string, content []map[string]string, junitSuite junit.Interface, gitHashes map[string]struct{}) error {
125126
var commitsFullData []string
126127
for _, c := range commitsFull {
127128
commit := []string{}
@@ -143,6 +144,14 @@ func (a Aspell) Check(subjects []string, commitsFull []string, content []map[str
143144
commitsFullData = append(commitsFullData, strings.Join(commit, "\n"))
144145
}
145146

147+
// Remove known git commit hashes from body portions of commit messages
148+
// so they are not flagged by spell checking. Subject lines are preserved.
149+
if len(gitHashes) > 0 {
150+
for i, c := range commitsFullData {
151+
commitsFullData[i] = removeKnownHashesFromBody(c, gitHashes)
152+
}
153+
}
154+
146155
var response strings.Builder
147156
var checks []string
148157
switch a.Mode {
@@ -194,6 +203,30 @@ func (a Aspell) Check(subjects []string, commitsFull []string, content []map[str
194203
return nil
195204
}
196205

206+
var hexStringRe = regexp.MustCompile(`[0-9a-fA-F]{7,40}`)
207+
208+
// removeKnownHashesFromBody removes known git commit hashes from the body
209+
// of a commit message, leaving the subject line intact. A hex string in the
210+
// body is removed if it is a prefix of (or equal to) any known full hash.
211+
func removeKnownHashesFromBody(message string, fullHashes map[string]struct{}) string {
212+
parts := strings.SplitN(message, "\n\n", 2)
213+
if len(parts) < 2 {
214+
return message // no body
215+
}
216+
217+
body := hexStringRe.ReplaceAllStringFunc(parts[1], func(match string) string {
218+
lower := strings.ToLower(match)
219+
for hash := range fullHashes {
220+
if strings.HasPrefix(hash, lower) {
221+
return ""
222+
}
223+
}
224+
return match
225+
})
226+
227+
return parts[0] + "\n\n" + body
228+
}
229+
197230
func checkWithAspellExec(subject string) (string, error) {
198231
cmd := exec.Command("aspell", "--lang=en", "--list")
199232
cmd.Stdin = strings.NewReader(subject)

aspell/aspell_test.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,57 @@ func Test_checkWithAspell(t *testing.T) {
3636
}
3737
}
3838

39+
func Test_removeKnownHashesFromBody(t *testing.T) {
40+
hashes := map[string]struct{}{
41+
"abc1234567890abcdef1234567890abcdef12340": {},
42+
"def9876543210fedcba9876543210fedcba98760": {},
43+
}
44+
tests := []struct {
45+
name string
46+
input string
47+
expect string
48+
}{
49+
{
50+
"no body",
51+
"subject line only",
52+
"subject line only",
53+
},
54+
{
55+
"hash in body is removed",
56+
"subject line\n\nThis reverts abc1234567890abcdef1234567890abcdef12340.",
57+
"subject line\n\nThis reverts .",
58+
},
59+
{
60+
"short hash in body is removed",
61+
"subject line\n\nThis reverts abc1234.",
62+
"subject line\n\nThis reverts .",
63+
},
64+
{
65+
"hash in subject is preserved",
66+
"revert abc1234\n\nbody text here",
67+
"revert abc1234\n\nbody text here",
68+
},
69+
{
70+
"unknown hex string preserved",
71+
"subject\n\nSee fffffff for details",
72+
"subject\n\nSee fffffff for details",
73+
},
74+
{
75+
"multiple hashes in body",
76+
"subject\n\nReverts abc1234 and def9876.",
77+
"subject\n\nReverts and .",
78+
},
79+
}
80+
for _, tt := range tests {
81+
t.Run(tt.name, func(t *testing.T) {
82+
got := removeKnownHashesFromBody(tt.input, hashes)
83+
if got != tt.expect {
84+
t.Errorf("removeKnownHashesFromBody() = %q, want %q", got, tt.expect)
85+
}
86+
})
87+
}
88+
}
89+
3990
func TestAspell_Check(t *testing.T) {
4091
type fields struct {
4192
Mode mode
@@ -109,7 +160,7 @@ func TestAspell_Check(t *testing.T) {
109160
AllowedWords: tt.fields.AllowedWords,
110161
HelpText: tt.fields.HelpText,
111162
}
112-
if err := a.Check(tt.args.subjects, tt.args.commitsFull, tt.args.content, &junit.JunitSuiteDummy{}); (err != nil) != tt.wantErr {
163+
if err := a.Check(tt.args.subjects, tt.args.commitsFull, tt.args.content, &junit.JunitSuiteDummy{}, nil); (err != nil) != tt.wantErr {
113164
t.Errorf("Aspell.Check() error = %v, wantErr %v", err, tt.wantErr)
114165
}
115166
})

aspell_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func Test_Aspell(t *testing.T) {
3737
}
3838
err = aspellCheck.Check([]string{"subject"}, []string{"body"}, []map[string]string{
3939
{filename: readme.String()},
40-
}, &junit.JunitSuiteDummy{})
40+
}, &junit.JunitSuiteDummy{}, nil)
4141
if err != nil {
4242
t.Errorf("checkWithAspell() error = %v", err)
4343
}

check.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,35 @@ func (c CommitPolicyConfig) CheckSubjectList(subjects []string, junitSuite junit
575575
}
576576

577577
const requiredCmdlineArgs = 2
578+
579+
// getGitHashes opens the git repository at repoPath and collects all commit
580+
// hashes (full, lowercase). These are used to ignore commit hash references
581+
// that appear in commit message bodies.
582+
func getGitHashes(repoPath string) map[string]struct{} {
583+
hashes := map[string]struct{}{}
584+
585+
repo, err := git.PlainOpen(repoPath)
586+
if err != nil {
587+
log.Printf("warning: could not open git repo for hash collection: %s", err)
588+
return hashes
589+
}
590+
591+
iter, err := repo.Log(&git.LogOptions{
592+
Order: git.LogOrderCommitterTime,
593+
All: true,
594+
})
595+
if err != nil {
596+
log.Printf("warning: could not get git log for hash collection: %s", err)
597+
return hashes
598+
}
599+
600+
_ = iter.ForEach(func(c *object.Commit) error {
601+
full := strings.ToLower(c.Hash.String())
602+
hashes[full] = struct{}{}
603+
return nil
604+
})
605+
606+
log.Printf("collected %d git commit hashes for body hash filtering", len(hashes))
607+
608+
return hashes
609+
}

main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ func start(junitSuite junit.Interface) {
112112
return
113113
}
114114

115-
err = aspellCheck.Check(subjects, messages, content, junitSuite)
115+
gitHashes := getGitHashes(repoPath)
116+
117+
err = aspellCheck.Check(subjects, messages, content, junitSuite, gitHashes)
116118
if err != nil {
117119
log.Print("encountered one or more commit message spelling errors")
118120
// log.Fatalf("%s\n", err)

0 commit comments

Comments
 (0)