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)
}