Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 package main | |
| 5 | |
| 6 import ( | |
| 7 "bufio" | |
| 8 "flag" | |
| 9 "fmt" | |
| 10 "io/ioutil" | |
| 11 "os" | |
| 12 "strings" | |
| 13 | |
| 14 "github.com/daviddengcn/go-colortext" | |
|
Vadim Sh.
2014/10/21 15:27:00
did you manage to add this to Goop file?
| |
| 15 | |
| 16 "infra/libs/git" | |
| 17 "infra/libs/gitiles" | |
| 18 ) | |
| 19 | |
| 20 var commitish = flag.String("commit", "", "The commit to process (or commit posi tion number).") | |
| 21 var action = flag.String("action", "", "The action to take. Must specify `cherry -pick|revert`.") | |
| 22 var ref = flag.String("ref", "", "The target ref to change.") | |
| 23 var workdir string | |
| 24 | |
| 25 func getCodereviewSettings(g *gitiles.Gitiles, ref string) (ret map[string]strin g) { | |
| 26 crSettings, err := g.Text("+", ref, "codereview.settings") | |
| 27 if err == nil { | |
| 28 lines := strings.Split(string(crSettings), "\n") | |
| 29 ret = make(map[string]string, len(lines)) | |
| 30 for _, line := range lines { | |
| 31 if len(line) == 0 || line[0] == '#' { | |
| 32 continue | |
| 33 } | |
| 34 keyVal := strings.Split(line, ":") | |
| 35 if len(keyVal) == 2 { | |
| 36 ret[keyVal[0]] = strings.TrimSpace(keyVal[1]) | |
| 37 } | |
| 38 } | |
| 39 } | |
| 40 return ret | |
| 41 } | |
| 42 | |
| 43 func getLandingCommit(g *gitiles.Gitiles, ref string, settings map[string]string ) (*git.Commit, string) { | |
| 44 prefix, ok := settings["PENDING_REF_PREFIX"] | |
| 45 if ok { | |
| 46 ref = strings.Replace(ref, "refs/", prefix, 1) | |
| 47 } | |
| 48 result, err := g.GetObjectFromPath(ref) | |
| 49 failIf(err) | |
| 50 if result.Type() != git.CommitType { | |
| 51 panic(fmt.Errorf("ref points at non-commit? %#v", result)) | |
| 52 } | |
| 53 return result.(*git.Commit), ref | |
| 54 } | |
| 55 | |
| 56 func confirmAction(URL, action, ref string, commit *git.Commit, diff string) { | |
| 57 verbPart := "to" | |
| 58 if action == "revert" { | |
| 59 verbPart = "from" | |
| 60 } | |
| 61 | |
| 62 fmt.Printf("Planning to %s commit %s %s %s in %s:\n\n", | |
| 63 action, commit.ID(), verbPart, ref, URL) | |
| 64 | |
| 65 ct.ChangeColor(ct.Yellow, false, ct.None, false) | |
| 66 fmt.Println("commit", commit.ID()) | |
| 67 ct.ResetColor() | |
| 68 fmt.Printf("Author: %s <%s>\n", commit.Author().Name, commit.Author().Em ail) | |
| 69 fmt.Printf("Date: %s\n", commit.Author().Time) | |
| 70 fmt.Println() | |
| 71 for _, l := range commit.MessageLines() { | |
| 72 fmt.Printf(" %s\n", l) | |
| 73 } | |
| 74 fmt.Println() | |
| 75 for _, pair := range commit.FooterPairs() { | |
| 76 ct.ChangeColor(ct.White, true, ct.None, false) | |
| 77 fmt.Printf(" %s: ", pair.Key) | |
| 78 ct.ResetColor() | |
| 79 fmt.Println(pair.Value) | |
| 80 } | |
| 81 fmt.Println() | |
| 82 for _, line := range strings.Split(diff, "\n") { | |
| 83 if len(line) > 0 { | |
| 84 switch { | |
| 85 case strings.HasPrefix(line, "+++") || strings.HasPrefix (line, "---"): | |
| 86 ct.ChangeColor(ct.White, true, ct.None, false) | |
| 87 case line[0] == '@': | |
| 88 ct.ChangeColor(ct.Cyan, false, ct.None, false) | |
| 89 bits := strings.SplitN(line, "@@", 3) | |
| 90 fmt.Print("@@", bits[1], "@@") | |
| 91 ct.ResetColor() | |
| 92 fmt.Println(bits[2]) | |
| 93 continue | |
| 94 case line[0] == '+': | |
| 95 ct.ChangeColor(ct.Green, false, ct.None, false) | |
| 96 case line[0] == '-': | |
| 97 ct.ChangeColor(ct.Red, false, ct.None, false) | |
| 98 case line[0] == ' ': | |
| 99 ct.ResetColor() | |
| 100 default: | |
| 101 ct.ChangeColor(ct.White, true, ct.None, false) | |
| 102 } | |
| 103 } | |
| 104 fmt.Println(line) | |
| 105 } | |
| 106 ct.ResetColor() | |
| 107 | |
| 108 fmt.Println() | |
| 109 fmt.Printf("Continue? [y/N] ") | |
| 110 answerRaw, err := bufio.NewReader(os.Stdin).ReadBytes('\n') | |
| 111 failIf(err) | |
| 112 | |
| 113 answer := strings.TrimSpace(string(answerRaw)) | |
| 114 | |
| 115 if len(answer) != 0 && strings.HasPrefix("yes", strings.ToLower(answer)) { | |
| 116 return | |
| 117 } | |
| 118 | |
| 119 fmt.Println("Action aborted") | |
| 120 os.Exit(1) | |
| 121 } | |
| 122 | |
| 123 func getCommitData(g *gitiles.Gitiles, gitSha string) (*git.Commit, git.TreeDiff ) { | |
| 124 commitRslt, err := g.GetObjectFromPath(gitSha) | |
| 125 failIf(err) | |
| 126 | |
| 127 commit, ok := commitRslt.(*git.Commit) | |
| 128 if !ok { | |
| 129 failIf(fmt.Errorf("%s is not a commit?", gitSha)) | |
| 130 } | |
| 131 | |
| 132 commitDiffRslt, err := g.GetCommitDiff(gitSha) | |
| 133 failIf(err) | |
| 134 | |
| 135 return commit, commitDiffRslt | |
| 136 } | |
| 137 | |
| 138 func main() { | |
| 139 cwd, err := os.Getwd() | |
| 140 if err != nil { | |
| 141 panic(err) | |
| 142 } | |
| 143 flag.StringVar(&workdir, "workdir", cwd, "The work directory to use.") | |
| 144 | |
| 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) | |
| 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, err := g.GetTextCommitDiff(commit.ID()) | |
| 198 failIf(err) | |
| 199 | |
| 200 settings := getCodereviewSettings(g, *ref) | |
| 201 landCommit, real_ref := getLandingCommit(g, *ref, settings) | |
| 202 | |
| 203 confirmAction(g.URL(), *action, *ref, commit, textDiff) | |
| 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 diff, err := repo.GetTextDiff(id+"~", id) | |
| 217 failIf(err) | |
| 218 | |
| 219 confirmAction(g.URL(), "push", real_ref, toPush, diff) | |
| 220 } | |
| OLD | NEW |