Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(640)

Side by Side Diff: go/src/infra/tools/drover/main.go

Issue 662113003: Drover's back, baby! (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git/+/master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 package main
2
3 import "bufio"
4 import "flag"
5 import "fmt"
6 import "io/ioutil"
7 import "os"
8 import "strings"
9
10 import "github.com/daviddengcn/go-colortext"
11
12 import "infra/libs/git"
13 import "infra/libs/gitiles"
14
15 var commitish = flag.String("commit", "", "The commit to process (or commit posi tion number).")
16 var action = flag.String("action", "", "The action to take. Must specify `cherry -pick|revert`.")
M-A Ruel 2014/10/18 00:47:06 Why not two boolean flags instead?
iannucci 2014/10/20 21:11:58 They're mutually exclusive.
17 var ref = flag.String("ref", "", "The target ref to change.")
18 var workdir string
19
20 func init() {
M-A Ruel 2014/10/18 00:47:06 No need for init in package main, just put it at t
iannucci 2014/10/20 21:11:58 Done.
21 cwd, err := os.Getwd()
22 if err != nil {
23 panic(err)
24 }
25 flag.StringVar(&workdir, "workdir", cwd, "The work directory to use.")
26 }
27
28 func getCodereviewSettings(g *gitiles.Gitiles, ref string) (ret map[string]strin g) {
29 crSettings := <-g.Text("+", ref, "codereview.settings")
30 if crSettings.Err == nil {
31 lines := strings.Split(string(crSettings.Data), "\n")
32 ret = make(map[string]string, len(lines))
33 for _, line := range lines {
34 if len(line) == 0 || line[0] == '#' {
35 continue
36 }
37 keyVal := strings.Split(line, ":")
38 if len(keyVal) == 2 {
39 ret[keyVal[0]] = strings.TrimSpace(keyVal[1])
40 }
41 }
42 }
43 return ret
44 }
45
46 func getLandingCommit(g *gitiles.Gitiles, ref string, settings map[string]string ) (*git.Commit, string) {
47 prefix, ok := settings["PENDING_REF_PREFIX"]
48 if ok {
49 ref = strings.Replace(ref, "refs/", prefix, 1)
50 }
51 result := <-g.GetObjectFromPath(ref)
52 failIf(result.Err)
53 if result.Object.Type() != "commit" {
54 panic(fmt.Errorf("ref points at non-commit? %#v", result.Object) )
55 }
56 return result.Object.(*git.Commit), ref
57 }
58
59 func confirmAction(URL, action, ref string, commit *git.Commit, diff []string) {
60 verbPart := "to"
61 if action == "revert" {
62 verbPart = "from"
63 }
64
65 fmt.Printf("Planning to %s commit %s %s %s in %s:\n\n",
66 action, commit.ID(), verbPart, ref, URL)
67
68 ct.ChangeColor(ct.Yellow, false, ct.None, false)
69 fmt.Println("commit", commit.ID())
70 ct.ResetColor()
71 fmt.Printf("Author: %s <%s>\n", commit.Author().Name, commit.Author().Em ail)
72 fmt.Printf("Date: %s\n", commit.Author().Time)
73 fmt.Println()
74 for _, l := range commit.MessageLines() {
75 fmt.Printf(" %s\n", l)
76 }
77 fmt.Println()
78 for _, pair := range commit.FooterPairs() {
79 ct.ChangeColor(ct.White, true, ct.None, false)
80 fmt.Printf(" %s: ", pair.Key)
81 ct.ResetColor()
82 fmt.Println(pair.Value)
83 }
84 fmt.Println()
85 for _, line := range diff {
86 if len(line) > 0 {
87 switch {
88 case strings.HasPrefix(line, "+++") || strings.HasPrefix (line, "---"):
89 ct.ChangeColor(ct.White, true, ct.None, false)
90 case line[0] == '@':
91 ct.ChangeColor(ct.Cyan, false, ct.None, false)
92 bits := strings.SplitN(line, "@@", 3)
93 fmt.Print("@@", bits[1], "@@")
94 ct.ResetColor()
95 fmt.Println(bits[2])
96 continue
97 case line[0] == '+':
98 ct.ChangeColor(ct.Green, false, ct.None, false)
99 case line[0] == '-':
100 ct.ChangeColor(ct.Red, false, ct.None, false)
101 case line[0] == ' ':
102 ct.ResetColor()
103 default:
104 ct.ChangeColor(ct.White, true, ct.None, false)
105 }
106 }
107 fmt.Println(line)
108 }
109 ct.ResetColor()
110
111 fmt.Println()
112 fmt.Printf("Continue? [y/N] ")
113 answerRaw, err := bufio.NewReader(os.Stdin).ReadBytes('\n')
114 failIf(err)
115
116 answer := strings.TrimSpace(string(answerRaw))
117
118 if len(answer) != 0 && strings.HasPrefix("yes", strings.ToLower(answer)) {
119 return
120 }
121
122 fmt.Println("Action aborted")
123 os.Exit(1)
124 }
125
126 func getCommitData(g *gitiles.Gitiles, gitSha string) (*git.Commit, git.TreeDiff ) {
127 chCommit := g.GetObjectFromPath(gitSha)
128 chCommitDiff := g.GetCommitDiff(gitSha)
129
130 commitRslt := <-chCommit
131 failIf(commitRslt.Err)
132
133 commit, ok := commitRslt.Object.(*git.Commit)
134 if !ok {
135 failIf(fmt.Errorf("%s is not a commit?", gitSha))
136 }
137
138 commitDiffRslt := chCommitDiff
139 failIf(commitRslt.Err)
140
141 return commit, commitDiffRslt.Diff
142 }
143
144 func main() {
145 flag.Usage = func() {
146 fmt.Println("Usage: git drover -commit deadbeef -action cherry-p ick -ref 2125")
147 flag.PrintDefaults()
148 }
149 flag.Parse()
150 if *commitish == "" || *action == "" || *ref == "" {
151 flag.PrintDefaults()
152 fmt.Println("must specify commitish, action and ref")
153 os.Exit(1)
M-A Ruel 2014/10/18 00:47:06 I generally prefer to have something that looks li
154 }
155 if *action != "cherry-pick" && *action != "revert" {
156 flag.PrintDefaults()
157 fmt.Printf(
158 "must action must be 'cherry-pick' or 'revert'. Got '%s' .\n", *action)
159 os.Exit(1)
160 }
161
162 repo := &git.Repo{Path: workdir}
163 if repo.RunOk("rev-parse", "--git-dir") {
164 if !repo.RunOk("config", "drover.repo") {
165 fmt.Println("must be run from an empty directory or a dr over git repo")
166 os.Exit(1)
167 }
168 } else {
169 files, err := ioutil.ReadDir(workdir)
170 failIf(err)
171 if len(files) == 0 {
172 fmt.Println("Initializing current directory as a drover git repo")
173 if !repo.RunOk("init") {
174 flag.PrintDefaults()
175 panic("Couldn't initialize git repo!")
176 }
177 if !repo.RunOk("config", "drover.repo", "true") {
178 flag.PrintDefaults()
179 panic("Couldn't configure git repo to be owned b y drover!")
180 }
181 } else {
182 flag.PrintDefaults()
183 fmt.Println("must be run from an empty directory or a dr over git repo")
184 os.Exit(1)
185 }
186 }
187
188 gitSha, gitilesURL, err := disambiguateCommit(*commitish)
189 failIf(err)
190
191 g := gitiles.NewGitiles(gitilesURL, 8)
192
193 *ref, err = disambiguateRef(g, gitSha, *ref)
194 failIf(err)
195
196 commit, treeDiff := getCommitData(g, gitSha)
197 textDiff := g.GetTextCommitDiff(commit.ID())
198 failIf(textDiff.Err)
199
200 settings := getCodereviewSettings(g, *ref)
201 landCommit, real_ref := getLandingCommit(g, *ref, settings)
202
203 confirmAction(g.URL(), *action, *ref, commit, textDiff.Diff)
204
205 acquireObjects(repo, g, commit, landCommit, treeDiff)
206
207 var left, right git.ObjectID
208 if *action == "cherry-pick" {
209 left, right = commit.Parents()[0], commit.ID()
210 } else if *action == "revert" {
211 right, left = commit.Parents()[0], commit.ID()
212 }
213 toPush := createCommit(*action, repo, left, right, landCommit.ID())
214 id := toPush.ID().String()
215
216 confirmAction(g.URL(), "push", real_ref, toPush, repo.GetTextDiff(id+"~" , id))
217 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698