From 9523bfabfdf50ae40a1e7ae8c5decd2f7815d9c9 Mon Sep 17 00:00:00 2001 From: Divya Jain Date: Fri, 23 May 2025 00:02:04 +0530 Subject: [PATCH 1/2] patchutil: add ability to add trailers to commit message of a format-patch --- patchutil/patchutil.go | 86 +++++++++++ patchutil/patchutil_test.go | 279 ++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+) diff --git a/patchutil/patchutil.go b/patchutil/patchutil.go index 6920f97..54424d0 100644 --- a/patchutil/patchutil.go +++ b/patchutil/patchutil.go @@ -316,3 +316,89 @@ func AsNiceDiff(patch, targetBranch string) types.NiceDiff { return nd } + +func extractTrailersFromCommitMessage(commitMsg string) (string, []string) { + lines := strings.Split(strings.TrimSpace(commitMsg), "\n") + if len(lines) == 0 { + return "", []string{} + } + + trailerStart := len(lines) + trailers := []string{} + + // Loop over commit message lines in reverse order to find trailers + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line == "" { + continue + } + + // Check if line looks like a trailer (has a colon) + if colonIndex := strings.Index(line, ":"); colonIndex <= 0 { + // Not a trailer, stop scanning + break + } + + trailers = append([]string{line}, trailers...) + trailerStart = i + } + + // Extract message body (everything before trailers) + messageBody := "" + if trailerStart > 0 { + bodyLines := lines[:trailerStart] + for len(bodyLines) > 0 && strings.TrimSpace(bodyLines[len(bodyLines)-1]) == "" { + bodyLines = bodyLines[:len(bodyLines)-1] + } + messageBody = strings.Join(bodyLines, "\n") + } + + return messageBody, trailers +} + +func AddCommitMessageTrailers(patch string, trailers []string) string { + re := regexp.MustCompile(`(?s)\n(Subject: )(.*?)(---\n|\ndiff --git )`) + + // Find all matches + allMatches := re.FindAllStringSubmatch(patch, -1) + if len(allMatches) == 0 { + return patch + } + + result := patch + // Process matches in reverse order to avoid offset issues + for i := len(allMatches) - 1; i >= 0; i-- { + matches := allMatches[i] + if len(matches) < 4 { + continue + } + + commitMsg := matches[2] + messageBody, existingTrailers := extractTrailersFromCommitMessage(commitMsg) + + allTrailers := append([]string{}, existingTrailers...) + + for _, trailer := range trailers { + allTrailers = append(allTrailers, strings.TrimSpace(trailer)) + } + + // Reconstruct commit message + newCommitMsg := messageBody + if len(allTrailers) > 0 { + if !strings.HasSuffix(newCommitMsg, "\n\n") { + if strings.HasSuffix(newCommitMsg, "\n") { + newCommitMsg += "\n" + } else { + newCommitMsg += "\n\n" + } + } + newCommitMsg += strings.Join(allTrailers, "\n") + newCommitMsg += "\n" + } + + // Replace in result + result = strings.Replace(result, matches[0], "\n"+matches[1]+newCommitMsg+matches[3], 1) + } + + return result +} diff --git a/patchutil/patchutil_test.go b/patchutil/patchutil_test.go index d0d8c1f..a735b46 100644 --- a/patchutil/patchutil_test.go +++ b/patchutil/patchutil_test.go @@ -322,3 +322,282 @@ content }) } } + +func TestAddCommmitMessageTrailers(t *testing.T) { + tests := []struct { + name string + input string + trailers []string + expected string + }{ + { + name: "Single patch with existing trailers", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +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`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +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`, + }, + { + name: "Single patch with existing trailers and no stat", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +Signed-off-by: Author +Reviewed-by: Reviewer + +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`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH] Example patch + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 + +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`, + }, + { + name: "Multiple patches with existing trailers", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +more message body + +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +more message body + +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +more message body + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 +--- + file.txt | 2 +- + 1 file changed, 1 insertions(+), 1 deletions(-) + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + }, + { + name: "Multiple patches with existing trailers and no stat", + input: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +with long message body + +Signed-off-by: Author + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +Signed-off-by: Author +Reviewed-by: Reviewer + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + trailers: []string{`Pull-Id: Author `, `Merge-Request: 123`}, + expected: `From 3c5035488318164b81f60fe3adcd6c9199d76331 Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:01:00 +0300 +Subject: [PATCH 1/2] First patch + +with long message body + +Signed-off-by: Author +Pull-Id: Author +Merge-Request: 123 + +diff --git a/file1.txt b/file1.txt +index 123456..789012 100644 +--- a/file1.txt ++++ b/file1.txt +@@ -1 +1 @@ +-old content ++new content +-- +2.48.1 +From a9529f3b3a653329a5268f0f4067225480207e3c Mon Sep 17 00:00:00 2001 +From: Author +Date: Wed, 16 Apr 2025 11:03:11 +0300 +Subject: [PATCH 2/2] Second patch + +Signed-off-by: Author +Reviewed-by: Reviewer +Pull-Id: Author +Merge-Request: 123 + +diff --git a/file2.txt b/file2.txt +index abcdef..ghijkl 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1 @@ +-foo bar ++baz qux +-- +2.48.1`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := AddCommitMessageTrailers(tt.input, tt.trailers) + if result != tt.expected { + t.Errorf("Got:\n========\n%v\n========\nExpected:\n========\n%v\n========\n", result, tt.expected) + } + }) + } +} -- 2.47.2 From c520622ccd39225e52ccf2fabb4b74288b300269 Mon Sep 17 00:00:00 2001 From: Divya Jain Date: Thu, 15 May 2025 23:26:00 +0530 Subject: [PATCH 2/2] appview: state: add Pull-id and Merged-by commit trailers --- appview/pulls/pulls.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go index ab80aff..9494c6f 100644 --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -1915,8 +1915,18 @@ func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) { return } + actor := s.oauth.GetUser(r) // no need to check for nil as this is an authenticated request + + footers := strings.Join([]string{ + fmt.Sprintf("Pull-id: %s", pull.PullAt()), + fmt.Sprintf("Merged-by: %s", actor.Did), + }, "\n") + + pullBody := pull.Body + "\n\n" + footers + patch = patchutil.AddCommitMessageFooters(patch, footers) + // Merge the pull request - resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address) + resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pullBody, ident.Handle.String(), email.Address) if err != nil { log.Printf("failed to merge pull request: %s", err) s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") -- 2.47.2