github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/review/git-review/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(adg): recognize non-master remote branches 6 // TODO(adg): accept -a flag on 'commit' (like git commit -a) 7 // TODO(adg): check style of commit message 8 // TOOD(adg): print gerrit votes on 'pending' 9 // TODO(adg): add gofmt commit hook 10 // TODO(adg): print changed files on review sync 11 // TODO(adg): translate email addresses without @ by looking up somewhere 12 13 // Command git-review provides a simple command-line user interface for 14 // working with git repositories and the Gerrit code review system. 15 // See "git-review help" for details. 16 package main // import "golang.org/x/review/git-review" 17 18 import ( 19 "bytes" 20 "flag" 21 "fmt" 22 "os" 23 "os/exec" 24 "strconv" 25 "strings" 26 ) 27 28 var ( 29 flags *flag.FlagSet 30 verbose = new(count) // installed as -v below 31 noRun = new(bool) 32 ) 33 34 func initFlags() { 35 flags = flag.NewFlagSet("", flag.ExitOnError) 36 flags.Usage = func() { 37 fmt.Fprintf(os.Stderr, usage, os.Args[0], os.Args[0]) 38 } 39 flags.Var(verbose, "v", "report git commands") 40 flags.BoolVar(noRun, "n", false, "print but do not run commands") 41 } 42 43 const globalFlags = "[-n] [-v]" 44 45 const usage = `Usage: %s <command> ` + globalFlags + ` 46 Type "%s help" for more information. 47 ` 48 49 const help = `Usage: %s <command> ` + globalFlags + ` 50 51 The review command is a wrapper for the git command that provides a simple 52 interface to the "single-commit feature branch" development model. 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 62 gofmt 63 TBD 64 65 help 66 Show this help text. 67 68 hooks 69 Install Git commit hooks for Gerrit and gofmt. 70 Every other operation except help also does this, if they are not 71 already installed. 72 73 mail [-f] [-r reviewer,...] [-cc mail,...] 74 Upload change commit to the code review server and send mail 75 requesting a code review. 76 If -f is specified, upload even if there are staged changes. 77 78 mail -diff 79 Show the changes but do not send mail or upload. 80 81 pending [-r] 82 Show local branches and their head commits. 83 If -r is specified, show additional information from Gerrit. 84 85 sync 86 Fetch changes from the remote repository and merge them into 87 the current branch, rebasing the change commit on top of them. 88 89 90 ` 91 92 func main() { 93 initFlags() 94 95 if len(os.Args) < 2 { 96 flags.Usage() 97 if dieTrap != nil { 98 dieTrap() 99 } 100 os.Exit(2) 101 } 102 command, args := os.Args[1], os.Args[2:] 103 104 if command == "help" { 105 fmt.Fprintf(os.Stdout, help, os.Args[0]) 106 return 107 } 108 109 installHook() 110 111 switch command { 112 case "change", "c": 113 change(args) 114 case "gofmt": 115 dief("gofmt not implemented") 116 case "hooks": 117 // done - installHook already ran 118 case "mail", "m": 119 mail(args) 120 case "pending", "p": 121 pending(args) 122 case "sync", "s": 123 doSync(args) 124 default: 125 flags.Usage() 126 } 127 } 128 129 func expectZeroArgs(args []string, command string) { 130 flags.Parse(args) 131 if len(flags.Args()) > 0 { 132 fmt.Fprintf(os.Stderr, "Usage: %s %s %s\n", os.Args[0], command, globalFlags) 133 os.Exit(2) 134 } 135 } 136 137 func run(command string, args ...string) { 138 if err := runErr(command, args...); err != nil { 139 if *verbose == 0 { 140 // If we're not in verbose mode, print the command 141 // before dying to give context to the failure. 142 fmt.Fprintln(os.Stderr, commandString(command, args)) 143 } 144 dief("%v", err) 145 } 146 } 147 148 var runLog []string 149 150 func runErr(command string, args ...string) error { 151 if *verbose > 0 || *noRun { 152 fmt.Fprintln(os.Stderr, commandString(command, args)) 153 } 154 if *noRun { 155 return nil 156 } 157 if runLog != nil { 158 runLog = append(runLog, strings.TrimSpace(command+" "+strings.Join(args, " "))) 159 } 160 cmd := exec.Command(command, args...) 161 cmd.Stdin = os.Stdin 162 cmd.Stdout = os.Stdout 163 cmd.Stderr = os.Stderr 164 return cmd.Run() 165 } 166 167 // getOutput runs the specified command and returns its combined standard 168 // output and standard error outputs. 169 // NOTE: It should only be used to run commands that return information, 170 // **not** commands that make any actual changes. 171 func getOutput(command string, args ...string) string { 172 // NOTE: We only show these non-state-modifying commands with -v -v. 173 // Otherwise things like 'git sync -v' show all our internal "find out about 174 // the git repo" commands, which is confusing if you are just trying to find 175 // out what git sync means. 176 if *verbose > 1 { 177 fmt.Fprintln(os.Stderr, commandString(command, args)) 178 } 179 b, err := exec.Command(command, args...).CombinedOutput() 180 if err != nil { 181 fmt.Fprintf(os.Stderr, "%v\n%s\n", commandString(command, args), b) 182 dief("%v", err) 183 } 184 return string(bytes.TrimSpace(b)) 185 } 186 187 // getLines is like getOutput but it returns non-empty output lines. 188 // NOTE: It should only be used to run commands that return information, 189 // **not** commands that make any actual changes. 190 func getLines(command string, args ...string) []string { 191 var s []string 192 for _, l := range strings.Split(getOutput(command, args...), "\n") { 193 s = append(s, strings.TrimSpace(l)) 194 } 195 return s 196 } 197 198 func commandString(command string, args []string) string { 199 return strings.Join(append([]string{command}, args...), " ") 200 } 201 202 var dieTrap func() 203 204 func dief(format string, args ...interface{}) { 205 printf(format, args...) 206 if dieTrap != nil { 207 dieTrap() 208 } 209 os.Exit(1) 210 } 211 212 func verbosef(format string, args ...interface{}) { 213 if *verbose > 0 { 214 printf(format, args...) 215 } 216 } 217 218 func printf(format string, args ...interface{}) { 219 fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], fmt.Sprintf(format, args...)) 220 } 221 222 // count is a flag.Value that is like a flag.Bool and a flag.Int. 223 // If used as -name, it increments the count, but -name=x sets the count. 224 // Used for verbose flag -v. 225 type count int 226 227 func (c *count) String() string { 228 return fmt.Sprint(int(*c)) 229 } 230 231 func (c *count) Set(s string) error { 232 switch s { 233 case "true": 234 *c++ 235 case "false": 236 *c = 0 237 default: 238 n, err := strconv.Atoi(s) 239 if err != nil { 240 return fmt.Errorf("invalid count %q", s) 241 } 242 *c = count(n) 243 } 244 return nil 245 } 246 247 func (c *count) IsBoolFlag() bool { 248 return true 249 }