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 7793e734 ce1f2b90

verified
Changed files
+108 -76
appview
db
models
pulls
repo
knotserver
types
+9
appview/db/db.go
··· 1097 }) 1098 conn.ExecContext(ctx, "pragma foreign_keys = on;") 1099 1100 return &DB{ 1101 db, 1102 logger,
··· 1097 }) 1098 conn.ExecContext(ctx, "pragma foreign_keys = on;") 1099 1100 + // knots may report the combined patch for a comparison, we can store that on the appview side 1101 + // (but not on the pds record), because calculating the combined patch requires a git index 1102 + runMigration(conn, logger, "add-combined-column-submissions", func(tx *sql.Tx) error { 1103 + _, err := tx.Exec(` 1104 + alter table pull_submissions add column combined text; 1105 + `) 1106 + return err 1107 + }) 1108 + 1109 return &DB{ 1110 db, 1111 logger,
+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, pullAt syntax.ATURI, newRoundNumber int, newPatch string, newSourceRev string) error { 594 _, err := e.Exec(` 595 - insert into pull_submissions (pull_at, round_number, patch, source_rev) 596 - values (?, ?, ?, ?) 597 - `, pullAt, newRoundNumber, newPatch, newSourceRev) 598 599 return err 600 }
··· 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, pullAt syntax.ATURI, newRoundNumber int, newPatch string, combinedPatch string, newSourceRev string) error { 598 _, err := e.Exec(` 599 + insert into pull_submissions (pull_at, round_number, patch, combined, source_rev) 600 + values (?, ?, ?, ?, ?) 601 + `, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev) 602 603 return err 604 }
+19 -8
appview/models/pull.go
··· 125 // content 126 RoundNumber int 127 Patch string 128 Comments []PullComment 129 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 130 ··· 150 Created time.Time 151 } 152 153 func (p *Pull) LatestPatch() string { 154 - latestSubmission := p.Submissions[p.LastRoundNumber()] 155 - return latestSubmission.Patch 156 } 157 158 func (p *Pull) LatestSha() string { 159 - latestSubmission := p.Submissions[p.LastRoundNumber()] 160 - return latestSubmission.SourceRev 161 } 162 163 func (p *Pull) PullAt() syntax.ATURI { 164 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.OwnerDid, tangled.RepoPullNSID, p.Rkey)) 165 - } 166 - 167 - func (p *Pull) LastRoundNumber() int { 168 - return len(p.Submissions) - 1 169 } 170 171 func (p *Pull) IsPatchBased() bool { ··· 252 } 253 254 return participants 255 } 256 257 type Stack []*Pull
··· 125 // content 126 RoundNumber int 127 Patch string 128 + Combined string 129 Comments []PullComment 130 SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs 131 ··· 151 Created time.Time 152 } 153 154 + func (p *Pull) LastRoundNumber() int { 155 + return len(p.Submissions) - 1 156 + } 157 + 158 + func (p *Pull) LatestSubmission() *PullSubmission { 159 + return p.Submissions[p.LastRoundNumber()] 160 + } 161 + 162 func (p *Pull) LatestPatch() string { 163 + return p.LatestSubmission().Patch 164 } 165 166 func (p *Pull) LatestSha() string { 167 + return p.LatestSubmission().SourceRev 168 } 169 170 func (p *Pull) PullAt() syntax.ATURI { 171 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.OwnerDid, tangled.RepoPullNSID, p.Rkey)) 172 } 173 174 func (p *Pull) IsPatchBased() bool { ··· 255 } 256 257 return participants 258 + } 259 + 260 + func (s PullSubmission) CombinedPatch() string { 261 + if s.Combined == "" { 262 + return s.Patch 263 + } 264 + 265 + return s.Combined 266 } 267 268 type Stack []*Pull
+26 -41
appview/pulls/pulls.go
··· 145 stack, _ := r.Context().Value("stack").(models.Stack) 146 abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*models.Pull) 147 148 - totalIdents := 1 149 - for _, submission := range pull.Submissions { 150 - totalIdents += len(submission.Comments) 151 - } 152 - 153 - identsToResolve := make([]string, totalIdents) 154 - 155 - // populate idents 156 - identsToResolve[0] = pull.OwnerDid 157 - idx := 1 158 - for _, submission := range pull.Submissions { 159 - for _, comment := range submission.Comments { 160 - identsToResolve[idx] = comment.OwnerDid 161 - idx += 1 162 - } 163 - } 164 - 165 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 166 branchDeleteStatus := s.branchDeleteStatus(r, f, pull) 167 resubmitResult := pages.Unknown ··· 459 return 460 } 461 462 - patch := pull.Submissions[roundIdInt].Patch 463 diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 464 465 s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ ··· 510 return 511 } 512 513 - currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].Patch) 514 if err != nil { 515 log.Println("failed to interdiff; current patch malformed") 516 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 517 return 518 } 519 520 - previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].Patch) 521 if err != nil { 522 log.Println("failed to interdiff; previous patch malformed") 523 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") ··· 719 720 createdAt := time.Now().Format(time.RFC3339) 721 722 - pullAt, err := db.GetPullAt(s.db, f.RepoAt(), pull.PullId) 723 - if err != nil { 724 - log.Println("failed to get pull at", err) 725 - s.pages.Notice(w, "pull-comment", "Failed to create comment.") 726 - return 727 - } 728 - 729 client, err := s.oauth.AuthorizedClient(r) 730 if err != nil { 731 log.Println("failed to get authorized client", err) ··· 738 Rkey: tid.TID(), 739 Record: &lexutil.LexiconTypeDecoder{ 740 Val: &tangled.RepoPullComment{ 741 - Pull: string(pullAt), 742 Body: body, 743 CreatedAt: createdAt, 744 }, ··· 986 } 987 988 sourceRev := comparison.Rev2 989 - patch := comparison.Patch 990 991 if !patchutil.IsPatchValid(patch) { 992 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 1001 Sha: comparison.Rev2, 1002 } 1003 1004 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource, isStacked) 1005 } 1006 1007 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) { ··· 1010 return 1011 } 1012 1013 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil, isStacked) 1014 } 1015 1016 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) { ··· 1093 } 1094 1095 sourceRev := comparison.Rev2 1096 - patch := comparison.Patch 1097 1098 if !patchutil.IsPatchValid(patch) { 1099 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 1113 Sha: sourceRev, 1114 } 1115 1116 - s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource, isStacked) 1117 } 1118 1119 func (s *Pulls) createPullRequest( ··· 1123 user *oauth.User, 1124 title, body, targetBranch string, 1125 patch string, 1126 sourceRev string, 1127 pullSource *models.PullSource, 1128 recordPullSource *tangled.RepoPull_Source, ··· 1182 rkey := tid.TID() 1183 initialSubmission := models.PullSubmission{ 1184 Patch: patch, 1185 SourceRev: sourceRev, 1186 } 1187 pull := &models.Pull{ ··· 1611 1612 patch := r.FormValue("patch") 1613 1614 - s.resubmitPullHelper(w, r, f, user, pull, patch, "") 1615 } 1616 1617 func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) { ··· 1672 } 1673 1674 sourceRev := comparison.Rev2 1675 - patch := comparison.Patch 1676 1677 - s.resubmitPullHelper(w, r, f, user, pull, patch, sourceRev) 1678 } 1679 1680 func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) { ··· 1767 comparison := forkComparison 1768 1769 sourceRev := comparison.Rev2 1770 - patch := comparison.Patch 1771 1772 - s.resubmitPullHelper(w, r, f, user, pull, patch, sourceRev) 1773 } 1774 1775 // validate a resubmission against a pull request ··· 1796 user *oauth.User, 1797 pull *models.Pull, 1798 patch string, 1799 sourceRev string, 1800 ) { 1801 if pull.IsStacked() { ··· 1829 newRoundNumber := len(pull.Submissions) 1830 newPatch := patch 1831 newSourceRev := sourceRev 1832 - err = db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, newSourceRev) 1833 if err != nil { 1834 log.Println("failed to create pull request", err) 1835 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 2024 pullAt := op.PullAt() 2025 newRoundNumber := len(op.Submissions) 2026 newPatch := np.LatestPatch() 2027 newSourceRev := np.LatestSha() 2028 - err := db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, newSourceRev) 2029 - 2030 if err != nil { 2031 log.Println("failed to update pull", err, op.PullId) 2032 s.pages.Notice(w, "pull-resubmit-error", "Failed to resubmit pull request. Try again later.") ··· 2374 initialSubmission := models.PullSubmission{ 2375 Patch: fp.Raw, 2376 SourceRev: fp.SHA, 2377 } 2378 pull := models.Pull{ 2379 Title: title,
··· 145 stack, _ := r.Context().Value("stack").(models.Stack) 146 abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*models.Pull) 147 148 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 149 branchDeleteStatus := s.branchDeleteStatus(r, f, pull) 150 resubmitResult := pages.Unknown ··· 442 return 443 } 444 445 + patch := pull.Submissions[roundIdInt].CombinedPatch() 446 diff := patchutil.AsNiceDiff(patch, pull.TargetBranch) 447 448 s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ ··· 493 return 494 } 495 496 + currentPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt].CombinedPatch()) 497 if err != nil { 498 log.Println("failed to interdiff; current patch malformed") 499 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 500 return 501 } 502 503 + previousPatch, err := patchutil.AsDiff(pull.Submissions[roundIdInt-1].CombinedPatch()) 504 if err != nil { 505 log.Println("failed to interdiff; previous patch malformed") 506 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") ··· 702 703 createdAt := time.Now().Format(time.RFC3339) 704 705 client, err := s.oauth.AuthorizedClient(r) 706 if err != nil { 707 log.Println("failed to get authorized client", err) ··· 714 Rkey: tid.TID(), 715 Record: &lexutil.LexiconTypeDecoder{ 716 Val: &tangled.RepoPullComment{ 717 + Pull: pull.PullAt().String(), 718 Body: body, 719 CreatedAt: createdAt, 720 }, ··· 962 } 963 964 sourceRev := comparison.Rev2 965 + patch := comparison.FormatPatchRaw 966 + combined := comparison.CombinedPatchRaw 967 968 if !patchutil.IsPatchValid(patch) { 969 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 978 Sha: comparison.Rev2, 979 } 980 981 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked) 982 } 983 984 func (s *Pulls) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *reporesolver.ResolvedRepo, user *oauth.User, title, body, targetBranch, patch string, isStacked bool) { ··· 987 return 988 } 989 990 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", "", nil, nil, isStacked) 991 } 992 993 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) { ··· 1070 } 1071 1072 sourceRev := comparison.Rev2 1073 + patch := comparison.FormatPatchRaw 1074 + combined := comparison.CombinedPatchRaw 1075 1076 if !patchutil.IsPatchValid(patch) { 1077 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") ··· 1091 Sha: sourceRev, 1092 } 1093 1094 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, combined, sourceRev, pullSource, recordPullSource, isStacked) 1095 } 1096 1097 func (s *Pulls) createPullRequest( ··· 1101 user *oauth.User, 1102 title, body, targetBranch string, 1103 patch string, 1104 + combined string, 1105 sourceRev string, 1106 pullSource *models.PullSource, 1107 recordPullSource *tangled.RepoPull_Source, ··· 1161 rkey := tid.TID() 1162 initialSubmission := models.PullSubmission{ 1163 Patch: patch, 1164 + Combined: combined, 1165 SourceRev: sourceRev, 1166 } 1167 pull := &models.Pull{ ··· 1591 1592 patch := r.FormValue("patch") 1593 1594 + s.resubmitPullHelper(w, r, f, user, pull, patch, "", "") 1595 } 1596 1597 func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) { ··· 1652 } 1653 1654 sourceRev := comparison.Rev2 1655 + patch := comparison.FormatPatchRaw 1656 + combined := comparison.CombinedPatchRaw 1657 1658 + s.resubmitPullHelper(w, r, f, user, pull, patch, combined, sourceRev) 1659 } 1660 1661 func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) { ··· 1748 comparison := forkComparison 1749 1750 sourceRev := comparison.Rev2 1751 + patch := comparison.FormatPatchRaw 1752 + combined := comparison.CombinedPatchRaw 1753 1754 + s.resubmitPullHelper(w, r, f, user, pull, patch, combined, sourceRev) 1755 } 1756 1757 // validate a resubmission against a pull request ··· 1778 user *oauth.User, 1779 pull *models.Pull, 1780 patch string, 1781 + combined string, 1782 sourceRev string, 1783 ) { 1784 if pull.IsStacked() { ··· 1812 newRoundNumber := len(pull.Submissions) 1813 newPatch := patch 1814 newSourceRev := sourceRev 1815 + combinedPatch := combined 1816 + err = db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev) 1817 if err != nil { 1818 log.Println("failed to create pull request", err) 1819 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 2008 pullAt := op.PullAt() 2009 newRoundNumber := len(op.Submissions) 2010 newPatch := np.LatestPatch() 2011 + combinedPatch := np.LatestSubmission().Combined 2012 newSourceRev := np.LatestSha() 2013 + err := db.ResubmitPull(tx, pullAt, newRoundNumber, newPatch, combinedPatch, newSourceRev) 2014 if err != nil { 2015 log.Println("failed to update pull", err, op.PullId) 2016 s.pages.Notice(w, "pull-resubmit-error", "Failed to resubmit pull request. Try again later.") ··· 2358 initialSubmission := models.PullSubmission{ 2359 Patch: fp.Raw, 2360 SourceRev: fp.SHA, 2361 + Combined: fp.Raw, 2362 } 2363 pull := models.Pull{ 2364 Title: title,
+6 -1
appview/repo/repo.go
··· 2565 return 2566 } 2567 2568 - diff := patchutil.AsNiceDiff(formatPatch.Patch, base) 2569 2570 repoinfo := f.RepoInfo(user) 2571
··· 2565 return 2566 } 2567 2568 + var diff types.NiceDiff 2569 + if formatPatch.CombinedPatchRaw != "" { 2570 + diff = patchutil.AsNiceDiff(formatPatch.CombinedPatchRaw, base) 2571 + } else { 2572 + diff = patchutil.AsNiceDiff(formatPatch.FormatPatchRaw, base) 2573 + } 2574 2575 repoinfo := f.RepoInfo(user) 2576
+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 {