func()

in git-codereview/branch.go [232:318]


func (b *Branch) loadPending() {
	if b.loadedPending {
		return
	}
	b.loadedPending = true

	// In case of early return.
	// But avoid the git exec unless really needed.
	b.branchpoint = ""
	defer func() {
		if b.branchpoint == "" {
			b.branchpoint = gitHash("HEAD")
		}
	}()

	if b.DetachedHead() {
		return
	}

	// Note: This runs in parallel with "git fetch -q",
	// so the commands may see a stale version of origin/master.
	// The use of origin here is for identifying what the branch has
	// in common with origin (what's old on the branch).
	// Any new commits in origin do not affect that.

	// Note: --topo-order means child first, then parent.
	origin := b.OriginBranch()
	const numField = 9
	all := trim(cmdOutput("git", "log", "--topo-order",
		"--format=format:%H%x00%h%x00%P%x00%T%x00%B%x00%s%x00%an%x00%ae%x00%at%x00",
		origin+".."+b.FullName(), "--"))
	fields := strings.Split(all, "\x00")
	if len(fields) < numField {
		return // nothing pending
	}
	for i, field := range fields {
		fields[i] = strings.TrimLeft(field, "\r\n")
	}
Log:
	for i := 0; i+numField <= len(fields); i += numField {
		parents := strings.Fields(fields[i+2])
		c := &Commit{
			Hash:        fields[i],
			ShortHash:   fields[i+1],
			Parents:     parents,
			Tree:        fields[i+3],
			Message:     fields[i+4],
			Subject:     fields[i+5],
			AuthorName:  fields[i+6],
			AuthorEmail: fields[i+7],
			AuthorDate:  fields[i+8],
		}
		if len(c.Parents) > 0 {
			c.Parent = c.Parents[0]
		}
		if len(c.Parents) > 1 {
			// Found merge point.
			// Merges break the invariant that the last shared commit (the branchpoint)
			// is the parent of the final commit in the log output.
			// If c.Parent is on the origin branch, then since we are reading the log
			// in (reverse) topological order, we know that c.Parent is the actual branchpoint,
			// even if we later see additional commits on a different branch leading down to
			// a lower location on the same origin branch.
			// Check c.Merge (the second parent) too, so we don't depend on the parent order.
			for _, parent := range c.Parents {
				if strings.Contains(cmdOutput("git", "branch", "-a", "--contains", parent), " remotes/"+origin+"\n") {
					b.pending = append(b.pending, c)
					b.branchpoint = parent
					break Log
				}
			}
		}
		for _, line := range lines(c.Message) {
			// Note: Keep going even if we find one, so that
			// we take the last Change-Id line, just in case
			// there is a commit message quoting another
			// commit message.
			// I'm not sure this can come up at all, but just in case.
			if strings.HasPrefix(line, "Change-Id: ") {
				c.ChangeID = line[len("Change-Id: "):]
			}
		}
		b.pending = append(b.pending, c)
		b.branchpoint = c.Parent
	}
	b.commitsAhead = len(b.pending)
}