func cmdMail()

in git-codereview/mail.go [16:173]


func cmdMail(args []string) {
	// NOTE: New flags should be added to the usage message below as well as doc.go.
	var (
		rList  = new(stringList) // installed below
		ccList = new(stringList) // installed below

		diff        = flags.Bool("diff", false, "show change commit diff and don't upload or mail")
		force       = flags.Bool("f", false, "mail even if there are staged changes")
		hashtagList = new(stringList) // installed below
		noKeyCheck  = flags.Bool("nokeycheck", false, "set 'git push -o nokeycheck', to prevent Gerrit from checking for private keys")
		topic       = flags.String("topic", "", "set Gerrit topic")
		trust       = flags.Bool("trust", false, "add a Trust+1 vote to the CL")
		trybot      = flags.Bool("trybot", false, "run trybots on the uploaded CLs")
		wip         = flags.Bool("wip", false, "set the status of a change to Work-in-Progress")
	)
	flags.Var(rList, "r", "comma-separated list of reviewers")
	flags.Var(ccList, "cc", "comma-separated list of people to CC:")
	flags.Var(hashtagList, "hashtag", "comma-separated list of tags to set")

	flags.Usage = func() {
		fmt.Fprintf(stderr(),
			"Usage: %s mail %s [-r reviewer,...] [-cc mail,...]\n"+
				"\t[-f] [-diff] [-hashtag tag,...] [-nokeycheck] [-topic topic]\n"+
				"\t[-trust] [-trybot] [-wip] [commit]\n", progName, globalFlags)
		exit(2)
	}
	flags.Parse(args)
	if len(flags.Args()) > 1 {
		flags.Usage()
		exit(2)
	}

	b := CurrentBranch()

	var c *Commit
	if len(flags.Args()) == 1 {
		c = b.CommitByRev("mail", flags.Arg(0))
	} else {
		c = b.DefaultCommit("mail", "must specify commit on command line; use HEAD to mail all pending changes")
	}

	if *diff {
		run("git", "diff", b.Branchpoint()[:7]+".."+c.ShortHash, "--")
		return
	}

	if len(ListFiles(c)) == 0 && len(c.Parents) == 1 {
		dief("cannot mail: commit %s is empty", c.ShortHash)
	}

	foundCommit := false
	for _, c1 := range b.Pending() {
		if c1 == c {
			foundCommit = true
		}
		if !foundCommit {
			continue
		}
		if strings.Contains(strings.ToLower(c1.Message), "do not mail") {
			dief("%s: CL says DO NOT MAIL", c1.ShortHash)
		}
		if strings.HasPrefix(c1.Message, "fixup!") {
			dief("%s: CL is a fixup! commit", c1.ShortHash)
		}
		if strings.HasPrefix(c1.Message, "squash!") {
			dief("%s: CL is a squash! commit", c1.ShortHash)
		}

		for _, f := range ListFiles(c1) {
			if strings.HasPrefix(f, ".#") || strings.HasSuffix(f, "~") ||
				(strings.HasPrefix(f, "#") && strings.HasSuffix(f, "#")) {
				dief("cannot mail temporary files: %s", f)
			}
		}
	}
	if !foundCommit {
		// b.CommitByRev and b.DefaultCommit both return a commit on b.
		dief("internal error: did not find chosen commit on current branch")
	}

	if !*force && HasStagedChanges() {
		dief("there are staged changes; aborting.\n"+
			"Use '%s change' to include them or '%s mail -f' to force it.", progName, progName)
	}

	if !utf8.ValidString(c.Message) {
		dief("cannot mail message with invalid UTF-8")
	}
	for _, r := range c.Message {
		if !unicode.IsPrint(r) && !unicode.IsSpace(r) {
			dief("cannot mail message with non-printable rune %q", r)
		}
	}

	// for side effect of dying with a good message if origin is GitHub
	loadGerritOrigin()

	refSpec := b.PushSpec(c)
	start := "%"
	if *rList != "" {
		refSpec += mailList(start, "r", string(*rList))
		start = ","
	}
	if *ccList != "" {
		refSpec += mailList(start, "cc", string(*ccList))
		start = ","
	}
	if *hashtagList != "" {
		for _, tag := range strings.Split(string(*hashtagList), ",") {
			if tag == "" {
				dief("hashtag may not contain empty tags")
			}
			refSpec += start + "hashtag=" + tag
			start = ","
		}
	}
	if *topic != "" {
		// There's no way to escape the topic, but the only
		// ambiguous character is ',' (though other characters
		// like ' ' will be rejected outright by git).
		if strings.Contains(*topic, ",") {
			dief("topic may not contain a comma")
		}
		refSpec += start + "topic=" + *topic
		start = ","
	}
	if *trust {
		refSpec += start + "l=Trust"
		start = ","
	}
	if *trybot {
		refSpec += start + "l=Run-TryBot"
		start = ","
	}
	if *wip {
		refSpec += start + "wip"
		start = ","
	}
	args = []string{"push", "-q"}
	if *noKeyCheck {
		args = append(args, "-o", "nokeycheck")
	}
	args = append(args, "origin", refSpec)
	run("git", args...)

	// Create local tag for mailed change.
	// If in the 'work' branch, this creates or updates work.mailed.
	// Older mailings are in the reflog, so work.mailed is newest,
	// work.mailed@{1} is the one before that, work.mailed@{2} before that,
	// and so on.
	// Git doesn't actually have a concept of a local tag,
	// but Gerrit won't let people push tags to it, so the tag
	// can't propagate out of the local client into the official repo.
	// There is no conflict with the branch names people are using
	// for work, because git change rejects any name containing a dot.
	// The space of names with dots is ours (the Go team's) to define.
	run("git", "tag", "--no-sign", "-f", b.Name+".mailed", c.ShortHash)
}