aboutsummaryrefslogtreecommitdiff
path: root/git-codereview/review.go
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2014-12-18 11:25:48 +1100
committerAndrew Gerrand <adg@golang.org>2014-12-18 00:35:19 +0000
commitf473ce13dd1bba7ce531e7800fdf018f60aa2454 (patch)
treefdc20fb8b555be78ca5a4f251e04de5e77d338ea /git-codereview/review.go
parent6a0c83f0c935e49b841a3a880579cb07918bcb57 (diff)
downloadgo-x-review-f473ce13dd1bba7ce531e7800fdf018f60aa2454.tar.xz
git-codereview: rename from 'git-review' to 'git-codereview'
Mostly trivial search and replace, except for hooks.go which includes a special case to remove the old git-review hooks. Change-Id: Ic0792bb3e26607e5e0ead88958e46c3ac08288cd Reviewed-on: https://go-review.googlesource.com/1741 Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'git-codereview/review.go')
-rw-r--r--git-codereview/review.go287
1 files changed, 287 insertions, 0 deletions
diff --git a/git-codereview/review.go b/git-codereview/review.go
new file mode 100644
index 0000000..0b0ee8e
--- /dev/null
+++ b/git-codereview/review.go
@@ -0,0 +1,287 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO(adg): translate email addresses without @ by looking up somewhere
+
+// Command git-codereview provides a simple command-line user interface for
+// working with git repositories and the Gerrit code review system.
+// See "git-codereview help" for details.
+package main // import "golang.org/x/review/git-codereview"
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+)
+
+var (
+ flags *flag.FlagSet
+ verbose = new(count) // installed as -v below
+ noRun = new(bool)
+)
+
+func initFlags() {
+ flags = flag.NewFlagSet("", flag.ExitOnError)
+ flags.Usage = func() {
+ fmt.Fprintf(os.Stderr, usage, os.Args[0], os.Args[0])
+ }
+ flags.Var(verbose, "v", "report git commands")
+ flags.BoolVar(noRun, "n", false, "print but do not run commands")
+}
+
+const globalFlags = "[-n] [-v]"
+
+const usage = `Usage: %s <command> ` + globalFlags + `
+Type "%s help" for more information.
+`
+
+const help = `Usage: %s <command> ` + globalFlags + `
+
+The git-codereview command is a wrapper for the git command that provides a
+simple interface to the "single-commit feature branch" development model.
+
+See the docs for details: https://godoc.org/golang.org/x/review/git-codereview
+
+The -v flag prints all Git commands that make changes.
+The -n flag prints all commands that would be run, but does not run them.
+
+Available commands:
+
+ change [name]
+ Create a change commit, or amend an existing change commit,
+ with the staged changes. If a branch name is provided, check
+ out that branch (creating it if it does not exist).
+ Does not amend the existing commit when switching branches.
+ If -q is specified, skip the editing of an extant pending
+ change's commit message.
+ If -a is specified, automatically add any unstaged changes in
+ tracked files during commit.
+
+ gofmt [-l]
+ Run gofmt on all tracked files in the staging area and the
+ working tree.
+ If -l is specified, list files that need formatting.
+ Otherwise, reformat files in place.
+
+ help
+ Show this help text.
+
+ hooks
+ Install Git commit hooks for Gerrit and gofmt.
+ Every other operation except help also does this,
+ if they are not already installed.
+
+ mail [-f] [-r reviewer,...] [-cc mail,...]
+ Upload change commit to the code review server and send mail
+ requesting a code review.
+ If -f is specified, upload even if there are staged changes.
+ The -r and -cc flags identify the email addresses of people to
+ do the code review and to be CC'ed about the code review.
+ Multiple addresses are given as a comma-separated list.
+
+ mail -diff
+ Show the changes but do not send mail or upload.
+
+ pending [-l]
+ Show the status of all pending changes and staged, unstaged,
+ and untracked files in the local repository.
+ If -l is specified, only use locally available information.
+
+ submit
+ Push the pending change to the Gerrit server and tell Gerrit to
+ submit it to the master branch.
+
+ sync
+ Fetch changes from the remote repository and merge them into
+ the current branch, rebasing the change commit on top of them.
+
+
+`
+
+func main() {
+ initFlags()
+
+ if len(os.Args) < 2 {
+ flags.Usage()
+ if dieTrap != nil {
+ dieTrap()
+ }
+ os.Exit(2)
+ }
+ command, args := os.Args[1], os.Args[2:]
+
+ if command == "help" {
+ fmt.Fprintf(os.Stdout, help, os.Args[0])
+ return
+ }
+
+ installHook()
+
+ switch command {
+ case "change":
+ change(args)
+ case "gofmt":
+ gofmt(args)
+ case "hook-invoke":
+ hookInvoke(args)
+ case "hooks":
+ // done - installHook already ran
+ case "mail", "m":
+ mail(args)
+ case "pending":
+ pending(args)
+ case "submit":
+ submit(args)
+ case "sync":
+ doSync(args)
+ default:
+ flags.Usage()
+ }
+}
+
+func expectZeroArgs(args []string, command string) {
+ flags.Parse(args)
+ if len(flags.Args()) > 0 {
+ fmt.Fprintf(os.Stderr, "Usage: %s %s %s\n", os.Args[0], command, globalFlags)
+ os.Exit(2)
+ }
+}
+
+func run(command string, args ...string) {
+ if err := runErr(command, args...); err != nil {
+ if *verbose == 0 {
+ // If we're not in verbose mode, print the command
+ // before dying to give context to the failure.
+ fmt.Fprintln(os.Stderr, commandString(command, args))
+ }
+ dief("%v", err)
+ }
+}
+
+func runErr(command string, args ...string) error {
+ return runDirErr("", command, args...)
+}
+
+var runLogTrap []string
+
+func runDirErr(dir, command string, args ...string) error {
+ if *verbose > 0 || *noRun {
+ fmt.Fprintln(os.Stderr, commandString(command, args))
+ }
+ if *noRun {
+ return nil
+ }
+ if runLogTrap != nil {
+ runLogTrap = append(runLogTrap, strings.TrimSpace(command+" "+strings.Join(args, " ")))
+ }
+ cmd := exec.Command(command, args...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ if stdoutTrap != nil {
+ cmd.Stdout = stdoutTrap
+ }
+ cmd.Stderr = os.Stderr
+ if stderrTrap != nil {
+ cmd.Stderr = stderrTrap
+ }
+ return cmd.Run()
+}
+
+// getOutput runs the specified command and returns its combined standard
+// output and standard error outputs.
+// NOTE: It should only be used to run commands that return information,
+// **not** commands that make any actual changes.
+func getOutput(command string, args ...string) string {
+ // NOTE: We only show these non-state-modifying commands with -v -v.
+ // Otherwise things like 'git sync -v' show all our internal "find out about
+ // the git repo" commands, which is confusing if you are just trying to find
+ // out what git sync means.
+ if *verbose > 1 {
+ fmt.Fprintln(os.Stderr, commandString(command, args))
+ }
+ b, err := exec.Command(command, args...).CombinedOutput()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%v\n%s\n", commandString(command, args), b)
+ dief("%v", err)
+ }
+ return string(bytes.TrimSpace(b))
+}
+
+// getLines is like getOutput but it returns only non-empty output lines,
+// with leading and trailing spaces removed.
+// NOTE: It should only be used to run commands that return information,
+// **not** commands that make any actual changes.
+func getLines(command string, args ...string) []string {
+ var s []string
+ for _, l := range strings.Split(getOutput(command, args...), "\n") {
+ if len(strings.TrimSpace(l)) > 0 {
+ s = append(s, l)
+ }
+ }
+ return s
+}
+
+func commandString(command string, args []string) string {
+ return strings.Join(append([]string{command}, args...), " ")
+}
+
+var dieTrap func()
+
+func dief(format string, args ...interface{}) {
+ printf(format, args...)
+ if dieTrap != nil {
+ dieTrap()
+ }
+ os.Exit(1)
+}
+
+func verbosef(format string, args ...interface{}) {
+ if *verbose > 0 {
+ printf(format, args...)
+ }
+}
+
+var stdoutTrap, stderrTrap *bytes.Buffer
+
+func printf(format string, args ...interface{}) {
+ w := io.Writer(os.Stderr)
+ if stderrTrap != nil {
+ w = stderrTrap
+ }
+ fmt.Fprintf(w, "%s: %s\n", os.Args[0], fmt.Sprintf(format, args...))
+}
+
+// count is a flag.Value that is like a flag.Bool and a flag.Int.
+// If used as -name, it increments the count, but -name=x sets the count.
+// Used for verbose flag -v.
+type count int
+
+func (c *count) String() string {
+ return fmt.Sprint(int(*c))
+}
+
+func (c *count) Set(s string) error {
+ switch s {
+ case "true":
+ *c++
+ case "false":
+ *c = 0
+ default:
+ n, err := strconv.Atoi(s)
+ if err != nil {
+ return fmt.Errorf("invalid count %q", s)
+ }
+ *c = count(n)
+ }
+ return nil
+}
+
+func (c *count) IsBoolFlag() bool {
+ return true
+}