Skip to content

Commit 4a3502c

Browse files
committed
Submitting review efcba5b
Better support for rebasing reviews This change improves the support for rebasing reviews in multiple ways: 1. Adding a new `refs/archives/devtools` ref that can be used to make sure that commits which have been rebased do not get garbage collected. 2. Adding a new `alias` field to the request schema that enables tracking review history across rebases. 3. Adding a new `rebase` subcommand that enables us to update the history for a change prior to submitting it.
2 parents 7e4f4b3 + ce5d467 commit 4a3502c

File tree

13 files changed

+703
-78
lines changed

13 files changed

+703
-78
lines changed

commands/commands.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
)
2323

2424
const notesRefPattern = "refs/notes/devtools/*"
25+
const archiveRefPattern = "refs/devtools/archives/*"
2526
const commentFilename = "APPRAISE_COMMENT_EDITMSG"
2627

2728
// Command represents the definition of a single command.
@@ -45,6 +46,7 @@ var CommandMap = map[string]*Command{
4546
"list": listCmd,
4647
"pull": pullCmd,
4748
"push": pushCmd,
49+
"rebase": rebaseCmd,
4850
"reject": rejectCmd,
4951
"request": requestCmd,
5052
"show": showCmd,

commands/pull.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ func pull(repo repository.Repo, args []string) error {
3333
remote = args[0]
3434
}
3535

36-
repo.PullNotes(remote, notesRefPattern)
36+
if err := repo.PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern); err != nil {
37+
return err
38+
}
3739
return nil
3840
}
3941

commands/push.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ func push(repo repository.Repo, args []string) error {
3333
remote = args[0]
3434
}
3535

36-
return repo.PushNotes(remote, notesRefPattern)
36+
if err := repo.PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern); err != nil {
37+
return err
38+
}
39+
return nil
3740
}
3841

