commit 4b6c9460c99283f193f32bd5177bb09047abacf3 from: Stefan Sperling date: Thu Mar 05 08:41:12 2020 UTC 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 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