Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

appview: use knotmirror for format-patch & interdiff

Signed-off-by: Seongmin Lee <git@boltless.me>

Seongmin Lee (May 6, 2026, 3:32 PM +0900) 48c32933 45a673f5

+132 -177
+5 -17
appview/db/pulls.go
··· 194 194 pull_at, 195 195 round_number, 196 196 patch, 197 - combined, 198 197 source_rev, 199 198 patch_blob_ref, 200 199 patch_blob_mime, 201 200 patch_blob_size 202 201 ) 203 - values (?, ?, ?, ?, ?, ?, ?, ?) 202 + values (?, ?, ?, ?, ?, ?, ?) 204 203 `, 205 204 pull.AtUri(), 206 205 i, 207 206 s.Patch, 208 - s.Combined, 209 207 s.SourceRev, 210 208 s.Blob.Ref.String(), 211 209 s.Blob.MimeType, ··· 255 253 pull_at, 256 254 round_number, 257 255 patch, 258 - combined, 259 256 source_rev, 260 257 patch_blob_ref, 261 258 patch_blob_mime, 262 259 patch_blob_size 263 260 ) 264 - values (?, ?, ?, ?, ?, ?, ?, ?) 261 + values (?, ?, ?, ?, ?, ?, ?) 265 262 `, 266 263 pull.AtUri(), 267 264 i, 268 265 s.Patch, 269 - s.Combined, 270 266 s.SourceRev, 271 267 s.Blob.Ref.String(), 272 268 s.Blob.MimeType, ··· 505 501 pull_at, 506 502 round_number, 507 503 patch, 508 - combined, 509 504 created, 510 505 source_rev, 511 506 patch_blob_ref, ··· 529 524 for rows.Next() { 530 525 var submission models.PullSubmission 531 526 var submissionCreatedStr string 532 - var submissionSourceRev, submissionCombined sql.Null[string] 527 + var submissionSourceRev sql.Null[string] 533 528 var patchBlobRef, patchBlobMime sql.Null[string] 534 529 var patchBlobSize sql.Null[int64] 535 530 err := rows.Scan( ··· 537 532 &submission.PullAt, 538 533 &submission.RoundNumber, 539 534 &submission.Patch, 540 - &submissionCombined, 541 535 &submissionCreatedStr, 542 536 &submissionSourceRev, 543 537 &patchBlobRef, ··· 554 548 555 549 if submissionSourceRev.Valid { 556 550 submission.SourceRev = submissionSourceRev.V 557 - } 558 - 559 - if submissionCombined.Valid { 560 - submission.Combined = submissionCombined.V 561 551 } 562 552 563 553 if patchBlobRef.Valid { ··· 845 835 pullAt syntax.ATURI, 846 836 newRoundNumber int, 847 837 newPatch string, 848 - combinedPatch string, 849 838 newSourceRev string, 850 839 blob *lexutil.LexBlob, 851 840 ) error { ··· 854 843 pull_at, 855 844 round_number, 856 845 patch, 857 - combined, 858 846 source_rev, 859 847 patch_blob_ref, 860 848 patch_blob_mime, 861 849 patch_blob_size 862 850 ) 863 - values (?, ?, ?, ?, ?, ?, ?, ?) 864 - `, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev, blob.Ref.String(), blob.MimeType, blob.Size) 851 + values (?, ?, ?, ?, ?, ?, ?) 852 + `, pullAt, newRoundNumber, newPatch, newSourceRev, blob.Ref.String(), blob.MimeType, blob.Size) 865 853 866 854 return err 867 855 }
-9
appview/models/pull.go
··· 298 298 RoundNumber int 299 299 Blob lexutil.LexBlob 300 300 Patch string 301 - Combined string 302 301 Comments []PullComment 303 302 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 304 303 ··· 455 454 } 456 455 457 456 return participants 458 - } 459 - 460 - func (s PullSubmission) CombinedPatch() string { 461 - if s.Combined == "" { 462 - return s.Patch 463 - } 464 - 465 - return s.Combined 466 457 } 467 458 468 459 func (s *PullSubmission) GetBlob() *lexutil.LexBlob {
+22 -67
appview/pulls/create.go
··· 24 24 comatproto "github.com/bluesky-social/indigo/api/atproto" 25 25 "github.com/bluesky-social/indigo/atproto/syntax" 26 26 lexutil "github.com/bluesky-social/indigo/lex/util" 27 + indigoxrpc "github.com/bluesky-social/indigo/xrpc" 27 28 ) 28 29 29 30 func (s *Pulls) handleBranchBasedPull( ··· 40 41 ) { 41 42 l := s.logger.With("handler", "handleBranchBasedPull", "user", userDid, "target_branch", targetBranch, "source_branch", sourceBranch, "is_stacked", isStacked) 42 43 43 - xrpcc := s.knotClient(repo.Knot) 44 + xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 44 45 45 - xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo.RepoIdentifier(), targetBranch, sourceBranch) 46 + fpOut, err := tangled.GitTempFormatPatch(r.Context(), xrpcc, repo.RepoAt().String(), "", targetBranch, sourceBranch) 46 47 if err != nil { 47 48 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 48 - l.Error("failed to call XRPC repo.compare", "xrpcerr", xrpcerr, "err", err) 49 + l.Error("failed to call XRPC git.formatPatch", "xrpcerr", xrpcerr, "err", err) 49 50 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 50 51 return 51 52 } 52 - l.Error("failed to compare", "err", err) 53 + l.Error("failed to run format-patch", "err", err) 53 54 s.pages.Notice(w, "pull", err.Error()) 54 55 return 55 56 } 56 - 57 - var comparison types.RepoFormatPatchResponse 58 - if err := json.Unmarshal(xrpcBytes, &comparison); err != nil { 59 - l.Error("failed to decode XRPC compare response", "err", err) 57 + if fpOut.Rev2 == nil { 58 + l.Error("failed to parse git.formatPatch output", "err", "rev2 is missing") 60 59 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 61 60 return 62 61 } 63 62 64 - if len(comparison.FormatPatch) == 0 { 63 + if fpOut.Patch == "" { 65 64 s.pages.Notice(w, "pull", "No commits between target and source.") 66 65 return 67 66 } 68 67 69 - sourceRev := comparison.Rev2 70 - patch := comparison.FormatPatchRaw 71 - combined := comparison.CombinedPatchRaw 68 + sourceRev := *fpOut.Rev2 69 + patch := fpOut.Patch 72 70 73 71 if err := s.validator.ValidatePatch(&patch); err != nil { 74 72 s.logger.Error("failed to validate patch", "err", err) ··· 80 78 Branch: sourceBranch, 81 79 } 82 80 83 - s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, combined, sourceRev, pullSource, isStacked, stackTitles, stackBodies) 81 + s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, sourceRev, pullSource, isStacked) 84 82 } 85 83 86 84 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, repo *models.Repo, userDid syntax.DID, title, body, targetBranch, patch string, isStacked bool, stackTitles, stackBodies map[string]string) { ··· 90 88 return 91 89 } 92 90 93 - s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, "", "", nil, isStacked, stackTitles, stackBodies) 91 + s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, "", nil, isStacked, stackTitles, stackBodies) 94 92 } 95 93 96 94 func (s *Pulls) handleForkBasedPull(w http.ResponseWriter, r *http.Request, repo *models.Repo, userDid syntax.DID, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool, stackTitles, stackBodies map[string]string) { ··· 109 107 return 110 108 } 111 109 112 - client, err := s.oauth.ServiceClient( 113 - r, 114 - oauth.WithService(fork.Knot), 115 - oauth.WithLxm(tangled.RepoHiddenRefNSID), 116 - oauth.WithDev(s.config.Core.Dev), 117 - ) 110 + xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 118 111 119 - resp, err := tangled.RepoHiddenRef( 120 - r.Context(), 121 - client, 122 - &tangled.RepoHiddenRef_Input{ 123 - ForkRef: sourceBranch, 124 - RemoteRef: targetBranch, 125 - Repo: fork.RepoAt().String(), 126 - }, 127 - ) 128 - if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 129 - s.logger.Error("failed to set hidden ref", "xrpcerr", xrpcerr, "err", err) 130 - s.pages.Notice(w, "pull", xrpcerr.Error()) 131 - return 132 - } 133 - 134 - if !resp.Success { 135 - errorMsg := "Failed to create pull request" 136 - if resp.Error != nil { 137 - errorMsg = fmt.Sprintf("Failed to create pull request: %s", *resp.Error) 138 - } 139 - s.pages.Notice(w, "pull", errorMsg) 140 - return 141 - } 142 - 143 - hiddenRef := fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch) 144 - // We're now comparing the sourceBranch (on the fork) against the hiddenRef which is tracking 145 - // the targetBranch on the target repository. This code is a bit confusing, but here's an example: 146 - // hiddenRef: hidden/feature-1/main (on repo-fork) 147 - // targetBranch: main (on repo-1) 148 - // sourceBranch: feature-1 (on repo-fork) 149 - forkXrpcc := s.knotClient(fork.Knot) 150 - 151 - forkXrpcBytes, err := tangled.RepoCompare(r.Context(), forkXrpcc, fork.RepoIdentifier(), hiddenRef, sourceBranch) 112 + fpOut, err := tangled.GitTempFormatPatch(r.Context(), xrpcc, repo.RepoAt().String(), fork.RepoAt().String(), targetBranch, sourceBranch) 152 113 if err != nil { 153 114 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 154 - l.Error("failed to call XRPC repo.compare for fork", "xrpcerr", xrpcerr, "err", err, "hidden_ref", hiddenRef) 115 + l.Error("failed to call XRPC git.formatPatch", "xrpcerr", xrpcerr, "err", err) 155 116 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 156 117 return 157 118 } 158 - l.Error("failed to compare across branches", "err", err, "hidden_ref", hiddenRef) 119 + l.Error("failed to run format-patch", "err", err) 159 120 s.pages.Notice(w, "pull", err.Error()) 160 121 return 161 122 } 162 - 163 - var comparison types.RepoFormatPatchResponse 164 - if err := json.Unmarshal(forkXrpcBytes, &comparison); err != nil { 165 - l.Error("failed to decode XRPC compare response for fork", "err", err) 123 + if fpOut.Rev2 == nil { 124 + l.Error("failed to parse git.formatPatch output", "err", "rev2 is missing") 166 125 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 167 126 return 168 127 } 169 128 170 - if len(comparison.FormatPatch) == 0 { 129 + if fpOut.Patch == "" { 171 130 s.pages.Notice(w, "pull", "No commits between target and source.") 172 131 return 173 132 } 174 133 175 - sourceRev := comparison.Rev2 176 - patch := comparison.FormatPatchRaw 177 - combined := comparison.CombinedPatchRaw 134 + sourceRev := *fpOut.Rev2 135 + patch := fpOut.Patch 178 136 179 137 if err := s.validator.ValidatePatch(&patch); err != nil { 180 138 s.logger.Error("failed to validate patch", "err", err) ··· 195 153 RepoDid: forkDid, 196 154 } 197 155 198 - s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, combined, sourceRev, pullSource, isStacked, stackTitles, stackBodies) 156 + s.createPullRequest(w, r, repo, userDid, title, body, targetBranch, patch, sourceRev, pullSource, isStacked, stackTitles, stackBodies) 199 157 } 200 158 201 159 func (s *Pulls) createPullRequest( ··· 205 163 userDid syntax.DID, 206 164 title, body, targetBranch string, 207 165 patch string, 208 - combined string, 209 166 sourceRev string, 210 167 pullSource *models.PullSource, 211 168 isStacked bool, ··· 291 248 Submissions: []*models.PullSubmission{ 292 249 { 293 250 Patch: patch, 294 - Combined: combined, 295 251 SourceRev: sourceRev, 296 252 Blob: *blob.Blob, 297 253 Created: now, ··· 509 465 { 510 466 Patch: fp.Raw, 511 467 SourceRev: fp.SHA, 512 - Combined: fp.Raw, 513 468 Blob: *blobs[i], 514 469 Created: now, 515 470 },
+27 -71
appview/pulls/resubmit.go
··· 1 1 package pulls 2 2 3 3 import ( 4 - "encoding/json" 5 4 "fmt" 6 5 "net/http" 7 6 "time" ··· 9 8 "tangled.org/core/api/tangled" 10 9 "tangled.org/core/appview/db" 11 10 "tangled.org/core/appview/models" 12 - "tangled.org/core/appview/oauth" 13 11 "tangled.org/core/appview/pages" 14 12 "tangled.org/core/appview/pages/repoinfo" 15 13 "tangled.org/core/appview/reporesolver" 16 14 "tangled.org/core/appview/xrpcclient" 17 15 "tangled.org/core/orm" 18 16 "tangled.org/core/patchutil" 19 - "tangled.org/core/types" 20 17 "tangled.org/core/xrpc" 21 18 22 19 comatproto "github.com/bluesky-social/indigo/api/atproto" 23 20 "github.com/bluesky-social/indigo/atproto/syntax" 24 21 lexutil "github.com/bluesky-social/indigo/lex/util" 22 + indigoxrpc "github.com/bluesky-social/indigo/xrpc" 25 23 ) 26 24 27 25 func (s *Pulls) ResubmitPull(w http.ResponseWriter, r *http.Request) { ··· 91 89 92 90 patch := r.FormValue("patch") 93 91 94 - s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, "", "") 92 + s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, "") 95 93 } 96 94 97 95 func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) { ··· 129 127 return 130 128 } 131 129 132 - xrpcc := s.knotClient(f.Knot) 130 + xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 133 131 134 - xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, f.RepoIdentifier(), pull.TargetBranch, pull.PullSource.Branch) 132 + fpOut, err := tangled.GitTempFormatPatch(r.Context(), xrpcc, f.RepoAt().String(), "", pull.TargetBranch, pull.PullSource.Branch) 135 133 if err != nil { 136 134 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 137 - l.Error("failed to call XRPC repo.compare", "xrpcerr", xrpcerr, "err", err, "source_branch", pull.PullSource.Branch) 138 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 135 + l.Error("failed to call XRPC git.formatPatch", "xrpcerr", xrpcerr, "err", err, "source_branch", pull.PullSource.Branch) 136 + s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") 139 137 return 140 138 } 141 - l.Error("compare request failed", "err", err, "source_branch", pull.PullSource.Branch) 139 + l.Error("failed to run format-patch", "err", err, "source_branch", pull.PullSource.Branch) 142 140 s.pages.Notice(w, "resubmit-error", err.Error()) 143 141 return 144 142 } 145 143 146 - var comparison types.RepoFormatPatchResponse 147 - if err := json.Unmarshal(xrpcBytes, &comparison); err != nil { 148 - l.Error("failed to decode XRPC compare response", "err", err) 149 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 144 + if fpOut.Rev2 == nil { 145 + l.Error("failed to parse git.formatPatch output", "err", "rev2 is missing") 146 + s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") 150 147 return 151 148 } 152 149 153 - sourceRev := comparison.Rev2 154 - patch := comparison.FormatPatchRaw 155 - combined := comparison.CombinedPatchRaw 150 + sourceRev := *fpOut.Rev2 151 + patch := fpOut.Patch 156 152 157 - s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, combined, sourceRev) 153 + s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, sourceRev) 158 154 } 159 155 160 156 func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) { ··· 192 188 return 193 189 } 194 190 195 - // update the hidden tracking branch to latest 196 - client, err := s.oauth.ServiceClient( 197 - r, 198 - oauth.WithService(forkRepo.Knot), 199 - oauth.WithLxm(tangled.RepoHiddenRefNSID), 200 - oauth.WithDev(s.config.Core.Dev), 201 - ) 202 - if err != nil { 203 - l.Error("failed to connect to knot server", "err", err, "fork_knot", forkRepo.Knot) 204 - return 205 - } 206 - 207 - resp, err := tangled.RepoHiddenRef( 208 - r.Context(), 209 - client, 210 - &tangled.RepoHiddenRef_Input{ 211 - ForkRef: pull.PullSource.Branch, 212 - RemoteRef: pull.TargetBranch, 213 - Repo: forkRepo.RepoAt().String(), 214 - }, 215 - ) 216 - if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 217 - s.logger.Error("failed to set hidden ref", "xrpcerr", xrpcerr, "err", err) 218 - s.pages.Notice(w, "resubmit-error", xrpcerr.Error()) 219 - return 220 - } 221 - if !resp.Success { 222 - l.Error("failed to update tracking ref", "err", resp.Error, "fork_ref", pull.PullSource.Branch, "remote_ref", pull.TargetBranch) 223 - s.pages.Notice(w, "resubmit-error", "Failed to update tracking ref.") 224 - return 225 - } 191 + xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 226 192 227 - hiddenRef := fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch) 228 - // extract patch by performing compare 229 - forkXrpcBytes, err := tangled.RepoCompare(r.Context(), s.knotClient(forkRepo.Knot), forkRepo.RepoIdentifier(), hiddenRef, pull.PullSource.Branch) 193 + fpOut, err := tangled.GitTempFormatPatch(r.Context(), xrpcc, pull.RepoAt.String(), forkRepo.RepoAt().String(), pull.TargetBranch, pull.PullSource.Branch) 230 194 if err != nil { 231 195 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 232 - l.Error("failed to call XRPC repo.compare for fork", "xrpcerr", xrpcerr, "err", err, "hidden_ref", hiddenRef, "source_branch", pull.PullSource.Branch) 233 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 196 + l.Error("failed to call XRPC git.formatPatch", "xrpcerr", xrpcerr, "err", err) 197 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 234 198 return 235 199 } 236 - l.Error("failed to compare branches", "err", err, "hidden_ref", hiddenRef, "source_branch", pull.PullSource.Branch) 237 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 200 + l.Error("failed to run format-patch", "err", err) 201 + s.pages.Notice(w, "pull", err.Error()) 238 202 return 239 203 } 240 204 241 - var forkComparison types.RepoFormatPatchResponse 242 - if err := json.Unmarshal(forkXrpcBytes, &forkComparison); err != nil { 243 - l.Error("failed to decode XRPC compare response for fork", "err", err) 244 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 205 + if fpOut.Rev2 == nil { 206 + l.Error("failed to parse git.formatPatch output", "err", "rev2 is missing") 207 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 245 208 return 246 209 } 247 210 248 - // Use the fork comparison we already made 249 - comparison := forkComparison 211 + sourceRev := *fpOut.Rev2 212 + patch := fpOut.Patch 250 213 251 - sourceRev := comparison.Rev2 252 - patch := comparison.FormatPatchRaw 253 - combined := comparison.CombinedPatchRaw 254 - 255 - s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, combined, sourceRev) 214 + s.resubmitPullHelper(w, r, f, syntax.DID(user.Did), pull, patch, sourceRev) 256 215 } 257 216 258 217 func (s *Pulls) resubmitPullHelper( ··· 262 221 userDid syntax.DID, 263 222 pull *models.Pull, 264 223 patch string, 265 - combined string, 266 224 sourceRev string, 267 225 ) { 268 226 l := s.logger.With("handler", "resubmitPullHelper", "user", userDid, "pull_id", pull.PullId, "target_branch", pull.TargetBranch) ··· 296 254 newRoundNumber := len(pull.Submissions) 297 255 newPatch := patch 298 256 newSourceRev := sourceRev 299 - combinedPatch := combined 300 257 301 258 client, err := s.oauth.AuthorizedClient(r) 302 259 if err != nil { ··· 340 297 return 341 298 } 342 299 343 - err = db.ResubmitPull(s.db, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev, blob.Blob) 300 + err = db.ResubmitPull(s.db, pullAt, newRoundNumber, newPatch, newSourceRev, blob.Blob) 344 301 if err != nil { 345 302 l.Error("failed to resubmit pull request in database", "err", err, "round_number", newRoundNumber) 346 303 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 543 500 pullAt := op.AtUri() 544 501 newRoundNumber := len(op.Submissions) 545 502 newPatch := np.LatestPatch() 546 - combinedPatch := np.LatestSubmission().Combined 547 503 newSourceRev := np.LatestSha() 548 504 549 505 blob, err := xrpc.RepoUploadBlob(r.Context(), client, gz(newPatch), ApplicationGzip) ··· 554 510 } 555 511 556 512 // create new round 557 - err = db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev, blob.Blob) 513 + err = db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, newSourceRev, blob.Blob) 558 514 if err != nil { 559 515 l.Error("failed to update pull in database", "err", err, "pull_id", op.PullId, "round_number", newRoundNumber) 560 516 s.pages.Notice(w, "pull-resubmit-error", "Failed to resubmit pull request. Try again later.")
+78 -13
appview/pulls/single.go
··· 1 1 package pulls 2 2 3 3 import ( 4 + "encoding/json" 4 5 "fmt" 5 6 "net/http" 6 7 "slices" ··· 119 120 l.Error("failed to parse round id", "err", err, "round_number", roundIdInt) 120 121 return 121 122 } 123 + if interdiff && roundIdInt < 1 { 124 + http.Error(w, "bad round id", http.StatusBadRequest) 125 + l.Error("failed to parse round id", "err", err, "round_number", roundIdInt) 126 + return 127 + } 122 128 123 129 var diffOpts types.DiffOpts 124 130 if d := r.URL.Query().Get("diff"); d == "split" { ··· 197 203 } 198 204 } 199 205 200 - patch := pull.Submissions[roundIdInt].CombinedPatch() 206 + ctx := r.Context() 207 + xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 208 + 201 209 var diff types.DiffRenderer 202 - diff = patchutil.AsNiceDiff(patch, pull.TargetBranch) 203 210 204 211 if interdiff { 205 - currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].CombinedPatch()) 206 - if err != nil { 207 - l.Error("failed to interdiff; current patch malformed", "err", err, "round_number", roundIdInt) 208 - s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 209 - return 212 + if pull.IsForkBased() { 213 + // calculate interdiff in appview 214 + prevPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].Patch) 215 + if err != nil { 216 + l.Error("failed to interdiff; previous patch malformed", "err", err, "round_number", roundIdInt) 217 + s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") 218 + return 219 + } 220 + currPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].Patch) 221 + if err != nil { 222 + l.Error("failed to interdiff; current patch malformed", "err", err, "round_number", roundIdInt) 223 + s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 224 + return 225 + } 226 + diff = patchutil.Interdiff(prevPatch, currPatch) 227 + } else { 228 + var sourceRepo string 229 + if pull.PullSource != nil && pull.PullSource.RepoAt != nil { 230 + sourceRepo = pull.PullSource.RepoAt.String() 231 + } 232 + xrpcBytes, err := tangled.GitTempInterdiffRevs( 233 + ctx, 234 + xrpcc, 235 + pull.TargetBranch, 236 + pull.Submissions[roundIdInt-1].SourceRev, 237 + pull.Submissions[roundIdInt].SourceRev, 238 + sourceRepo, 239 + pull.RepoAt.String(), 240 + ) 241 + if err != nil { 242 + l.Error("failed to call git.interdiffRevs", "err", err, "round_number", roundIdInt) 243 + s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff.") 244 + return 245 + } 246 + 247 + // NOTE: see comment at knotmirror/xrpc/git_interdiff_revs.go 248 + var out struct{ 249 + Patch1 string 250 + Patch2 string 251 + } 252 + if err := json.Unmarshal(xrpcBytes, &out); err != nil { 253 + l.Error("failed to decode XRPC response", "err", err) 254 + s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff.") 255 + return 256 + } 257 + 258 + diff, err = func() (*patchutil.InterdiffResult, error) { 259 + l.Debug("interdiff", "patch1", out.Patch1, "patch2", out.Patch2) 260 + patch1, err := patchutil.AsDiff(out.Patch1) 261 + if err != nil { 262 + return nil, err 263 + } 264 + patch2, err := patchutil.AsDiff(out.Patch2) 265 + if err != nil { 266 + return nil, err 267 + } 268 + return patchutil.Interdiff(patch1, patch2), nil 269 + }() 270 + if err != nil { 271 + l.Error("failed to interdiff", "err", err, "round_number", roundIdInt) 272 + s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff.") 273 + return 274 + } 210 275 } 211 - 212 - previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].CombinedPatch()) 276 + } else { 277 + // diff from merge-base to pull sourceRev 278 + out, err := tangled.GitTempCompareRevs(ctx, xrpcc, pull.RepoAt.String(), pull.TargetBranch, pull.Submissions[roundIdInt].SourceRev) 213 279 if err != nil { 214 - l.Error("failed to interdiff; previous patch malformed", "err", err, "round_number", roundIdInt) 215 - s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") 280 + l.Error("failed to get combined patch", "err", err) 281 + s.pages.Error503(w) 216 282 return 217 283 } 218 - 219 - diff = patchutil.Interdiff(previousPatch, currentPatch) 284 + diff = patchutil.AsNiceDiff(out.Patch, pull.TargetBranch) 220 285 } 221 286 222 287 err = s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{