review: modify argument parsing for use as a git sub-command

For this command to be invoked as "git change", "git upload", etc
we must parse the -v and -n arguments after the command name.

Change-Id: I4beb386379457c7a49910bd0b7ee4fd8aa5cc609
Reviewed-on: https://go-review.googlesource.com/1120
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/change.go b/change.go
index b4198ab..ee5f8cc 100644
--- a/change.go
+++ b/change.go
@@ -10,16 +10,17 @@
 )
 
 func change(args []string) {
-	if len(args) > 1 {
-		fmt.Fprintf(os.Stderr, "Usage: review [-n] [-v] change [branch]\n")
+	flags.Parse(args)
+	if len(flags.Args()) > 1 {
+		fmt.Fprintf(os.Stderr, "Usage: %s change %s [branch]\n", os.Args[0], globalFlags)
 		os.Exit(2)
 
 	}
 
 	// Checkout or create branch, if specified.
 	checkedOut := false
-	if len(args) == 1 {
-		checkoutOrCreate(args[0])
+	if branch := flags.Arg(0); branch != "" {
+		checkoutOrCreate(branch)
 		checkedOut = true
 	}
 
diff --git a/pending.go b/pending.go
index 287a0ab..3e987e8 100644
--- a/pending.go
+++ b/pending.go
@@ -4,15 +4,10 @@
 
 package main
 
-import (
-	"flag"
-	"fmt"
-)
+import "fmt"
 
 func pending(args []string) {
-	if len(args) != 0 {
-		flag.Usage()
-	}
+	expectZeroArgs(args, "pending")
 	// TODO(adg): implement -r
 
 	current := currentBranchName()
diff --git a/revert.go b/revert.go
index 286740e..1a8a334 100644
--- a/revert.go
+++ b/revert.go
@@ -10,8 +10,10 @@
 )
 
 func revert(args []string) {
-	if len(args) == 0 {
-		fmt.Fprintf(os.Stderr, "Usage: %s [-n] [-v] revert files...\n", os.Args[0])
+	flags.Parse(args)
+	files := flags.Args()
+	if len(files) == 0 {
+		fmt.Fprintf(os.Stderr, "Usage: %s %s revert files...\n", os.Args[0], globalFlags)
 		os.Exit(2)
 	}
 	branch := CurrentBranch()
@@ -22,6 +24,6 @@
 		dief("no pending change; can't revert.")
 	}
 	// TODO(adg): make this work correctly before hooking it up
-	run("git", append([]string{"checkout", "HEAD^"}, args...)...)
-	run("git", append([]string{"add"}, args...)...)
+	run("git", append([]string{"checkout", "HEAD^"}, files...)...)
+	run("git", append([]string{"add"}, files...)...)
 }
diff --git a/review.go b/review.go
index cf30dab..96a6f6d 100644
--- a/review.go
+++ b/review.go
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// TODO(adg): change command-line parsing so this works as a git alias
 // TODO(adg): rename 'upload' to 'mail'
 // TODO(adg): recognize non-master remote branches
 // TODO(adg): accept -a flag on 'commit' (like git commit -a)
@@ -27,15 +26,24 @@
 )
 
 var (
-	verbose = flag.Bool("v", false, "verbose output")
-	noRun   = flag.Bool("n", false, "print but do not run commands")
+	flags   = flag.NewFlagSet("", flag.ExitOnError)
+	verbose = flags.Bool("v", false, "verbose output")
+	noRun   = flags.Bool("n", false, "print but do not run commands")
 )
 
-const usage = `Usage: %s [-n] [-v] <command>
+const globalFlags = "[-n] [-v]"
+
+const usage = `Usage: %s <command> ` + globalFlags + `
 Type "%s help" for more information.
 `
 
-const help = `Usage: %s [-n] [-v] <command>
+func init() {
+	flags.Usage = func() {
+		fmt.Fprintf(os.Stderr, usage, os.Args[0], os.Args[0])
+	}
+}
+
+const help = `Usage: %s <command> ` + globalFlags + `
 
 The review command is a wrapper for the git command that provides a simple
 interface to the "single-commit feature branch" development model.
@@ -73,19 +81,13 @@
 `
 
 func main() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, usage, os.Args[0], os.Args[0])
-	}
-	flag.Parse()
-
 	installHook()
 
