+21
-5
patchutil/patchutil.go
+21
-5
patchutil/patchutil.go
···
40
40
41
41
// IsPatchValid checks if the given patch string is valid.
42
42
// It performs very basic sniffing for either git-diff or git-format-patch
43
-
// header lines.
43
+
// header lines. For format patches, it attempts to extract and validate each one.
44
44
func IsPatchValid(patch string) bool {
45
45
if len(patch) == 0 {
46
46
return false
···
52
52
}
53
53
54
54
firstLine := strings.TrimSpace(lines[0])
55
-
return strings.HasPrefix(firstLine, "diff ") ||
55
+
56
+
// check if it's a git diff
57
+
if strings.HasPrefix(firstLine, "diff ") ||
56
58
strings.HasPrefix(firstLine, "--- ") ||
57
59
strings.HasPrefix(firstLine, "Index: ") ||
58
60
strings.HasPrefix(firstLine, "+++ ") ||
59
-
strings.HasPrefix(firstLine, "@@ ") ||
60
-
strings.HasPrefix(firstLine, "From ") && strings.Contains(firstLine, " Mon Sep 17 00:00:00 2001") ||
61
-
strings.HasPrefix(firstLine, "From: ")
61
+
strings.HasPrefix(firstLine, "@@ ") {
62
+
return true
63
+
}
64
+
65
+
// check if it's format-patch
66
+
if strings.HasPrefix(firstLine, "From ") && strings.Contains(firstLine, " Mon Sep 17 00:00:00 2001") ||
67
+
strings.HasPrefix(firstLine, "From: ") {
68
+
// ExtractPatches already runs it through gitdiff.Parse so if that errors,
69
+
// it's safe to say it's broken.
70
+
patches, err := ExtractPatches(patch)
71
+
if err != nil {
72
+
return false
73
+
}
74
+
return len(patches) > 0
75
+
}
76
+
77
+
return false
62
78
}
63
79
64
80
func IsFormatPatch(patch string) bool {
+115
-1
patchutil/patchutil_test.go
+115
-1
patchutil/patchutil_test.go
···
5
5
"testing"
6
6
)
7
7
8
+
func TestIsPatchValid(t *testing.T) {
9
+
tests := []struct {
10
+
name string
11
+
patch string
12
+
expected bool
13
+
}{
14
+
{
15
+
name: `empty patch`,
16
+
patch: ``,
17
+
expected: false,
18
+
},
19
+
{
20
+
name: `single line patch`,
21
+
patch: `single line`,
22
+
expected: false,
23
+
},
24
+
{
25
+
name: `valid diff patch`,
26
+
patch: `diff --git a/file.txt b/file.txt
27
+
index abc..def 100644
28
+
--- a/file.txt
29
+
+++ b/file.txt
30
+
@@ -1,3 +1,3 @@
31
+
-old line
32
+
+new line
33
+
context`,
34
+
expected: true,
35
+
},
36
+
{
37
+
name: `valid patch starting with ---`,
38
+
patch: `--- a/file.txt
39
+
+++ b/file.txt
40
+
@@ -1,3 +1,3 @@
41
+
-old line
42
+
+new line
43
+
context`,
44
+
expected: true,
45
+
},
46
+
{
47
+
name: `valid patch starting with Index`,
48
+
patch: `Index: file.txt
49
+
==========
50
+
--- a/file.txt
51
+
+++ b/file.txt
52
+
@@ -1,3 +1,3 @@
53
+
-old line
54
+
+new line
55
+
context`,
56
+
expected: true,
57
+
},
58
+
{
59
+
name: `valid patch starting with +++`,
60
+
patch: `+++ b/file.txt
61
+
--- a/file.txt
62
+
@@ -1,3 +1,3 @@
63
+
-old line
64
+
+new line
65
+
context`,
66
+
expected: true,
67
+
},
68
+
{
69
+
name: `valid patch starting with @@`,
70
+
patch: `@@ -1,3 +1,3 @@
71
+
-old line
72
+
+new line
73
+
context
74
+
`,
75
+
expected: true,
76
+
},
77
+
{
78
+
name: `valid format patch`,
79
+
patch: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
80
+
From: Author <author@example.com>
81
+
Date: Wed, 16 Apr 2025 11:01:00 +0300
82
+
Subject: [PATCH] Example patch
83
+
84
+
diff --git a/file.txt b/file.txt
85
+
index 123456..789012 100644
86
+
--- a/file.txt
87
+
+++ b/file.txt
88
+
@@ -1 +1 @@
89
+
-old content
90
+
+new content
91
+
--
92
+
2.48.1`,
93
+
expected: true,
94
+
},
95
+
{
96
+
name: `invalid format patch`,
97
+
patch: `From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
98
+
From: Author <author@example.com>
99
+
This is not a valid patch format`,
100
+
expected: false,
101
+
},
102
+
{
103
+
name: `not a patch at all`,
104
+
patch: `This is
105
+
just some
106
+
random text
107
+
that isn't a patch`,
108
+
expected: false,
109
+
},
110
+
}
111
+
112
+
for _, tt := range tests {
113
+
t.Run(tt.name, func(t *testing.T) {
114
+
result := IsPatchValid(tt.patch)
115
+
if result != tt.expected {
116
+
t.Errorf("IsPatchValid() = %v, want %v", result, tt.expected)
117
+
}
118
+
})
119
+
}
120
+
}
121
+
8
122
func TestSplitPatches(t *testing.T) {
9
123
tests := []struct {
10
124
name string
···
18
132
},
19
133
{
20
134
name: "No valid patches",
21
-
input: "This is not a patch\nJust some random text",
135
+
input: "This is not a \nJust some random text",
22
136
expected: []string{},
23
137
},
24
138
{