github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/review/git-review/change.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  package main
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"regexp"
    11  	"strings"
    12  )
    13  
    14  var changeQuick bool
    15  
    16  func change(args []string) {
    17  	flags.BoolVar(&changeQuick, "q", false, "do not edit commit msg when updating commit")
    18  	flags.Parse(args)
    19  	if len(flags.Args()) > 1 {
    20  		fmt.Fprintf(os.Stderr, "Usage: %s change %s [branch]\n", os.Args[0], globalFlags)
    21  		os.Exit(2)
    22  
    23  	}
    24  
    25  	// Checkout or create branch, if specified.
    26  	target := flags.Arg(0)
    27  	if target != "" {
    28  		checkoutOrCreate(target)
    29  	}
    30  
    31  	// Create or amend change commit.
    32  	b := CurrentBranch()
    33  	if !b.IsLocalOnly() {
    34  		if target != "" {
    35  			// Permit "review change master".
    36  			return
    37  		}
    38  		dief("can't commit to %s branch (use '%s change branchname').", b.Name, os.Args[0])
    39  	}
    40  
    41  	if b.ChangeID() == "" {
    42  		// No change commit on this branch, create one.
    43  		commitChanges(false)
    44  		return
    45  	}
    46  	if target != "" {
    47  		// If we switched to an existing branch, don't amend the
    48  		// commit. (The user can run 'review change' to do that.)
    49  		return
    50  	}
    51  	// Amend the commit.
    52  	commitChanges(true)
    53  }
    54  
    55  var testCommitMsg string
    56  
    57  func commitChanges(amend bool) {
    58  	if !HasStagedChanges() {
    59  		printf("no staged changes. Did you forget to 'git add'?")
    60  	}
    61  	commit := func(amend bool) {
    62  		args := []string{"commit", "-q", "--allow-empty"}
    63  		if amend {
    64  			args = append(args, "--amend")
    65  			if changeQuick {
    66  				args = append(args, "--no-edit")
    67  			}
    68  		}
    69  		if testCommitMsg != "" {
    70  			args = append(args, "-m", testCommitMsg)
    71  		}
    72  		run("git", args...)
    73  	}
    74  	commit(amend)
    75  	for !commitMessageOK() {
    76  		fmt.Print("re-edit commit message (y/n)? ")
    77  		if !scanYes() {
    78  			break
    79  		}
    80  		commit(true)
    81  	}
    82  	printf("change updated.")
    83  }
    84  
    85  func checkoutOrCreate(target string) {
    86  	// If local branch exists, check it out.
    87  	for _, b := range LocalBranches() {
    88  		if b.Name == target {
    89  			run("git", "checkout", "-q", target)
    90  			printf("changed to branch %v.", target)
    91  			return
    92  		}
    93  	}
    94  
    95  	// If origin branch exists, create local branch tracking it.
    96  	for _, name := range OriginBranches() {
    97  		if name == "origin/"+target {
    98  			run("git", "checkout", "-q", "-t", "-b", target, name)
    99  			printf("created branch %v tracking %s.", target, name)
   100  			return
   101  		}
   102  	}
   103  
   104  	// Otherwise, this is a request to create a local work branch.
   105  	// Check for reserved names. We take everything with a dot.
   106  	if strings.Contains(target, ".") {
   107  		dief("invalid branch name %v: branch names with dots are reserved for git-review.", target)
   108  	}
   109  
   110  	// If the current branch has a pending commit, building
   111  	// on top of it will not help. Don't allow that.
   112  	// Otherwise, inherit HEAD and upstream from the current branch.
   113  	b := CurrentBranch()
   114  	if b.HasPendingCommit() {
   115  		dief("cannot branch from work branch; change back to %v first.", strings.TrimPrefix(b.OriginBranch(), "origin/"))
   116  	}
   117  
   118  	origin := b.OriginBranch()
   119  
   120  	// NOTE: This is different from git checkout -q -t -b branch. It does not move HEAD.
   121  	run("git", "checkout", "-q", "-b", target)
   122  	run("git", "branch", "-q", "--set-upstream-to", origin)
   123  	printf("created branch %v tracking %s.", target, origin)
   124  }
   125  
   126  var (
   127  	messageRE  = regexp.MustCompile(`^(\[[a-zA-Z0-9.-]+\] )?[a-zA-Z0-9-/, ]+: `)
   128  	oldFixesRE = regexp.MustCompile(`Fixes +(issue +#?)?([0-9]+)`)
   129  )
   130  
   131  func commitMessageOK() bool {
   132  	body := getOutput("git", "log", "--format=format:%B", "-n", "1")
   133  	ok := true
   134  	if !messageRE.MatchString(body) {
   135  		fmt.Print(commitMessageWarning)
   136  		ok = false
   137  	}
   138  	if m := oldFixesRE.FindStringSubmatch(body); m != nil {
   139  		fmt.Printf(fixesIssueWarning, m[0], m[2])
   140  		ok = false
   141  	}
   142  	return ok
   143  }
   144  
   145  const commitMessageWarning = `
   146  Your CL description appears not to use the standard form.
   147  
   148  The first line of your change description is conventionally a one-line summary
   149  of the change, prefixed by the primary affected package, and is used as the
   150  subject for code review mail; the rest of the description elaborates.
   151  
   152  Examples:
   153  
   154  	encoding/rot13: new package
   155  
   156  	math: add IsInf, IsNaN
   157  
   158  	net: fix cname in LookupHost
   159  
   160  	unicode: update to Unicode 5.0.2
   161  
   162  `
   163  
   164  const fixesIssueWarning = `
   165  Your CL description contains the string %q, which is
   166  the old Google Code way of linking commits to issues.
   167  
   168  You should rewrite it to use the GitHub convention: "Fixes #%v".
   169  
   170  `
   171  
   172  func scanYes() bool {
   173  	var s string
   174  	fmt.Scan(&s)
   175  	return strings.HasPrefix(strings.ToLower(s), "y")
   176  }