-	args := flag.Args()
-	if len(args) == 0 {
-		flag.Usage()
+	if len(os.Args) < 2 {
+		flags.Usage()
 		os.Exit(2)
 	}
-	command, args := args[0], args[1:]
+	command, args := os.Args[1], os.Args[2:]
 
 	switch command {
 	case "help":
@@ -103,7 +105,15 @@
 	case "gofmt":
 		dief("gofmt not implemented")
 	default:
-		flag.Usage()
+		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)
 	}
 }
 
diff --git a/sync.go b/sync.go
index 32381f7..4914da3 100644
--- a/sync.go
+++ b/sync.go
@@ -4,12 +4,8 @@
 
 package main
 
-import "flag"
-
 func doSync(args []string) {
-	if len(args) != 0 {
-		flag.Usage()
-	}
+	expectZeroArgs(args, "sync")
 
 	// Fetch remote changes.
 	run("git", "fetch", "-q")
diff --git a/upload.go b/upload.go
index 0bd5b83..c5810fa 100644
--- a/upload.go
+++ b/upload.go
@@ -5,37 +5,25 @@
 package main
 
 import (
-	"flag"
 	"fmt"
 	"os"
 	"regexp"
 	"strings"
 )
 
-var uploadFlags struct {
-	flag.FlagSet
-	diff   bool
-	force  bool
-	rList  string
-	ccList string
-}
-
-func init() {
-	f := &uploadFlags
-	f.Usage = func() {
-		fmt.Fprintf(os.Stderr, "Usage: %s [-n] [-v] upload [-r reviewer,...] [-cc mail,...]\n", os.Args[0])
-	}
-	f.BoolVar(&f.diff, "diff", false, "show change commit diff and don't upload")
-	f.BoolVar(&f.force, "f", false, "upload even if there are staged changes")
-	f.StringVar(&f.rList, "r", "", "comma-separated list of reviewers")
-	f.StringVar(&f.ccList, "cc", "", "comma-separated list of people to CC:")
-
-}
-
 func upload(args []string) {
-	f := &uploadFlags
-	if f.Parse(args) != nil || len(f.Args()) != 0 {
-		f.Usage()
+	var (
+		diff   = flags.Bool("diff", false, "show change commit diff and don't upload")
+		force  = flags.Bool("f", false, "upload even if there are staged changes")
+		rList  = flags.String("r", "", "comma-separated list of reviewers")
+		ccList = flags.String("cc", "", "comma-separated list of people to CC:")
+	)
+	flags.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage: %s upload %s [-r reviewer,...] [-cc mail,...]\n", os.Args[0], globalFlags)
+	}
+	flags.Parse(args)
+	if len(flags.Args()) != 0 {
+		flags.Usage()
 		os.Exit(2)
 	}
 
@@ -47,24 +35,24 @@
 		dief("no pending change; can't upload.")
 	}
 
-	if f.diff {
+	if *diff {
 		run("git", "diff", "master..HEAD")
 		return
 	}
 
-	if !f.force && hasStagedChanges() {
+	if !*force && hasStagedChanges() {
 		dief("there are staged changes; aborting.\n" +
 			"Use 'review change' to include them or 'review upload -f' to force upload.")
 	}
 
 	refSpec := "HEAD:refs/for/master"
 	start := "%"
-	if f.rList != "" {
-		refSpec += mailList(start, "r", f.rList)
+	if *rList != "" {
+		refSpec += mailList(start, "r", *rList)
 		start = ","
 	}
-	if f.ccList != "" {
-		refSpec += mailList(start, "cc", f.ccList)
+	if *ccList != "" {
+		refSpec += mailList(start, "cc", *ccList)
 	}
 	run("git", "push", "-q", "origin", refSpec)
 }