Monorepo for Tangled tangled.org

appview,knotserver: produce combined patch in comparisons

this is calculated by the knotserver in sh.tangled.repo.compare and
cached by the appview in pull submissions, this cannot be calculated on
the appview side with just the format-patch because this calculation
requires a git-index.

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 706c9a9e 779b15d2

verified
Changed files
+95 -67
appview
db
models
pulls
repo
knotserver
types
+9
appview/db/db.go
··· 1094 }) 1095 conn.ExecContext(ctx, "pragma foreign_keys = on;") 1096 1097 return &DB{db}, nil 1098 } 1099
··· 1094 }) 1095 conn.ExecContext(ctx, "pragma foreign_keys = on;") 1096 1097 + // knots may report the combined patch for a comparison, we can store that on the appview side 1098 + // (but not on the pds record), because calculating the combined patch requires a git index 1099 + runMigration(conn, "add-combined-column-submissions", func(tx *sql.Tx) error { 1100 + _, err := tx.Exec(` 1101 + alter table pull_submissions add column combined text; 1102 + `) 1103 + return err 1104 + }) 1105 + 1106 return &DB{db}, nil 1107 } 1108
+21 -17
appview/db/pulls.go
··· 90 pull.ID = int(id) 91 92 _, err = tx.Exec(` 93 - insert into pull_submissions (pull_at, round_number, patch, source_rev) 94 - values (?, ?, ?, ?) 95 - `, pull.PullAt(), 0, pull.Submissions[0].Patch, pull.Submissions[0].SourceRev) 96 return err 97 } 98 ··· 313 pull_at, 314 round_number, 315 patch, 316 created, 317 source_rev 318 from ··· 332 333 for rows.Next() { 334 var submission models.PullSubmission 335 - var createdAt string 336 - var sourceRev sql.NullString 337 err := rows.Scan( 338 &submission.ID, 339 &submission.PullAt, 340 &submission.RoundNumber, 341 &submission.Patch, 342 - &createdAt, 343 - &sourceRev, 344 ) 345 if err != nil { 346 return nil, err 347 } 348 349 - createdTime, err := time.Parse(time.RFC3339, createdAt) 350 - if err != nil { 351 - return nil, err 352 } 353 - submission.Created = createdTime 354 355 - if sourceRev.Valid { 356 - submission.SourceRev = sourceRev.String 357 } 358 359 submissionMap[submission.ID] = &submission ··· 590 return err 591 } 592 593 - func ResubmitPull(e Execer, pull *models.Pull, newPatch, sourceRev string) error { 594 newRoundNumber := len(pull.Submissions) 595 _, err := e.Exec(` 596 - insert into pull_submissions (pull_at, round_number, patch, source_rev) 597 - values (?, ?, ?, ?) 598 - `, pull.PullAt(), newRoundNumber, newPatch, sourceRev) 599 600 return err 601 }
··· 90 pull.ID = int(id) 91 92 _, err = tx.Exec(` 93 + insert into pull_submissions (pull_at, round_number, patch, combined, source_rev) 94 + values (?, ?, ?, ?, ?) 95 + `, pull.PullAt(), 0, pull.Submissions[0].Patch, pull.Submissions[0].Combined, pull.Submissions[0].SourceRev) 96 return err 97 } 98 ··· 313 pull_at, 314 round_number, 315 patch, 316 + combined, 317 created, 318 source_rev 319 from ··· 333 334 for rows.Next() { 335 var submission models.PullSubmission 336 + var submissionCreatedStr string 337 + var submissionSourceRev, submissionCombined sql.NullString 338 err := rows.Scan( 339 &submission.ID, 340 &submission.PullAt, 341 &submission.RoundNumber, 342 &submission.Patch, 343 + &submissionCombined, 344 + &submissionCreatedStr, 345 + &submissionSourceRev, 346 ) 347 if err != nil { 348 return nil, err 349 } 350 351 + if t, err := time.Parse(time.RFC3339, submissionCreatedStr); err == nil { 352 + submission.Created = t 353 + } 354 + 355 + if submissionSourceRev.Valid { 356 + submission.SourceRev = submissionSourceRev.String 357 } 358 359 + if submissionCombined.Valid { 360 + submission.Combined = submissionCombined.String 361 } 362 363 submissionMap[submission.ID] = &submission ··· 594 return err 595 } 596 597 + func ResubmitPull(e Execer, pull *models.Pull, newPatch, combined, sourceRev string) error { 598 newRoundNumber := len(pull.Submissions) 599 _, err := e.Exec(` 600 + insert into pull_submissions (pull_at, round_number, patch, combined, source_rev) 601 + values (?, ?, ?, ?, ?) 602 + `, pull.PullAt(), newRoundNumber, newPatch, combined, sourceRev) 603 604 return err 605 }
+9
appview/models/pull.go
··· 134 // content 135 RoundNumber int 136 Patch string 137 Comments []PullComment 138 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 139 ··· 261 } 262 263 return participants 264 } 265 266 type Stack []*Pull
··· 134 // content 135 RoundNumber int 136 Patch string 137 + Combined string 138 Comments []PullComment 139 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 140 ··· 262 } 263 264 return participants 265 + } 266 + 267 + func (s PullSubmission) CombinedPatch() string { 268 + if s.Combined == "" { 269 + return s.Patch 270 + } 271 + 272 + return s.Combined 273 } 274 275 type Stack []*Pull
+23 -40
appview/pulls/pulls.go
··· 142 stack, _ := r.Context().Value("stack").(models.Stack) 143 abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*models.Pull) 144 145 - totalIdents := 1 146 - for _, submission := range pull.Submissions { 147 - totalIdents += len(submission.Comments) 148 - } 149 - 150 - identsToResolve := make([]string, totalIdents) 151 - 152 - // populate idents 153 - identsToResolve[0] = pull.OwnerDid 154 - idx := 1 155 - for _, submission := range pull.Submissions { 156 - for _, comment := range submission.Comments { 157 - identsToResolve[idx] = comment.OwnerDid 158 - idx += 1 159 - } 160 - } 161 - 162 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 163 branchDeleteStatus := s.branchDeleteStatus(r, f, pull) 164 resubmitResult := pages.Unknown ··· 456 return 457 } 458 459 - patch := pull.Submissions[roundIdInt].Patch 460 diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 461 462 s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ ··· 507 return 508 } 509 510 - currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].Patch) 511 if err != nil { 512 log.Println("failed to interdiff; current patch malformed") 513 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 514 return 515 } 516 517 - previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].Patch) 518 if err != nil { 519 log.Println("failed to interdiff; previous patch malformed") 520 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") ··· 716 717 createdAt := time.Now().Format(time.RFC3339) 718 719 - pullAt, err := db.GetPullAt(s.db, f.RepoAt(), pull.PullId) 720 - if err != nil { 721 - log.Println("failed to get pull at", err) 722 - s.pages.Notice(w, "pull-comment", "Failed to create comment.") 723 - return 724 - } 725 - 726 client, err := s.oauth.AuthorizedClient(r) 727 if err != nil { 728 log.Println("failed to get authorized client", err) ··· 735 Rkey: tid.TID(), 736 Record: &lexutil.LexiconTypeDecoder{ 737 Val: &tangled.RepoPullComment{ 738 - Pull: string(pullAt), 739 Body: body, 740 CreatedAt: createdAt, 741 }, ··· 983 } 984 985 sourceRev := comparison.Rev2 986 - patch := comparison.Patch 987 988 if !patchutil.IsPatchValid(patch) { 989 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 998 Sha: comparison.Rev2, 999 } 1000 1001 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource, isStacked) 1002 } 1003 1004 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) { ··· 1007 return 1008 } 1009 1010 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil, isStacked) 1011 } 1012 1013 func (s *Pulls) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool) { ··· 1090 } 1091 1092 sourceRev := comparison.Rev2 1093 - patch := comparison.Patch 1094 1095 if !patchutil.IsPatchValid(patch) { 1096 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 1110 Sha: sourceRev, 1111 } 1112 1113 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource, isStacked) 1114 } 1115 1116 func (s *Pulls) createPullRequest( ··· 1120 user *oauth.User, 1121 title, body, targetBranch string, 1122 patch string, 1123 sourceRev string, 1124 pullSource *models.PullSource, 1125 recordPullSource *tangled.RepoPull_Source, ··· 1179 rkey := tid.TID() 1180 initialSubmission := models.PullSubmission{ 1181 Patch: patch, 1182 SourceRev: sourceRev, 1183 } 1184 pull := &models.Pull{ ··· 1608 1609 patch := r.FormValue("patch") 1610 1611 - s.resubmitPullHelper(w, r, f, user, pull, patch, "") 1612 } 1613 1614 func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) { ··· 1669 } 1670 1671 sourceRev := comparison.Rev2 1672 - patch := comparison.Patch 1673 1674 - s.resubmitPullHelper(w, r, f, user, pull, patch, sourceRev) 1675 } 1676 1677 func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) { ··· 1764 comparison := forkComparison 1765 1766 sourceRev := comparison.Rev2 1767 - patch := comparison.Patch 1768 1769 - s.resubmitPullHelper(w, r, f, user, pull, patch, sourceRev) 1770 } 1771 1772 // validate a resubmission against a pull request ··· 1793 user *oauth.User, 1794 pull *models.Pull, 1795 patch string, 1796 sourceRev string, 1797 ) { 1798 if pull.IsStacked() { ··· 1822 } 1823 defer tx.Rollback() 1824 1825 - err = db.ResubmitPull(tx, pull, patch, sourceRev) 1826 if err != nil { 1827 log.Println("failed to create pull request", err) 1828 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 2038 submission := np.Submissions[np.LastRoundNumber()] 2039 2040 // resubmit the old pull 2041 - err := db.ResubmitPull(tx, op, submission.Patch, submission.SourceRev) 2042 2043 if err != nil { 2044 log.Println("failed to update pull", err, op.PullId)
··· 142 stack, _ := r.Context().Value("stack").(models.Stack) 143 abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*models.Pull) 144 145 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 146 branchDeleteStatus := s.branchDeleteStatus(r, f, pull) 147 resubmitResult := pages.Unknown ··· 439 return 440 } 441 442 + patch := pull.Submissions[roundIdInt].CombinedPatch() 443 diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 444 445 s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ ··· 490 return 491 } 492 493 + currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].CombinedPatch()) 494 if err != nil { 495 log.Println("failed to interdiff; current patch malformed") 496 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 497 return 498 } 499 500 + previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].CombinedPatch()) 501 if err != nil { 502 log.Println("failed to interdiff; previous patch malformed") 503 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") ··· 699 700 createdAt := time.Now().Format(time.RFC3339) 701 702 client, err := s.oauth.AuthorizedClient(r) 703 if err != nil { 704 log.Println("failed to get authorized client", err) ··· 711 Rkey: tid.TID(), 712 Record: &lexutil.LexiconTypeDecoder{ 713 Val: &tangled.RepoPullComment{ 714 + Pull: pull.PullAt().String(), 715 Body: body, 716 CreatedAt: createdAt, 717 }, ··· 959 } 960 961 sourceRev := comparison.Rev2 962 + patch := comparison.FormatPatchRaw 963 + combined := comparison.CombinedPatchRaw 964 965 if !patchutil.IsPatchValid(patch) { 966 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 975 Sha: comparison.Rev2, 976 } 977 978 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked) 979 } 980 981 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) { ··· 984 return 985 } 986 987 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", "", nil, nil, isStacked) 988 } 989 990 func (s *Pulls) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string, isStacked bool) { ··· 1067 } 1068 1069 sourceRev := comparison.Rev2 1070 + patch := comparison.FormatPatchRaw 1071 + combined := comparison.CombinedPatchRaw 1072 1073 if !patchutil.IsPatchValid(patch) { 1074 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 1088 Sha: sourceRev, 1089 } 1090 1091 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked) 1092 } 1093 1094 func (s *Pulls) createPullRequest( ··· 1098 user *oauth.User, 1099 title, body, targetBranch string, 1100 patch string, 1101 + combined string, 1102 sourceRev string, 1103 pullSource *models.PullSource, 1104 recordPullSource *tangled.RepoPull_Source, ··· 1158 rkey := tid.TID() 1159 initialSubmission := models.PullSubmission{ 1160 Patch: patch, 1161 + Combined: combined, 1162 SourceRev: sourceRev, 1163 } 1164 pull := &models.Pull{ ··· 1588 1589 patch := r.FormValue("patch") 1590 1591 + s.resubmitPullHelper(w, r, f, user, pull, patch, "", "") 1592 } 1593 1594 func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) { ··· 1649 } 1650 1651 sourceRev := comparison.Rev2 1652 + patch := comparison.FormatPatchRaw 1653 + combined := comparison.CombinedPatchRaw 1654 1655 + s.resubmitPullHelper(w, r, f, user, pull, patch, combined, sourceRev) 1656 } 1657 1658 func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) { ··· 1745 comparison := forkComparison 1746 1747 sourceRev := comparison.Rev2 1748 + patch := comparison.FormatPatchRaw 1749 + combined := comparison.CombinedPatchRaw 1750 1751 + s.resubmitPullHelper(w, r, f, user, pull, patch, combined, sourceRev) 1752 } 1753 1754 // validate a resubmission against a pull request ··· 1775 user *oauth.User, 1776 pull *models.Pull, 1777 patch string, 1778 + combined string, 1779 sourceRev string, 1780 ) { 1781 if pull.IsStacked() { ··· 1805 } 1806 defer tx.Rollback() 1807 1808 + err = db.ResubmitPull(tx, pull, patch, combined, sourceRev) 1809 if err != nil { 1810 log.Println("failed to create pull request", err) 1811 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 2021 submission := np.Submissions[np.LastRoundNumber()] 2022 2023 // resubmit the old pull 2024 + err := db.ResubmitPull(tx, op, submission.Patch, submission.Combined, submission.SourceRev) 2025 2026 if err != nil { 2027 log.Println("failed to update pull", err, op.PullId)
+6 -1
appview/repo/repo.go
··· 2527 return 2528 } 2529 2530 - diff := patchutil.AsNiceDiff(formatPatch.Patch, base) 2531 2532 repoinfo := f.RepoInfo(user) 2533
··· 2527 return 2528 } 2529 2530 + var diff types.NiceDiff 2531 + if formatPatch.CombinedPatchRaw != "" { 2532 + diff = patchutil.AsNiceDiff(formatPatch.CombinedPatchRaw, base) 2533 + } else { 2534 + diff = patchutil.AsNiceDiff(formatPatch.FormatPatchRaw, base) 2535 + } 2536 2537 repoinfo := f.RepoInfo(user) 2538
+20 -4
knotserver/xrpc/repo_compare.go
··· 4 "fmt" 5 "net/http" 6 7 "tangled.org/core/knotserver/git" 8 "tangled.org/core/types" 9 xrpcerr "tangled.org/core/xrpc/errors" ··· 71 return 72 } 73 74 response := types.RepoFormatPatchResponse{ 75 - Rev1: commit1.Hash.String(), 76 - Rev2: commit2.Hash.String(), 77 - FormatPatch: formatPatch, 78 - Patch: rawPatch, 79 } 80 81 writeJson(w, response)
··· 4 "fmt" 5 "net/http" 6 7 + "github.com/bluekeyes/go-gitdiff/gitdiff" 8 "tangled.org/core/knotserver/git" 9 "tangled.org/core/types" 10 xrpcerr "tangled.org/core/xrpc/errors" ··· 72 return 73 } 74 75 + var combinedPatch []*gitdiff.File 76 + var combinedPatchRaw string 77 + // we need the combined patch 78 + if len(formatPatch) >= 2 { 79 + diffTree, err := gr.DiffTree(commit1, commit2) 80 + if err != nil { 81 + x.Logger.Error("error comparing revisions", "msg", err.Error()) 82 + } else { 83 + combinedPatch = diffTree.Diff 84 + combinedPatchRaw = diffTree.Patch 85 + } 86 + } 87 + 88 response := types.RepoFormatPatchResponse{ 89 + Rev1: commit1.Hash.String(), 90 + Rev2: commit2.Hash.String(), 91 + FormatPatch: formatPatch, 92 + FormatPatchRaw: rawPatch, 93 + CombinedPatch: combinedPatch, 94 + CombinedPatchRaw: combinedPatchRaw, 95 } 96 97 writeJson(w, response)
+7 -5
types/repo.go
··· 1 package types 2 3 import ( 4 "github.com/go-git/go-git/v5/plumbing/object" 5 ) 6 ··· 33 } 34 35 type RepoFormatPatchResponse struct { 36 - Rev1 string `json:"rev1,omitempty"` 37 - Rev2 string `json:"rev2,omitempty"` 38 - FormatPatch []FormatPatch `json:"format_patch,omitempty"` 39 - MergeBase string `json:"merge_base,omitempty"` // deprecated 40 - Patch string `json:"patch,omitempty"` 41 } 42 43 type RepoTreeResponse struct {
··· 1 package types 2 3 import ( 4 + "github.com/bluekeyes/go-gitdiff/gitdiff" 5 "github.com/go-git/go-git/v5/plumbing/object" 6 ) 7 ··· 34 } 35 36 type RepoFormatPatchResponse struct { 37 + Rev1 string `json:"rev1,omitempty"` 38 + Rev2 string `json:"rev2,omitempty"` 39 + FormatPatch []FormatPatch `json:"format_patch,omitempty"` 40 + FormatPatchRaw string `json:"patch,omitempty"` 41 + CombinedPatch []*gitdiff.File `json:"combined_patch,omitempty"` 42 + CombinedPatchRaw string `json:"combined_patch_raw,omitempty"` 43 } 44 45 type RepoTreeResponse struct {