diff --git a/patchutil/patchutil.go b/patchutil/patchutil.go index f9222641..eb9281f3 100644 --- a/patchutil/patchutil.go +++ b/patchutil/patchutil.go @@ -40,7 +40,7 @@ func ExtractPatches(formatPatch string) ([]FormatPatch, error) { // 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 @@ -52,13 +52,29 @@ func IsPatchValid(patch string) bool { } 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 { diff --git a/patchutil/patchutil_test.go b/patchutil/patchutil_test.go index d25f3503..d0d8c1f1 100644 --- a/patchutil/patchutil_test.go +++ b/patchutil/patchutil_test.go @@ -5,6 +5,120 @@ import ( "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 +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 +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 @@ -18,7 +132,7 @@ func TestSplitPatches(t *testing.T) { }, { 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{}, }, {