@matheusbsbs / matheus-bagliano / commits / 0cb6ad3c3d
Fork 0

checkout: fix bug with ambiguous refs

The usual dwim_ref lookup prefers tags to branches. Because checkout primarily works on branches, though, we switch that behavior to prefer branches. However, there was a bug in the implementation in which we used lookup_commit_reference (which used the regular lookup rules) to get the actual commit to checkout. Checking out an ambiguous ref therefore ended up putting us in an extremely broken state in which we wrote the branch ref into HEAD, but actually checked out the tree for the tag. This patch fixes the bug by always attempting to pull the commit to be checked out from the branch-ified version of the name we were given. Patch by Junio, tests and commit message from Jeff King. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 11, 2011 at 15:00 UTC 0cb6ad3c3d8e9c738686ef8dc6f173f725e509bc
2 files changed +72 -10
builtin/checkout.c
+13 -10
@@ -678,7 +678,7 @@ static const char *unique_tracking_name(const char *name)
678 int cmd_checkout(int argc, const char **argv, const char *prefix)
679 {
680 struct checkout_opts opts;
681 - unsigned char rev[20];
681 + unsigned char rev[20], branch_rev[20];
682 const char *arg;
683 struct branch_info new;
684 struct tree *source_tree = NULL;
@@ -832,18 +832,21 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
832 argc--;
833
834 new.name = arg;
835 - if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
836 - setup_branch_path(&new);
835 + setup_branch_path(&new);
836
838 - if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
839 - resolve_ref(new.path, rev, 1, NULL))
840 - ;
841 - else
842 - new.path = NULL;
837 + if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK &&
838 + resolve_ref(new.path, branch_rev, 1, NULL))
839 + hashcpy(rev, branch_rev);
840 + else
841 + new.path = NULL; /* not an existing branch */
842 +
843 + if (!(new.commit = lookup_commit_reference_gently(rev, 1))) {
844 + /* not a commit */
845 + source_tree = parse_tree_indirect(rev);
846 + } else {
847 parse_commit(new.commit);
848 source_tree = new.commit->tree;
845 - } else
846 - source_tree = parse_tree_indirect(rev);
849 + }
850
851 if (!source_tree) /* case (1): want a tree */
852 die("reference is not a tree: %s", arg);
t/t2019-checkout-amiguous-ref.sh new
+59
@@ -0,0 +1,59 @@
1 +#!/bin/sh
2 +
3 +test_description='checkout handling of ambiguous (branch/tag) refs'
4 +. ./test-lib.sh
5 +
6 +test_expect_success 'setup ambiguous refs' '
7 + test_commit branch file &&
8 + git branch ambiguity &&
9 + git branch vagueness &&
10 + test_commit tag file &&
11 + git tag ambiguity &&
12 + git tag vagueness HEAD:file &&
13 + test_commit other file
14 +'
15 +
16 +test_expect_success 'checkout ambiguous ref succeeds' '
17 + git checkout ambiguity >stdout 2>stderr
18 +'
19 +
20 +test_expect_success 'checkout produces ambiguity warning' '
21 + grep "warning.*ambiguous" stderr
22 +'
23 +
24 +test_expect_success 'checkout chooses branch over tag' '
25 + echo refs/heads/ambiguity >expect &&
26 + git symbolic-ref HEAD >actual &&
27 + test_cmp expect actual &&
28 + echo branch >expect &&
29 + test_cmp expect file
30 +'
31 +
32 +test_expect_success 'checkout reports switch to branch' '
33 + grep "Switched to branch" stderr &&
34 + ! grep "^HEAD is now at" stderr
35 +'
36 +
37 +test_expect_success 'checkout vague ref succeeds' '
38 + git checkout vagueness >stdout 2>stderr &&
39 + test_set_prereq VAGUENESS_SUCCESS
40 +'
41 +
42 +test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' '
43 + grep "warning.*ambiguous" stderr
44 +'
45 +
46 +test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
47 + echo refs/heads/vagueness >expect &&
48 + git symbolic-ref HEAD >actual &&
49 + test_cmp expect actual &&
50 + echo branch >expect &&
51 + test_cmp expect file
52 +'
53 +
54 +test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
55 + grep "Switched to branch" stderr &&
56 + ! grep "^HEAD is now at" stderr
57 +'
58 +
59 +test_done