forked from tangled.org/core
this repo has no description

patchutil: extract and validate each patch in the series

Changed files
+136 -6
patchutil
+21 -5
patchutil/patchutil.go
···
// IsPatchValid checks if the given patch string is valid.
// It performs very basic sniffing for either git-diff or git-format-patch
-
// header lines.
+
// header lines. For format patches, it attempts to extract and validate each one.
func IsPatchValid(patch string) bool {
if len(patch) == 0 {
return false
···
}
firstLine := strings.TrimSpace(lines[0])
-
return strings.HasPrefix(firstLine, "diff ") ||
+
+
// check if it's a git diff
+
if strings.HasPrefix(firstLine, "diff ") ||
strings.HasPrefix(firstLine, "--- ") ||
strings.HasPrefix(firstLine, "Index: ") ||
strings.HasPrefix(firstLine, "+++ ") ||
-
strings.HasPrefix(firstLine, "@@ ") ||
-
strings.HasPrefix(firstLine, "From ") && strings.Contains(firstLine, " Mon Sep 17 00:00:00 2001") ||
-
strings.HasPrefix(firstLine, "From: ")
+
strings.HasPrefix(firstLine, "@@ ") {
+
return true
+
}
+
+
// check if it's format-patch
+
if strings.HasPrefix(firstLine, "From ") && strings.Contains(firstLine, " Mon Sep 17 00:00:00 2001") ||
+
strings.HasPrefix(firstLine, "From: ") {
+
// ExtractPatches already runs it through gitdiff.Parse so if that errors,
+
// it's safe to say it's broken.
+
patches, err := ExtractPatches(patch)
+
if err != nil {
+
return false
+
}
+
return len(patches) > 0
+
}
+
+
return false
}
func IsFormatPatch(patch string) bool {
+115 -1
patchutil/patchutil_test.go
···
"testing"
)
+
func TestIsPatchValid(t *testing.T) {
+
tests := []struct {
+
name string
+
patch string
+
expected bool
+
}{
+
{
+
name: `empty patch`,
+
patch: ``,
+
expected: false,
+
},
+
{
+
name: `single line patch`,
+
patch: `single line`,
+
expected: false,
+
},
+
{
+
name: `valid diff patch`,
+
patch: `diff --git a/file.txt b/file.txt
+
index abc..def 100644
+
--- a/file.txt
+
+++ b/file.txt
+
@@ -1,3 +1,3 @@
+
-old line
+
+new line
+
context`,
+
expected: true,
+
},
+
{
+
name: `valid patch starting with ---`,
+
patch: `--- a/file.txt
+
+++ b/file.txt
+
@@ -1,3 +1,3 @@
+
-old line
+
+new line
+
context`,
+
expected: true,
+
},
+
{
+
name: `valid patch starting with Index`,
+
patch: `Index: file.txt
+
==========
+
--- a/file.txt
+
+++ b/file.txt
+
@@ -1,3 +1,3 @@
+
-old line
+
+new line
+
context`,
+
expected: true,
+
},
+
{
+
name: `valid patch starting with +++`,
+
patch: `+++ b/file.txt
+
--- a/file.txt
+
@@ -1,3 +1,3 @@
+
-old line
+
+new line
+
context`,
+
expected: true,
+
},
+
{
+
name: `valid patch starting with @@`,
+
patch: `@@ -1,3 +1,3 @@
+
-old line
+
+new line
+
context
+
`,
+
expected: true,
+
},
+
{
+
name: `valid format patch`,
+
patch: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001
+
From: Author <author@example.com>
+
Date: Wed, 16 Apr 2025 11:01:00 +0300
+
Subject: [PATCH] Example patch
+
+
diff --git a/file.txt b/file.txt
+
index 123456..789012 100644
+
--- a/file.txt
+
+++ b/file.txt
+
@@ -1 +1 @@
+
-old content
+
+new content
+
--
+
2.48.1`,
+
expected: true,
+
},
+
{
+
name: `invalid format patch`,
+
patch: `From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
+
From: Author <author@example.com>
+
This is not a valid patch format`,
+
expected: false,
+
},
+
{
+
name: `not a patch at all`,
+
patch: `This is
+
just some
+
random text
+
that isn't a patch`,
+
expected: false,
+
},
+
}
+
+
for _, tt := range tests {
+
t.Run(tt.name, func(t *testing.T) {
+
result := IsPatchValid(tt.patch)
+
if result != tt.expected {
+
t.Errorf("IsPatchValid() = %v, want %v", result, tt.expected)
+
}
+
})
+
}
+
}
+
func TestSplitPatches(t *testing.T) {
tests := []struct {
name string
···
},
{
name: "No valid patches",
-
input: "This is not a patch\nJust some random text",
+
input: "This is not a \nJust some random text",
expected: []string{},
},
{