Commit Diff


commit - ee85c5e898e10f72841c918d9f453a6526ef7e2e
commit + 4b6c9460c99283f193f32bd5177bb09047abacf3
blob - 05a4896a63a16b4b11b02defcba785a8a2b95b14
blob + ed10056f87241ad9aa4d28d8c5fc24f1fb04f15f
--- got/got.1
+++ got/got.1
@@ -177,6 +177,20 @@ An abbreviated hash argument will be expanded to a ful
 automatically, provided the abbreviation is unique.
 If this option is not specified, the most recent commit on the selected
 branch will be used.
+.Pp
+If the specified
+.Ar commit
+is not contained in the selected branch, a different branch which contains
+this commit must be specified with the
+.Fl b
+option.
+If no such branch is known a new branch must be created for this
+commit with
+.Cm got branch
+before
+.Cm got checkout
+can be used.
+Checking out work trees with an unknown branch is intentionally not supported.
 .It Fl p Ar path-prefix
 Restrict the work tree to a subset of the repository's tree hierarchy.
 Only files beneath the specified
blob - 021418d817308d0e1fea429b3210b838b1a14ab3
blob + fc50380bc4c0fb4752f3e1718c004c136733c2e4
--- got/got.c
+++ got/got.c
@@ -957,6 +957,30 @@ done:
 }
 
 static const struct got_error *
+checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
+{
+	static char msg[512];
+	const char *branch_name;
+
+	if (got_ref_is_symbolic(ref))
+		branch_name = got_ref_get_symref_target(ref);
+	else
+		branch_name = got_ref_get_name(ref);
+
+	if (strncmp("refs/heads/", branch_name, 11) == 0)
+		branch_name += 11;
+
+	snprintf(msg, sizeof(msg),
+	    "target commit is not contained in branch '%s'; "
+	    "the branch to use must be specified with -b; "
+	    "if necessary a new branch can be created for "
+	    "this commit with 'got branch -c %s BRANCH_NAME'",
+	    branch_name, commit_id_str);
+
+	return got_error_msg(GOT_ERR_ANCESTRY, msg);
+}
+
+static const struct got_error *
 cmd_checkout(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
@@ -1116,11 +1140,20 @@ cmd_checkout(int argc, char *argv[])
 		    got_worktree_get_base_commit_id(worktree), 0, repo);
 		if (error != NULL) {
 			free(commit_id);
+			if (error->code == GOT_ERR_ANCESTRY) {
+				error = checkout_ancestry_error(
+				    head_ref, commit_id_str);
+			}
 			goto done;
 		}
 		error = check_same_branch(commit_id, head_ref, NULL, repo);
-		if (error)
+		if (error) {
+			if (error->code == GOT_ERR_ANCESTRY) {
+				error = checkout_ancestry_error(
+				    head_ref, commit_id_str);
+			}
 			goto done;
+		}
 		error = got_worktree_set_base_commit_id(worktree, repo,
 		    commit_id);
 		free(commit_id);
blob - 6859b9bab3ac0176db628a98074b484d5b80364b
blob + 6d3a99a2cce157075a82450a18aaa39d48615932
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
@@ -197,8 +197,14 @@ function test_checkout_commit_from_wrong_branch {
 		return 1
 	fi
 
-	echo  "got: target commit is on a different branch" \
+	echo -n "got: target commit is not contained in branch 'master'; " \
 		> $testroot/stderr.expected
+	echo -n "the branch to use must be specified with -b; if necessary " \
+		>> $testroot/stderr.expected
+	echo -n "a new branch can be created for this commit with "\
+		>> $testroot/stderr.expected
+	echo "'got branch -c $head_rev BRANCH_NAME'" \
+		>> $testroot/stderr.expected
 	cmp -s $testroot/stderr.expected $testroot/stderr
 	ret="$?"
 	if [ "$ret" != "0" ]; then