Skip to content

Commit 8db99d4

Browse files
committed
Split out the summary of a review into a separate struct.
This new struct is then returned by all of the methods that list the reviews in a repo, and is what the "list" subcommand operates on. By making this split, we can separate the data that is cheap to look up from the data that is expensive to look up, and forgoe the expensive look ups when we are operating on multiple reviews. This change improves the performance of listing the open reviews in the git-appraise repo by more than three fold (dropping the run time from more than 7 seconds to less than 2).
1 parent db6bfbf commit 8db99d4

File tree

3 files changed

+66
-34
lines changed

3 files changed

+66
-34
lines changed

commands/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var (
3434
// TODO(ojarjur): Add more flags for filtering the output (e.g. filtering by reviewer or status).
3535
func listReviews(repo repository.Repo, args []string) {
3636
listFlagSet.Parse(args)
37-
var reviews []review.Review
37+
var reviews []review.ReviewSummary
3838
if *listAll {
3939
reviews = review.ListAll(repo)
4040
fmt.Printf("Loaded %d reviews:\n", len(reviews))

commands/output/output.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ status: %s
5454

5555
// getStatusString returns a human friendly string encapsulating both the review's
5656
// resolved status, and its submitted status.
57-
func getStatusString(r *review.Review) string {
57+
func getStatusString(r *review.ReviewSummary) string {
5858
if r.Resolved == nil && r.Submitted {
5959
return "tbr"
6060
}
@@ -74,7 +74,7 @@ func getStatusString(r *review.Review) string {
7474
}
7575

7676
// PrintSummary prints a single-line summary of a review.
77-
func PrintSummary(r *review.Review) {
77+
func PrintSummary(r *review.ReviewSummary) {
7878
statusString := getStatusString(r)
7979
indentedDescription := strings.Replace(r.Request.Description, "\n", "\n ", -1)
8080
fmt.Printf(reviewSummaryTemplate, statusString, r.Revision, indentedDescription)
@@ -176,7 +176,7 @@ func printComments(r *review.Review) error {
176176

177177
// PrintDetails prints a multi-line overview of a review, including all comments.
178178
func PrintDetails(r *review.Review) error {
179-
PrintSummary(r)
179+
PrintSummary(r.ReviewSummary)
180180
fmt.Printf(reviewDetailsTemplate, r.Request.ReviewRef, r.Request.TargetRef,
181181
strings.Join(r.Request.Reviewers, ", "),
182182
r.Request.Requester, r.GetBuildStatusMessage())

review/review.go

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,35 @@ type CommentThread struct {
4444
Resolved *bool `json:"resolved,omitempty"`
4545
}
4646

47-
// Review represents the entire state of a code review.
47+
// ReviewSummary represents the high-level state of a code review.
48+
//
49+
// This high-level state corresponds to the data that can be quickly read
50+
// directly from the repo, so other methods that need to operate on a lot
51+
// of reviews (such as listing the open reviews) should prefer operating on
52+
// the summary rather than the details.
4853
//
49-
// Reviews have two status fields which are orthogonal:
54+
// Review summaries have two status fields which are orthogonal:
5055
// 1. Resolved indicates if a reviewer has accepted or rejected the change.
5156
// 2. Submitted indicates if the change has been incorporated into the target.
57+
type ReviewSummary struct {
58+
Repo repository.Repo `json:"-"`
59+
Revision string `json:"revision"`
60+
Request request.Request `json:"request"`
61+
Comments []CommentThread `json:"comments,omitempty"`
62+
Resolved *bool `json:"resolved,omitempty"`
63+
Submitted bool `json:"submitted"`
64+
}
65+
66+
// Review represents the entire state of a code review.
5267
//
53-
// Reviews also include a list of build-and-test status reports. Those
68+
// This extends ReviewSummary to also include a list of reports for both the
69+
// continuous integration status, and the static analysis runs. Those reports
5470
// correspond to either the current commit in the review ref (for pending
5571
// reviews), or to the last commented-upon commit (for submitted reviews).
5672
type Review struct {
57-
Repo repository.Repo `json:"-"`
58-
Revision string `json:"revision"`
59-
Request request.Request `json:"request"`
60-
Comments []CommentThread `json:"comments,omitempty"`
61-
Resolved *bool `json:"resolved,omitempty"`
62-
Submitted bool `json:"submitted"`
63-
Reports []ci.Report `json:"reports,omitempty"`
64-
Analyses []analyses.Report `json:"analyses,omitempty"`
73+
*ReviewSummary
74+
Reports []ci.Report `json:"reports,omitempty"`
75+
Analyses []analyses.Report `json:"analyses,omitempty"`
6576
}
6677

6778
type byTimestamp []CommentThread
@@ -173,46 +184,68 @@ func buildCommentThreads(commentsByHash map[string]comment.Comment) []CommentThr
173184

174185
// loadComments reads in the log-structured sequence of comments for a review,
175186
// and then builds the corresponding tree-structured comment threads.
176-
func (r *Review) loadComments() []CommentThread {
187+
func (r *ReviewSummary) loadComments() []CommentThread {
177188
commentNotes := r.Repo.GetNotes(comment.Ref, r.Revision)
178189
commentsByHash := comment.ParseAllValid(commentNotes)
179190
return buildCommentThreads(commentsByHash)
180191
}
181192

182-
// Get returns the specified code review.
193+
// Get returns the summary of the specified code review.
183194
//
184-
// If no review request exists, the returned review is nil.
185-
func Get(repo repository.Repo, revision string) (*Review, error) {
195+
// If no review request exists, the returned review summary is nil.
196+
func GetSummary(repo repository.Repo, revision string) (*ReviewSummary, error) {
186197
requestNotes := repo.GetNotes(request.Ref, revision)
187198
requests := request.ParseAllValid(requestNotes)
188199
if requests == nil {
189200
return nil, nil
190201
}
191-
review := Review{
202+
reviewSummary := ReviewSummary{
192203
Repo: repo,
193204
Revision: revision,
194205
Request: requests[len(requests)-1],
195206
}
196-
review.Comments = review.loadComments()
197-
review.Resolved = updateThreadsStatus(review.Comments)
198-
submitted, err := repo.IsAncestor(revision, review.Request.TargetRef)
207+
reviewSummary.Comments = reviewSummary.loadComments()
208+
reviewSummary.Resolved = updateThreadsStatus(reviewSummary.Comments)
209+
submitted, err := repo.IsAncestor(revision, reviewSummary.Request.TargetRef)
199210
if err != nil {
200211
return nil, err
201212
}
202-
review.Submitted = submitted
213+
reviewSummary.Submitted = submitted
214+
return &reviewSummary, nil
215+
}
216+
217+
// Details returns the detailed review for the given summary.
218+
func (r *ReviewSummary) Details() (*Review, error) {
219+
review := Review{
220+
ReviewSummary: r,
221+
}
203222
currentCommit, err := review.GetHeadCommit()
204223
if err == nil {
205-
review.Reports = ci.ParseAllValid(repo.GetNotes(ci.Ref, currentCommit))
206-
review.Analyses = analyses.ParseAllValid(repo.GetNotes(analyses.Ref, currentCommit))
224+
review.Reports = ci.ParseAllValid(review.Repo.GetNotes(ci.Ref, currentCommit))
225+
review.Analyses = analyses.ParseAllValid(review.Repo.GetNotes(analyses.Ref, currentCommit))
207226
}
208227
return &review, nil
209228
}
210229

230+
// Get returns the specified code review.
231+
//
232+
// If no review request exists, the returned review is nil.
233+
func Get(repo repository.Repo, revision string) (*Review, error) {
234+
summary, err := GetSummary(repo, revision)
235+
if err != nil {
236+
return nil, err
237+
}
238+
if summary == nil {
239+
return nil, nil
240+
}
241+
return summary.Details()
242+
}
243+
211244
// ListAll returns all reviews stored in the git-notes.
212-
func ListAll(repo repository.Repo) []Review {
213-
var reviews []Review
245+
func ListAll(repo repository.Repo) []ReviewSummary {
246+
var reviews []ReviewSummary
214247
for _, revision := range repo.ListNotedRevisions(request.Ref) {
215-
review, err := Get(repo, revision)
248+
review, err := GetSummary(repo, revision)
216249
if err == nil && review != nil {
217250
reviews = append(reviews, *review)
218251
}
@@ -221,8 +254,8 @@ func ListAll(repo repository.Repo) []Review {
221254
}
222255

223256
// ListOpen returns all reviews that are not yet incorporated into their target refs.
224-
func ListOpen(repo repository.Repo) []Review {
225-
var openReviews []Review
257+
func ListOpen(repo repository.Repo) []ReviewSummary {
258+
var openReviews []ReviewSummary
226259
for _, review := range ListAll(repo) {
227260
if !review.Submitted {
228261
openReviews = append(openReviews, review)
@@ -239,7 +272,7 @@ func GetCurrent(repo repository.Repo) (*Review, error) {
239272
if err != nil {
240273
return nil, err
241274
}
242-
var matchingReviews []Review
275+
var matchingReviews []ReviewSummary
243276
for _, review := range ListOpen(repo) {
244277
if review.Request.ReviewRef == reviewRef {
245278
matchingReviews = append(matchingReviews, review)
@@ -251,8 +284,7 @@ func GetCurrent(repo repository.Repo) (*Review, error) {
251284
if len(matchingReviews) != 1 {
252285
return nil, fmt.Errorf("There are %d open reviews for the ref \"%s\"", len(matchingReviews), reviewRef)
253286
}
254-
r := &matchingReviews[0]
255-
return r, nil
287+
return matchingReviews[0].Details()
256288
}
257289

258290
// GetBuildStatusMessage returns a string of the current build-and-test status

0 commit comments

Comments
 (0)