From 74b9d52b365830f84a7bf4ce0132b9ef526de7d4 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Tue, 20 May 2025 18:35:49 +0000 Subject: [PATCH] patchutil: move AsDiff and AsNiceDiff to patchutil Also move FormatPatch to types to avoid an import cycle. --- patchutil/patchutil.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- types/patch.go | 20 ++++++++++++++++++++ types/repo.go | 11 +++++------ appview/db/pulls.go | 71 ++--------------------------------------------------------------------- appview/state/pull.go | 11 ++++++----- appview/state/repo.go | 14 +++++++++----- knotserver/git/diff.go | 6 +++--- 7 file(s) changed, 114 insertion(s)(+), 104 deletion(s)(-) diff --git a/patchutil/patchutil.go b/patchutil/patchutil.go --- a/patchutil/patchutil.go +++ b/patchutil/patchutil.go @@ -2,6 +2,7 @@ import ( "fmt" + "log" "os" "os/exec" "regexp" @@ -9,25 +10,13 @@ "strings" "github.com/bluekeyes/go-gitdiff/gitdiff" + "tangled.sh/tangled.sh/core/types" ) -type FormatPatch struct { - Files []*gitdiff.File - *gitdiff.PatchHeader - Raw string -} - -func (f FormatPatch) ChangeId() (string, error) { - if vals, ok := f.RawHeaders["Change-Id"]; ok && len(vals) == 1 { - return vals[0], nil - } - return "", fmt.Errorf("no change-id found") -} - -func ExtractPatches(formatPatch string) ([]FormatPatch, error) { +func ExtractPatches(formatPatch string) ([]types.FormatPatch, error) { patches := splitFormatPatch(formatPatch) - result := []FormatPatch{} + result := []types.FormatPatch{} for _, patch := range patches { files, headerStr, err := gitdiff.Parse(strings.NewReader(patch)) @@ -40,7 +29,7 @@ return nil, fmt.Errorf("failed to parse patch header: %w", err) } - result = append(result, FormatPatch{ + result = append(result, types.FormatPatch{ Files: files, PatchHeader: header, Raw: patch, @@ -262,4 +251,68 @@ slices.SortFunc(patch, func(a, b *gitdiff.File) int { return strings.Compare(bestName(a), bestName(b)) }) +} + +func AsDiff(patch string) ([]*gitdiff.File, error) { + // if format-patch; then extract each patch + var diffs []*gitdiff.File + if IsFormatPatch(patch) { + patches, err := ExtractPatches(patch) + if err != nil { + return nil, err + } + var ps [][]*gitdiff.File + for _, p := range patches { + ps = append(ps, p.Files) + } + + diffs = CombineDiff(ps...) + } else { + d, _, err := gitdiff.Parse(strings.NewReader(patch)) + if err != nil { + return nil, err + } + diffs = d + } + + return diffs, nil +} + +func AsNiceDiff(patch, targetBranch string) types.NiceDiff { + diffs, err := AsDiff(patch) + if err != nil { + log.Println(err) + } + + nd := types.NiceDiff{} + nd.Commit.Parent = targetBranch + + for _, d := range diffs { + ndiff := types.Diff{} + ndiff.Name.New = d.NewName + ndiff.Name.Old = d.OldName + ndiff.IsBinary = d.IsBinary + ndiff.IsNew = d.IsNew + ndiff.IsDelete = d.IsDelete + ndiff.IsCopy = d.IsCopy + ndiff.IsRename = d.IsRename + + for _, tf := range d.TextFragments { + ndiff.TextFragments = append(ndiff.TextFragments, *tf) + for _, l := range tf.Lines { + switch l.Op { + case gitdiff.OpAdd: + nd.Stat.Insertions += 1 + case gitdiff.OpDelete: + nd.Stat.Deletions += 1 + } + } + } + + nd.Diff = append(nd.Diff, ndiff) + } + + nd.Stat.FilesChanged = len(diffs) + + return nd } diff --git a/types/patch.go b/types/patch.go new file mode 100644 --- /dev/null +++ b/types/patch.go @@ -0,0 +1,20 @@ +package types + +import ( + "fmt" + + "github.com/bluekeyes/go-gitdiff/gitdiff" +) + +type FormatPatch struct { + Files []*gitdiff.File + *gitdiff.PatchHeader + Raw string +} + +func (f FormatPatch) ChangeId() (string, error) { + if vals, ok := f.RawHeaders["Change-Id"]; ok && len(vals) == 1 { + return vals[0], nil + } + return "", fmt.Errorf("no change-id found") +} diff --git a/types/repo.go b/types/repo.go --- a/types/repo.go +++ b/types/repo.go @@ -2,7 +2,6 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" - "tangled.sh/tangled.sh/core/patchutil" ) type RepoIndexResponse struct { @@ -34,11 +33,11 @@ } type RepoFormatPatchResponse struct { - Rev1 string `json:"rev1,omitempty"` - Rev2 string `json:"rev2,omitempty"` - FormatPatch []patchutil.FormatPatch `json:"format_patch,omitempty"` - MergeBase string `json:"merge_base,omitempty"` - Patch string `json:"patch,omitempty"` + Rev1 string `json:"rev1,omitempty"` + Rev2 string `json:"rev2,omitempty"` + FormatPatch []FormatPatch `json:"format_patch,omitempty"` + MergeBase string `json:"merge_base,omitempty"` + Patch string `json:"patch,omitempty"` } type RepoTreeResponse struct { diff --git a/appview/db/pulls.go b/appview/db/pulls.go --- a/appview/db/pulls.go +++ b/appview/db/pulls.go @@ -9,7 +9,6 @@ "strings" "time" - "github.com/bluekeyes/go-gitdiff/gitdiff" "github.com/bluesky-social/indigo/atproto/syntax" "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/patchutil" @@ -203,81 +202,15 @@ return p.StackId != "" } -func (s PullSubmission) AsDiff(targetBranch string) ([]*gitdiff.File, error) { - patch := s.Patch - - // if format-patch; then extract each patch - var diffs []*gitdiff.File - if patchutil.IsFormatPatch(patch) { - patches, err := patchutil.ExtractPatches(patch) - if err != nil { - return nil, err - } - var ps [][]*gitdiff.File - for _, p := range patches { - ps = append(ps, p.Files) - } - - diffs = patchutil.CombineDiff(ps...) - } else { - d, _, err := gitdiff.Parse(strings.NewReader(patch)) - if err != nil { - return nil, err - } - diffs = d - } - - return diffs, nil -} - -func (s PullSubmission) AsNiceDiff(targetBranch string) types.NiceDiff { - diffs, err := s.AsDiff(targetBranch) - if err != nil { - log.Println(err) - } - - nd := types.NiceDiff{} - nd.Commit.Parent = targetBranch - - for _, d := range diffs { - ndiff := types.Diff{} - ndiff.Name.New = d.NewName - ndiff.Name.Old = d.OldName - ndiff.IsBinary = d.IsBinary - ndiff.IsNew = d.IsNew - ndiff.IsDelete = d.IsDelete - ndiff.IsCopy = d.IsCopy - ndiff.IsRename = d.IsRename - - for _, tf := range d.TextFragments { - ndiff.TextFragments = append(ndiff.TextFragments, *tf) - for _, l := range tf.Lines { - switch l.Op { - case gitdiff.OpAdd: - nd.Stat.Insertions += 1 - case gitdiff.OpDelete: - nd.Stat.Deletions += 1 - } - } - } - - nd.Diff = append(nd.Diff, ndiff) - } - - nd.Stat.FilesChanged = len(diffs) - - return nd -} - func (s PullSubmission) IsFormatPatch() bool { return patchutil.IsFormatPatch(s.Patch) } -func (s PullSubmission) AsFormatPatch() []patchutil.FormatPatch { +func (s PullSubmission) AsFormatPatch() []types.FormatPatch { patches, err := patchutil.ExtractPatches(s.Patch) if err != nil { log.Println("error extracting patches from submission:", err) - return []patchutil.FormatPatch{} + return []types.FormatPatch{} } return patches diff --git a/appview/state/pull.go b/appview/state/pull.go --- a/appview/state/pull.go +++ b/appview/state/pull.go @@ -305,7 +305,8 @@ } } - diff := pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch) + patch := pull.Submissions[roundIdInt].Patch + diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ LoggedInUser: user, @@ -361,14 +362,14 @@ } } - currentPatch, err := pull.Submissions[roundIdInt].AsDiff(pull.TargetBranch) + currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].Patch) if err != nil { log.Println("failed to interdiff; current patch malformed") s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") return } - previousPatch, err := pull.Submissions[roundIdInt-1].AsDiff(pull.TargetBranch) + previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].Patch) if err != nil { log.Println("failed to interdiff; previous patch malformed") s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") @@ -1143,7 +1144,7 @@ return } - branches := result.Branches + branches := result.Branches sort.Slice(branches, func(i int, j int) bool { return branches[i].Commit.Committer.When.After(branches[j].Commit.Committer.When) }) @@ -1233,7 +1234,7 @@ s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{ RepoInfo: f.RepoInfo(s, user), - SourceBranches: sourceResult.Branches, + SourceBranches: sourceBranches, TargetBranches: targetResult.Branches, }) } diff --git a/appview/state/repo.go b/appview/state/repo.go --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -2084,11 +2084,15 @@ return } - forks, err := db.GetForksByDid(s.db, user.Did) - if err != nil { - s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") - log.Println("failed to get forks", err) - return + var forks []db.Repo + if user != nil { + var err error + forks, err = db.GetForksByDid(s.db, user.Did) + if err != nil { + s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") + log.Println("failed to get forks", err) + return + } } s.pages.RepoCompare(w, pages.RepoCompareParams{ diff --git a/knotserver/git/diff.go b/knotserver/git/diff.go --- a/knotserver/git/diff.go +++ b/knotserver/git/diff.go @@ -127,7 +127,7 @@ // FormatPatch generates a git-format-patch output between two commits, // and returns the raw format-patch series, a parsed FormatPatch and an error. -func (g *GitRepo) formatSinglePatch(base, commit2 plumbing.Hash, extraArgs ...string) (string, *patchutil.FormatPatch, error) { +func (g *GitRepo) formatSinglePatch(base, commit2 plumbing.Hash, extraArgs ...string) (string, *types.FormatPatch, error) { var stdout bytes.Buffer args := []string{ @@ -222,7 +222,7 @@ return commits, nil } -func (g *GitRepo) FormatPatch(base, commit2 *object.Commit) (string, []patchutil.FormatPatch, error) { +func (g *GitRepo) FormatPatch(base, commit2 *object.Commit) (string, []types.FormatPatch, error) { // get list of commits between commir2 and base commits, err := g.commitsBetween(commit2, base) if err != nil { @@ -233,7 +233,7 @@ slices.Reverse(commits) var allPatchesContent strings.Builder - var allPatches []patchutil.FormatPatch + var allPatches []types.FormatPatch for _, commit := range commits { changeId := "" -- tangled.sh