github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/review/git-codereview/review.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // TODO(rsc): Document multi-change branch behavior. 6 7 // Command git-codereview provides a simple command-line user interface for 8 // working with git repositories and the Gerrit code review system. 9 // See "git-codereview help" for details. 10 package main // import "golang.org/x/review/git-codereview" 11 12 import ( 13 "bytes" 14 "flag" 15 "fmt" 16 "io" 17 "os" 18 "os/exec" 19 "strconv" 20 "strings" 21 ) 22 23 var ( 24 flags *flag.FlagSet 25 verbose = new(count) // installed as -v below 26 noRun = new(bool) 27 ) 28 29 func initFlags() { 30 flags = flag.NewFlagSet("", flag.ExitOnError) 31 flags.Usage = func() { 32 fmt.Fprintf(stderr(), usage, os.Args[0], os.Args[0]) 33 } 34 flags.Var(verbose, "v", "report commands") 35 flags.BoolVar(noRun, "n", false, "print but do not run commands") 36 } 37 38 const globalFlags = "[-n] [-v]" 39 40 const usage = `Usage: %s <command> ` + globalFlags + ` 41 Type "%s help" for more information. 42 ` 43 44 const help = `Usage: %s <command> ` + globalFlags + ` 45 46 The git-codereview command is a wrapper for the git command that provides a 47 simple interface to the "single-commit feature branch" development model. 48 49 See the docs for details: https://godoc.org/golang.org/x/review/git-codereview 50 51 The -v flag prints all commands that make changes. 52 The -n flag prints all commands that would be run, but does not run them. 53 54 Available commands: 55 56 change [name] 57 Create a change commit, or amend an existing change commit, 58 with the staged changes. If a branch name is provided, check 59 out that branch (creating it if it does not exist). 60 Does not amend the existing commit when switching branches. 61 If -q is specified, skip the editing of an extant pending 62 change's commit message. 63 If -a is specified, automatically add any unstaged changes in 64 tracked files during commit. 65 66 gofmt [-l] 67 Run gofmt on all tracked files in the staging area and the 68 working tree. 69 If -l is specified, list files that need formatting. 70 Otherwise, reformat files in place. 71 72 help 73 Show this help text. 74 75 hooks 76 Install Git commit hooks for Gerrit and gofmt. 77 Every other operation except help also does this, 78 if they are not already installed. 79 80 mail [-f] [-r reviewer,...] [-cc mail,...] 81 Upload change commit to the code review server and send mail 82 requesting a code review. 83 If -f is specified, upload even if there are staged changes. 84 The -r and -cc flags identify the email addresses of people to 85 do the code review and to be CC'ed about the code review. 86 Multiple addresses are given as a comma-separated list. 87 88 mail -diff 89 Show the changes but do not send mail or upload. 90 91 pending [-c] [-l] [-s] 92 Show the status of all pending changes and staged, unstaged, 93 and untracked files in the local repository. 94 If -c is specified, show only changes on the current branch. 95 If -l is specified, only use locally available information. 96 If -s is specified, show short output. 97 98 submit [-i | commit-hash...] 99 Push the pending change to the Gerrit server and tell Gerrit to 100 submit it to the master branch. 101 102 sync 103 Fetch changes from the remote repository and merge them into 104 the current branch, rebasing the change commit on top of them. 105 106 107 ` 108 109 func main() { 110 initFlags() 111 112 if len(os.Args) < 2 { 113 flags.Usage() 114 if dieTrap != nil { 115 dieTrap() 116 } 117 os.Exit(2) 118 } 119 command, args := os.Args[1], os.Args[2:] 120 121 if command == "help" { 122 fmt.Fprintf(stdout(), help, os.Args[0]) 123 return 124 } 125 126 installHook() 127 128 switch command { 129 case "branchpoint": 130 cmdBranchpoint(args) 131 case "change": 132 cmdChange(args) 133 case "gofmt": 134 cmdGofmt(args) 135 case "hook-invoke": 136 cmdHookInvoke(args) 137 case "hooks": 138 // done - installHook already ran 139 case "mail", "m": 140 cmdMail(args) 141 case "pending": 142 cmdPending(args) 143 case "rebase-work": 144 cmdRebaseWork(args) 145 case "submit": 146 cmdSubmit(args) 147 case "sync": 148 cmdSync(args) 149 case "test-loadAuth": // for testing only 150 loadAuth() 151 default: 152 flags.Usage() 153 } 154 } 155 156 func expectZeroArgs(args []string, command string) { 157 flags.Parse(args) 158 if len(flags.Args()) > 0 { 159 fmt.Fprintf(stderr(), "Usage: %s %s %s\n", os.Args[0], command, globalFlags) 160 os.Exit(2) 161 } 162 } 163 164 func run(command string, args ...string) { 165 if err := runErr(command, args...); err != nil { 166 if *verbose == 0 { 167 // If we're not in verbose mode, print the command 168 // before dying to give context to the failure. 169 fmt.Fprintf(stderr(), "(running: %s)\n", commandString(command, args)) 170 } 171 dief("%v", err) 172 } 173 } 174 175 func runErr(command string, args ...string) error { 176 return runDirErr("", command, args...) 177 } 178 179 var runLogTrap []string 180 181 func runDirErr(dir, command string, args ...string) error { 182 if *verbose > 0 || *noRun { 183 fmt.Fprintln(stderr(), commandString(command, args)) 184 } 185 if *noRun { 186 return nil 187 } 188 if runLogTrap != nil { 189 runLogTrap = append(runLogTrap, strings.TrimSpace(command+" "+strings.Join(args, " "))) 190 } 191 cmd := exec.Command(command, args...) 192 cmd.Stdin = os.Stdin 193 cmd.Stdout = stdout() 194 cmd.Stderr = stderr() 195 return cmd.Run() 196 } 197 198 // cmdOutput runs the command line, returning its output. 199 // If the command cannot be run or does not exit successfully, 200 // cmdOutput dies. 201 // 202 // NOTE: cmdOutput must be used only to run commands that read state, 203 // not for commands that make changes. Commands that make changes 204 // should be run using runDirErr so that the -v and -n flags apply to them. 205 func cmdOutput(command string, args ...string) string { 206 return cmdOutputDir(".", command, args...) 207 } 208 209 // cmdOutputDir runs the command line in dir, returning its output. 210 // If the command cannot be run or does not exit successfully, 211 // cmdOutput dies. 212 // 213 // NOTE: cmdOutput must be used only to run commands that read state, 214 // not for commands that make changes. Commands that make changes 215 // should be run using runDirErr so that the -v and -n flags apply to them. 216 func cmdOutputDir(dir, command string, args ...string) string { 217 s, err := cmdOutputDirErr(dir, command, args...) 218 if err != nil { 219 fmt.Fprintf(stderr(), "%v\n%s\n", commandString(command, args), s) 220 dief("%v", err) 221 } 222 return s 223 } 224 225 // cmdOutputErr runs the command line in dir, returning its output 226 // and any error results. 227 // 228 // NOTE: cmdOutputErr must be used only to run commands that read state, 229 // not for commands that make changes. Commands that make changes 230 // should be run using runDirErr so that the -v and -n flags apply to them. 231 func cmdOutputErr(command string, args ...string) (string, error) { 232 return cmdOutputDirErr(".", command, args...) 233 } 234 235 // cmdOutputDirErr runs the command line in dir, returning its output 236 // and any error results. 237 // 238 // NOTE: cmdOutputDirErr must be used only to run commands that read state, 239 // not for commands that make changes. Commands that make changes 240 // should be run using runDirErr so that the -v and -n flags apply to them. 241 func cmdOutputDirErr(dir, command string, args ...string) (string, error) { 242 // NOTE: We only show these non-state-modifying commands with -v -v. 243 // Otherwise things like 'git sync -v' show all our internal "find out about 244 // the git repo" commands, which is confusing if you are just trying to find 245 // out what git sync means. 246 if *verbose > 1 { 247 fmt.Fprintln(stderr(), commandString(command, args)) 248 } 249 cmd := exec.Command(command, args...) 250 if dir != "." { 251 cmd.Dir = dir 252 } 253 b, err := cmd.CombinedOutput() 254 return string(b), err 255 } 256 257 // trim is shorthand for strings.TrimSpace. 258 func trim(text string) string { 259 return strings.TrimSpace(text) 260 } 261 262 // trimErr applies strings.TrimSpace to the result of cmdOutput(Dir)Err, 263 // passing the error along unmodified. 264 func trimErr(text string, err error) (string, error) { 265 return strings.TrimSpace(text), err 266 } 267 268 // lines returns the lines in text. 269 func lines(text string) []string { 270 out := strings.Split(text, "\n") 271 // Split will include a "" after the last line. Remove it. 272 if n := len(out) - 1; n >= 0 && out[n] == "" { 273 out = out[:n] 274 } 275 return out 276 } 277 278 // nonBlankLines returns the non-blank lines in text. 279 func nonBlankLines(text string) []string { 280 var out []string 281 for _, s := range lines(text) { 282 if strings.TrimSpace(s) != "" { 283 out = append(out, s) 284 } 285 } 286 return out 287 } 288 289 func commandString(command string, args []string) string { 290 return strings.Join(append([]string{command}, args...), " ") 291 } 292 293 var dieTrap func() 294 295 func dief(format string, args ...interface{}) { 296 printf(format, args...) 297 die() 298 } 299 300 func die() { 301 if dieTrap != nil { 302 dieTrap() 303 } 304 os.Exit(1) 305 } 306 307 func verbosef(format string, args ...interface{}) { 308 if *verbose > 0 { 309 printf(format, args...) 310 } 311 } 312 313 var stdoutTrap, stderrTrap *bytes.Buffer 314 315 func stdout() io.Writer { 316 if stdoutTrap != nil { 317 return stdoutTrap 318 } 319 return os.Stdout 320 } 321 322 func stderr() io.Writer { 323 if stderrTrap != nil { 324 return stderrTrap 325 } 326 return os.Stderr 327 } 328 329 func printf(format string, args ...interface{}) { 330 fmt.Fprintf(stderr(), "%s: %s\n", os.Args[0], fmt.Sprintf(format, args...)) 331 } 332 333 // count is a flag.Value that is like a flag.Bool and a flag.Int. 334 // If used as -name, it increments the count, but -name=x sets the count. 335 // Used for verbose flag -v. 336 type count int 337 338 func (c *count) String() string { 339 return fmt.Sprint(int(*c)) 340 } 341 342 func (c *count) Set(s string) error { 343 switch s { 344 case "true": 345 *c++ 346 case "false": 347 *c = 0 348 default: 349 n, err := strconv.Atoi(s) 350 if err != nil { 351 return fmt.Errorf("invalid count %q", s) 352 } 353 *c = count(n) 354 } 355 return nil 356 } 357 358 func (c *count) IsBoolFlag() bool { 359 return true 360 }