github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/review/git-review/branch.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  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"regexp"
    13  	"strings"
    14  )
    15  
    16  // Branch describes a Git branch.
    17  type Branch struct {
    18  	Name     string // branch name
    19  	changeID string // Change-Id of pending commit ("" if nothing pending)
    20  	subject  string // first line of pending commit ("" if nothing pending)
    21  }
    22  
    23  // CurrentBranch returns the current branch.
    24  func CurrentBranch() *Branch {
    25  	name := getOutput("git", "rev-parse", "--abbrev-ref", "HEAD")
    26  	return &Branch{Name: name}
    27  }
    28  
    29  func (b *Branch) OriginBranch() string {
    30  	argv := []string{"git", "rev-parse", "--abbrev-ref", "@{u}"}
    31  	out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput()
    32  	if err == nil && len(out) > 0 {
    33  		return string(bytes.TrimSpace(out))
    34  	}
    35  	if strings.Contains(string(out), "No upstream configured") {
    36  		// Assume branch was created before we set upstream correctly.
    37  		return "origin/master"
    38  	}
    39  	fmt.Fprintf(os.Stderr, "%v\n%s\n", commandString(argv[0], argv[1:]), out)
    40  	dief("%v", err)
    41  	panic("not reached")
    42  }
    43  
    44  func (b *Branch) IsLocalOnly() bool {
    45  	return "origin/"+b.Name != b.OriginBranch()
    46  }
    47  
    48  func (b *Branch) HasPendingCommit() bool {
    49  	head := getOutput("git", "rev-parse", b.Name)
    50  	base := getOutput("git", "merge-base", b.OriginBranch(), b.Name)
    51  	return base != head
    52  }
    53  
    54  func (b *Branch) ChangeID() string {
    55  	if b.changeID == "" {
    56  		if b.HasPendingCommit() {
    57  			b.changeID = headChangeID(b.Name)
    58  			b.subject = commitSubject(b.Name)
    59  		}
    60  	}
    61  	return b.changeID
    62  }
    63  
    64  func (b *Branch) Subject() string {
    65  	b.ChangeID() // page in subject
    66  	return b.subject
    67  }
    68  
    69  func commitSubject(ref string) string {
    70  	const f = "--format=format:%s"
    71  	return getOutput("git", "log", "-n", "1", f, ref, "--")
    72  }
    73  
    74  func headChangeID(branch string) string {
    75  	const (
    76  		p = "Change-Id: "
    77  		f = "--format=format:%b"
    78  	)
    79  	for _, s := range getLines("git", "log", "-n", "1", f, branch, "--") {
    80  		if strings.HasPrefix(s, p) {
    81  			return strings.TrimSpace(strings.TrimPrefix(s, p))
    82  		}
    83  	}
    84  	dief("no Change-Id line found in HEAD commit on branch %s.", branch)
    85  	panic("unreachable")
    86  }
    87  
    88  // Submitted reports whether some form of b's pending commit
    89  // has been cherry picked to origin.
    90  func (b *Branch) Submitted(id string) bool {
    91  	return len(getOutput("git", "log", "--grep", "Change-Id: "+id, b.OriginBranch())) > 0
    92  }
    93  
    94  var stagedRe = regexp.MustCompile(`^[ACDMR]  `)
    95  
    96  func HasStagedChanges() bool {
    97  	for _, s := range getLines("git", "status", "-b", "--porcelain") {
    98  		if stagedRe.MatchString(s) {
    99  			return true
   100  		}
   101  	}
   102  	return false
   103  }
   104  
   105  func LocalBranches() []*Branch {
   106  	var branches []*Branch
   107  	for _, s := range getLines("git", "branch", "-q") {
   108  		branches = append(branches, &Branch{Name: strings.TrimPrefix(s, "* ")})
   109  	}
   110  	return branches
   111  }
   112  
   113  func OriginBranches() []string {
   114  	var branches []string
   115  	for _, line := range getLines("git", "branch", "-a", "-q") {
   116  		if i := strings.Index(line, " -> "); i >= 0 {
   117  			line = line[:i]
   118  		}
   119  		name := strings.TrimSpace(strings.TrimPrefix(line, "* "))
   120  		if strings.HasPrefix(name, "remotes/origin/") {
   121  			branches = append(branches, strings.TrimPrefix(name, "remotes/"))
   122  		}
   123  	}
   124  	return branches
   125  }