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

patchutil: extract and validate each patch in the series

authored by anirudh.fi and committed by oppi.li d250ccfa 3d8db920

Changed files
+136 -6
patchutil
+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
··· 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 {