3942
var pushCmd = &Command{

commands/rebase.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package commands
18+
19+
import (
20+
"errors"
21+
"flag"
22+
"fmt"
23+
"github.com/google/git-appraise/repository"
24+
"github.com/google/git-appraise/review"
25+
)
26+
27+
var rebaseFlagSet = flag.NewFlagSet("rebase", flag.ExitOnError)
28+
29+
var (
30+
rebaseArchive = rebaseFlagSet.Bool("archive", true, "Prevent the original commit from being garbage collected.")
31+
)
32+
33+
// Validate that the user's request to rebase a review makes sense.
34+
//
35+
// This checks both that the request is well formed, and that the
36+
// corresponding review is in a state where rebasing is appropriate.
37+
func validateRebaseRequest(repo repository.Repo, args []string) (*review.Review, error) {
38+
var r *review.Review
39+
var err error
40+
if len(args) > 1 {
41+
return nil, errors.New("Only rebasing a single review is supported.")
42+
}
43+
if len(args) == 1 {
44+
r, err = review.Get(repo, args[0])
45+
} else {
46+
r, err = review.GetCurrent(repo)
47+
}
48+
if err != nil {
49+
return nil, fmt.Errorf("Failed to load the review: %v\n", err)
50+
}
51+
if r == nil {
52+
return nil, errors.New("There is no matching review.")
53+
}
54+
55+
if r.Submitted {
56+
return nil, errors.New("The review has already been submitted.")
57+
}
58+
59+
target := r.Request.TargetRef
60+
if err := repo.VerifyGitRef(target); err != nil {
61+
return nil, err
62+
}
63+
64+
return r, nil
65+
}
66+
67+
// Rebase the current code review.
68+
//
69+
// The "args" parameter contains all of the command line arguments that followed the subcommand.
70+
func rebaseReview(repo repository.Repo, args []string) error {
71+
rebaseFlagSet.Parse(args)
72+
args = rebaseFlagSet.Args()
73+
74+
r, err := validateRebaseRequest(repo, args)
75+
if err != nil {
76+
return err
77+
}
78+
return r.Rebase(*rebaseArchive)
79+
}
80+
81+
// rebaseCmd defines the "rebase" subcommand.
82+
var rebaseCmd = &Command{
83+
Usage: func(arg0 string) {
84+
fmt.Printf("Usage: %s rebase [<option>...] [<review-hash>]\n\nOptions:\n", arg0)
85+
rebaseFlagSet.PrintDefaults()
86+
},
87+
RunMethod: func(repo repository.Repo, args []string) error {
88+
return rebaseReview(repo, args)
89+
},
90+
}

commands/request.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,39 @@ func buildRequestFromFlags(requester string) (request.Request, error) {
6565
return request.New(requester, reviewers, *requestSource, *requestTarget, *requestMessage), nil
6666
}
6767

68+
// Get the commit at which the review request should be anchored.
69+
func getReviewCommit(repo repository.Repo, r request.Request, args []string) (string, string, error) {
70+
if len(args) > 1 {
71+
return "", "", errors.New("Only updating a single review is supported.")
72+
}
73+
if len(args) == 1 {
74+
base, err := repo.MergeBase(r.TargetRef, args[0])
75+
if err != nil {
76+
return "", "", err
77+
}
78+
return args[0], base, nil
79+
}
80+
81+
base, err := repo.MergeBase(r.TargetRef, r.ReviewRef)
82+
if err != nil {
83+
return "", "", err
84+
}
85+
reviewCommits, err := repo.ListCommitsBetween(base, r.ReviewRef)
86+
if err != nil {
87+
return "", "", err
88+
}
89+
if reviewCommits == nil {
90+
return "", "", errors.New("There are no commits included in the review request")
91+
}
92+
return reviewCommits[0], base, nil
93+
}
94+
6895
// Create a new code review request.
6996
//
7097
// The "args" parameter is all of the command line arguments that followed the subcommand.
7198
func requestReview(repo repository.Repo, args []string) error {
7299
requestFlagSet.Parse(args)
100+
args = requestFlagSet.Args()
73101

74102
if !*requestAllowUncommitted {
75103
// Requesting a code review with uncommited local changes is usually a mistake, so
@@ -104,22 +132,14 @@ func requestReview(repo repository.Repo, args []string) error {
104132
if err := repo.VerifyGitRef(r.ReviewRef); err != nil {
105133
return err
106134
}
107-
base, err := repo.MergeBase(r.TargetRef, r.ReviewRef)
108-
if err != nil {
109-
return err
110-
}
111-
r.BaseCommit = base
112135

113-
reviewCommits, err := repo.ListCommitsBetween(base, r.ReviewRef)
136+
reviewCommit, baseCommit, err := getReviewCommit(repo, r, args)
114137
if err != nil {
115138
return err
116139
}
117-
if reviewCommits == nil {
118-
return errors.New("There are no commits included in the review request")
119-
}
120-
140+
r.BaseCommit = baseCommit
121141
if r.Description == "" {
122-
description, err := repo.GetCommitMessage(reviewCommits[0])
142+
description, err := repo.GetCommitMessage(reviewCommit)
123143
if err != nil {
124144
return err
125145
}
@@ -130,17 +150,17 @@ func requestReview(repo repository.Repo, args []string) error {
130150
if err != nil {
131151
return err
132152
}
133-
repo.AppendNote(request.Ref, reviewCommits[0], note)
153+
repo.AppendNote(request.Ref, reviewCommit, note)
134154
if !*requestQuiet {
135-
fmt.Printf(requestSummaryTemplate, reviewCommits[0], r.TargetRef, r.ReviewRef, r.Description)
155+
fmt.Printf(requestSummaryTemplate, reviewCommit, r.TargetRef, r.ReviewRef, r.Description)
136156
}
137157
return nil
138158
}
139159

140160
// requestCmd defines the "request" subcommand.
141161
var requestCmd = &Command{
142162
Usage: func(arg0 string) {
143-
fmt.Printf("Usage: %s request [<option>...]\n\nOptions:\n", arg0)
163+
fmt.Printf("Usage: %s request [<option>...] [<review-hash>]\n\nOptions:\n", arg0)
144164
requestFlagSet.PrintDefaults()
145165
},
146166
RunMethod: func(repo repository.Repo, args []string) error {

commands/submit.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var (
3131
submitRebase = submitFlagSet.Bool("rebase", false, "Rebase the source ref onto the target ref.")
3232
submitFastForward = submitFlagSet.Bool("fast-forward", false, "Create a merge using the default fast-forward mode.")
3333
submitTBR = submitFlagSet.Bool("tbr", false, "(To be reviewed) Force the submission of a review that has not been accepted.")
34+
submitArchive = submitFlagSet.Bool("archive", true, "Prevent the original commit from being garbage collected; only affects rebased submits.")
3435
)
3536

3637
// Submit the current code review request.
@@ -87,10 +88,6 @@ func submitReview(repo repository.Repo, args []string) error {
8788
return errors.New("Refusing to submit a non-fast-forward review. First merge the target ref.")
8889
}
8990

90-
if err := repo.SwitchToRef(target); err != nil {
91-
return err
92-
}
93-
9491
if !(*submitRebase || *submitMerge || *submitFastForward) {
9592
submitStrategy, err := repo.GetSubmitStrategy()
9693
if err != nil {
@@ -107,11 +104,22 @@ func submitReview(repo repository.Repo, args []string) error {
107104
}
108105
}
109106

107+
if *submitRebase {
108+
if err := r.Rebase(*submitArchive); err != nil {
109+
return err
110+
}
111+
source, err = r.GetHeadCommit()
112+
if err != nil {
113+
return err
114+
}
115+
}
116+
117+
if err := repo.SwitchToRef(target); err != nil {
118+
return err
119+
}
110120
if *submitMerge {
111121
submitMessage := fmt.Sprintf("Submitting review %.12s", r.Revision)
112122
return repo.MergeRef(source, false, submitMessage, r.Request.Description)
113-
} else if *submitRebase {
114-
return repo.RebaseRef(source)
115123
} else {
116124
return repo.MergeRef(source, true)
117125
}
@@ -120,7 +128,7 @@ func submitReview(repo repository.Repo, args []string) error {
120128
// submitCmd defines the "submit" subcommand.
121129
var submitCmd = &Command{
122130
Usage: func(arg0 string) {
123-
fmt.Printf("Usage: %s submit [<option>...]\n\nOptions:\n", arg0)
131+
fmt.Printf("Usage: %s submit [<option>...] [<review-hash>]\n\nOptions:\n", arg0)
124132
submitFlagSet.PrintDefaults()
125133
},
126134
RunMethod: func(repo repository.Repo, args []string) error {

0 commit comments

Comments
 (0)