Message ID | 031780431d790c16b3862d6f155693e197bb74ed.1556226502.git.liu.denton@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | checkout: allow -b/-B to work on a merge base | expand |
Denton Liu <liu.denton@gmail.com> writes: > - new_branch_info->name = arg; > + new_branch_info->name = strstr(arg, "...") ? > + xstrdup(oid_to_hex(rev)) : > + arg; Can we do better? I am not sure why we want to hardcode the knowledge of "..." syntax like this here. "git checkout A...B" introduced in 2009 needed only a single-liner change from get_sha1() to get_sha1_mb() without making the ugly implementation detail seep into this layer.
Junio C Hamano <gitster@pobox.com> writes: > Denton Liu <liu.denton@gmail.com> writes: > >> - new_branch_info->name = arg; >> + new_branch_info->name = strstr(arg, "...") ? >> + xstrdup(oid_to_hex(rev)) : >> + arg; > > Can we do better? > > I am not sure why we want to hardcode the knowledge of "..." syntax > like this here. "git checkout A...B" introduced in 2009 needed only > a single-liner change from get_sha1() to get_sha1_mb() without making > the ugly implementation detail seep into this layer. I _think_ what you are trying to work around is that a syntax that is not understood as a reference to a single revision by get_oid() but is understood as such by get_oid_mb() can be in the .name field, and that eventually gets passed to branch.c::create_branch() as "start_name" in the codepath. The function ought to know that start_name wants to name a single revision and never a revision range, but fails to use get_oid_mb() but just a plain get_oid(), and fails. If that is the case, wouldn't a better fix be more like the attached? This hasn't even been compile tested, but I suspect that without taking this approach, you would introduce a new "bug", namely, that $ git checkout -b newbranch master... ought to behave exactly like $ git branch newbranch master... $ git checkout newbranch But the first step would hit the same branch.c::create_branch() and would not work, no? The reason I care about hiding syntax details like "..." from users of get_*_mb() is because I anticipate that we'll want to extend it to things other than just "merge base between two" with syntax different from "..." (while probably renaming _mb suffix to something else). The codebase is not ready to replace get_oid() with get_oid_mb() blindly and wholesale, I think, as the former is used as an implementation detail of parsing range syntax like A..B but places that are end user facing *and* expect to take only a single revision (e.g. "rebase --onto <commit>", "checkout <commit>", etc.) and never a range that currently use get_oid() should be able to get replaced with get_oid_mb() to learn "A...B means their merge base, not a symmetric range" semantics for free. branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/branch.c b/branch.c index 28b81a7e02..a84c8aaca2 100644 --- a/branch.c +++ b/branch.c @@ -268,7 +268,7 @@ void create_branch(struct repository *r, } real_ref = NULL; - if (get_oid(start_name, &oid)) { + if (get_oid_mb(start_name, &oid)) { if (explicit_tracking) { if (advice_set_upstream_failure) { error(_(upstream_missing), start_name);
diff --git a/builtin/checkout.c b/builtin/checkout.c index ffa776c6e1..d99b3f3925 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1229,7 +1229,9 @@ static int parse_branchname_arg(int argc, const char **argv, argv++; argc--; - new_branch_info->name = arg; + new_branch_info->name = strstr(arg, "...") ? + xstrdup(oid_to_hex(rev)) : + arg; setup_branch_path(new_branch_info); if (!check_refname_format(new_branch_info->path, 0) && diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh index a3fa520d2e..d6ea556d84 100755 --- a/t/t2018-checkout-branch.sh +++ b/t/t2018-checkout-branch.sh @@ -65,7 +65,7 @@ test_expect_success 'checkout -b to a new branch, set to HEAD' ' do_checkout branch2 ' -test_expect_failure 'checkout -b to a merge base' ' +test_expect_success 'checkout -b to a merge base' ' test_when_finished test_might_fail git branch -D branch2 && test_when_finished git checkout branch1 && git checkout -b branch2 branch1... @@ -128,7 +128,7 @@ test_expect_success 'checkout -B to an existing branch resets branch to HEAD' ' do_checkout branch2 "" -B ' -test_expect_failure 'checkout -B to a merge base' ' +test_expect_success 'checkout -B to a merge base' ' git checkout branch1 && git branch -D branch2 &&
When we ran something like $ git checkout -b test master... it would fail with the message fatal: Not a valid object name: 'master...'. This was caused by the call to `create_branch` where `start_name` is expected to be a valid rev. However, git-checkout allows the branch to not be a valid rev in the case where we have "..." to specify getting the merge base. In the case where a branch with "..." is specified, use the oid instead so that `start_name` is a valid rev. Signed-off-by: Denton Liu <liu.denton@gmail.com> --- builtin/checkout.c | 4 +++- t/t2018-checkout-branch.sh | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-)