func checkoutOrCreate()

in git-codereview/change.go [121:200]


func checkoutOrCreate(target string) {
	// If it's a valid Gerrit number CL or CL/PS or GitHub pull request number PR,
	// checkout the CL or PR.
	cl, ps, isCL := parseCL(target)
	if isCL {
		what := "CL"
		if !haveGerrit() && haveGitHub() {
			what = "PR"
			if ps != "" {
				dief("change PR syntax is NNN not NNN.PP")
			}
		}
		if what == "CL" && !haveGerrit() {
			dief("cannot change to a CL without gerrit")
		}
		if HasStagedChanges() || HasUnstagedChanges() {
			dief("cannot change to a %s with uncommitted work", what)
		}
		checkoutCL(what, cl, ps)
		return
	}

	if strings.ToUpper(target) == "HEAD" {
		// Git gets very upset and confused if you 'git change head'
		// on systems with case-insensitive file names: the branch
		// head conflicts with the usual HEAD.
		dief("invalid branch name %q: ref name HEAD is reserved for git.", target)
	}

	// If local branch exists, check it out.
	for _, b := range LocalBranches() {
		if b.Name == target {
			run("git", "checkout", "-q", target)
			printf("changed to branch %v.", target)
			return
		}
	}

	// If origin branch exists, create local branch tracking it.
	for _, name := range OriginBranches() {
		if name == "origin/"+target {
			run("git", "checkout", "-q", "-t", "-b", target, name)
			printf("created branch %v tracking %s.", target, name)
			return
		}
	}

	// Otherwise, this is a request to create a local work branch.
	// Check for reserved names. We take everything with a dot.
	if strings.Contains(target, ".") {
		dief("invalid branch name %v: branch names with dots are reserved for git-codereview.", target)
	}

	// If the current branch has a pending commit, building
	// on top of it will not help. Don't allow that.
	// Otherwise, inherit branchpoint and upstream from the current branch.
	b := CurrentBranch()
	branchpoint := "HEAD"
	if b.HasPendingCommit() {
		fmt.Fprintf(stderr(), "warning: pending changes on %s are not copied to new branch %s\n", b.Name, target)
		branchpoint = b.Branchpoint()
	}

	origin := b.OriginBranch()

	// NOTE: This is different from git checkout -q -t -b origin,
	// because the -t wold use the origin directly, and that may be
	// ahead of the current directory. The goal of this command is
	// to create a new branch for work on the current directory,
	// not to incorporate new commits at the same time (use 'git sync' for that).
	// The ideal is that HEAD doesn't change at all.
	// In the absence of pending commits, that ideal is achieved.
	// But if there are pending commits, it'd be too confusing to have them
	// on two different work branches, so we drop them and use the
	// branchpoint they started from (after warning above), moving HEAD
	// as little as possible.
	run("git", "checkout", "-q", "-b", target, branchpoint)
	run("git", "branch", "-q", "--set-upstream-to", origin)
	printf("created branch %v tracking %s.", target, origin)
}