From: Junio C Hamano Date: Mon, 8 Sep 2008 05:44:45 +0000 (-0700) Subject: Merge branch 'jc/maint-checkout-fix' X-Git-Tag: v1.6.1-rc1~266 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/11bd3ddb913afd9665e7e822ea54be4fc8d2d5ea?hp=-c Merge branch 'jc/maint-checkout-fix' * jc/maint-checkout-fix: checkout: do not check out unmerged higher stages randomly Conflicts: t/t7201-co.sh --- 11bd3ddb913afd9665e7e822ea54be4fc8d2d5ea diff --combined builtin-checkout.c index efdb1e02bf,8544010994..d986ac7abb --- a/builtin-checkout.c +++ b/builtin-checkout.c @@@ -76,6 -76,15 +76,15 @@@ static int read_tree_some(struct tree * return 0; } + static int skip_same_name(struct cache_entry *ce, int pos) + { + while (++pos < active_nr && + !strcmp(active_cache[pos]->name, ce->name)) + ; /* skip */ + return pos; + } + + static int checkout_paths(struct tree *source_tree, const char **pathspec) { int pos; @@@ -107,6 -116,20 +116,20 @@@ if (report_path_error(ps_matched, pathspec, 0)) return 1; + /* Any unmerged paths? */ + for (pos = 0; pos < active_nr; pos++) { + struct cache_entry *ce = active_cache[pos]; + if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (!ce_stage(ce)) + continue; + errs = 1; + error("path '%s' is unmerged", ce->name); + pos = skip_same_name(ce, pos) - 1; + } + } + if (errs) + return 1; + /* Now we are committed to check them out */ memset(&state, 0, sizeof(state)); state.force = 1; @@@ -114,7 -137,11 +137,11 @@@ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { - errs |= checkout_entry(ce, &state, NULL); + if (!ce_stage(ce)) { + errs |= checkout_entry(ce, &state, NULL); + continue; + } + pos = skip_same_name(ce, pos) - 1; } } @@@ -157,7 -184,7 +184,7 @@@ struct checkout_opts int force; int writeout_error; - char *new_branch; + const char *new_branch; int new_branch_log; enum branch_track track; }; @@@ -386,11 -413,13 +413,11 @@@ static int switch_branches(struct check } /* - * If the new thing isn't a branch and isn't HEAD and we're - * not starting a new branch, and we want messages, and we - * weren't on a branch, and we're moving to a new commit, - * describe the old commit. + * If we were on a detached HEAD, but we are now moving to + * a new commit, we want to mention the old commit once more + * to remind the user that it might be lost. */ - if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch && - !opts->quiet && !old.path && new->commit != old.commit) + if (!opts->quiet && !old.path && new->commit != old.commit) describe_detached_head("Previous HEAD position was", old.commit); if (!old.commit) { @@@ -435,28 -464,13 +462,28 @@@ int cmd_checkout(int argc, const char * git_config(git_default_config, NULL); - opts.track = git_branch_track; + opts.track = BRANCH_TRACK_UNSPECIFIED; argc = parse_options(argc, argv, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - if (!opts.new_branch && (opts.track != git_branch_track)) - die("git checkout: --track and --no-track require -b"); + /* --track without -b should DWIM */ + if (0 < opts.track && !opts.new_branch) { + const char *argv0 = argv[0]; + if (!argc || !strcmp(argv0, "--")) + die ("--track needs a branch name"); + if (!prefixcmp(argv0, "refs/")) + argv0 += 5; + if (!prefixcmp(argv0, "remotes/")) + argv0 += 8; + argv0 = strchr(argv0, '/'); + if (!argv0 || !argv0[1]) + die ("Missing branch name; try -b"); + opts.new_branch = argv0 + 1; + } + + if (opts.track == BRANCH_TRACK_UNSPECIFIED) + opts.track = git_branch_track; if (opts.force && opts.merge) die("git checkout: -f and -m are incompatible"); diff --combined t/t7201-co.sh index 543b1c2898,83a366f1e7..25181388f8 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@@ -3,7 -3,7 +3,7 @@@ # Copyright (c) 2006 Junio C Hamano # -test_description='git-checkout tests. +test_description='git checkout tests. Creates master, forks renamer and side branches from it. Test switching across them. @@@ -337,36 -337,26 +337,58 @@@ test_expect_success test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && test_must_fail git checkout --track -b track' +test_expect_success \ + 'checkout with --track fakes a sensible -b ' ' + git update-ref refs/remotes/origin/koala/bear renamer && + git update-ref refs/new/koala/bear renamer && + + git checkout --track origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track refs/remotes/origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track remotes/origin/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" && + + git checkout master && git branch -D koala/bear && + + git checkout --track refs/new/koala/bear && + test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" && + test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" +' + +test_expect_success \ + 'checkout with --track, but without -b, fails with too short tracked name' ' + test_must_fail git checkout --track renamer' + + test_expect_success 'checkout an unmerged path should fail' ' + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + cat sample >fild && + cat sample >file && + cat sample >filf && + test_must_fail git checkout fild file filf && + test_cmp sample fild && + test_cmp sample filf && + test_cmp sample file + ' + test_done