be helpful when users try to check out work trees without a known branch Provide a useful error message in such cases and explicitly document intentional restrictions in the got(1) man page. Prompted by a question from Adam Steen via bsd.network https://bsd.network/@adams/103768951483318235
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
diff --git a/got/got.1 b/got/got.1
index 05a4896..ed10056 100644
--- a/got/got.1
+++ b/got/got.1
@@ -177,6 +177,20 @@ An abbreviated hash argument will be expanded to a full SHA1 hash
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
diff --git a/got/got.c b/got/got.c
index 021418d..fc50380 100644
--- a/got/got.c
+++ b/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);
diff --git a/regress/cmdline/checkout.sh b/regress/cmdline/checkout.sh
index 6859b9b..6d3a99a 100755
--- a/regress/cmdline/checkout.sh
+++ b/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