From db9410990ee41f2b253763621c0023c782ec86e2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Aug 2008 07:46:55 -0700 Subject: [PATCH] checkout -f: allow ignoring unmerged paths when checking out of the index Earlier we made "git checkout $pathspec" to atomically refuse the operation of $pathspec matched any path with unmerged stages. This patch allows: $ git checkout -f a b c to ignore, instead of error out on, such unmerged paths. The fix to prevent checkout of an unmerged path from random stages is still there. Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 19 +++++++++++----- builtin-checkout.c | 41 +++++++++++++++++++--------------- t/t7201-co.sh | 23 +++++++++++++++++++ 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 5aa69c0e1..15fdb08ce 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [[--track | --no-track] -b [-l]] [-m] [] -'git checkout' [] [--] ... +'git checkout' [-f] [] [--] ... DESCRIPTION ----------- @@ -23,14 +23,17 @@ options, which will be passed to `git branch`. When are given, this command does *not* switch branches. It updates the named paths in the working tree from -the index file (i.e. it runs `git checkout-index -f -u`), or -from a named commit. In -this case, the `-f` and `-b` options are meaningless and giving +the index file, or from a named commit. In +this case, the `-b` options is meaningless and giving either of them results in an error. argument can be used to specify a specific tree-ish (i.e. commit, tag or tree) to update the index for the given paths before updating the working tree. +The index may contain unmerged entries after a failed merge. By +default, if you try to check out such an entry from the index, the +checkout operation will fail and nothing will be checked out. +Using -f will ignore these unmerged entries. OPTIONS ------- @@ -38,8 +41,12 @@ OPTIONS Quiet, suppress feedback messages. -f:: - Proceed even if the index or the working tree differs - from HEAD. This is used to throw away local changes. + When switching branches, proceed even if the index or the + working tree differs from HEAD. This is used to throw away + local changes. ++ +When checking out paths from the index, do not fail upon unmerged +entries; instead, unmerged entries are ignored. -b:: Create a new branch named and start it at diff --git a/builtin-checkout.c b/builtin-checkout.c index 854401099..1303f3b5b 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -20,6 +20,17 @@ static const char * const checkout_usage[] = { NULL, }; +struct checkout_opts { + int quiet; + int merge; + int force; + int writeout_error; + + const char *new_branch; + int new_branch_log; + enum branch_track track; +}; + static int post_checkout_hook(struct commit *old, struct commit *new, int changed) { @@ -85,7 +96,8 @@ static int skip_same_name(struct cache_entry *ce, int pos) } -static int checkout_paths(struct tree *source_tree, const char **pathspec) +static int checkout_paths(struct tree *source_tree, const char **pathspec, + struct checkout_opts *opts) { int pos; struct checkout state; @@ -122,8 +134,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) if (pathspec_match(pathspec, NULL, ce->name, 0)) { if (!ce_stage(ce)) continue; - errs = 1; - error("path '%s' is unmerged", ce->name); + if (opts->force) { + warning("path '%s' is unmerged", ce->name); + } else { + errs = 1; + error("path '%s' is unmerged", ce->name); + } pos = skip_same_name(ce, pos) - 1; } } @@ -178,17 +194,6 @@ static void describe_detached_head(char *msg, struct commit *commit) strbuf_release(&sb); } -struct checkout_opts { - int quiet; - int merge; - int force; - int writeout_error; - - char *new_branch; - int new_branch_log; - enum branch_track track; -}; - static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) { struct unpack_trees_options opts; @@ -554,15 +559,15 @@ no_reference: die("invalid path specification"); /* Checkout paths */ - if (opts.new_branch || opts.force || opts.merge) { + if (opts.new_branch || opts.merge) { if (argc == 1) { - die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); + die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); } else { - die("git checkout: updating paths is incompatible with switching branches/forcing"); + die("git checkout: updating paths is incompatible with switching branches."); } } - return checkout_paths(source_tree, pathspec); + return checkout_paths(source_tree, pathspec, &opts); } if (new.name && !new.commit) { diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 83a366f1e..88692f987 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -359,4 +359,27 @@ test_expect_success 'checkout an unmerged path should fail' ' test_cmp sample file ' +test_expect_success 'checkout with an unmerged path can be ignored' ' + 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 && + echo ourside >expect && + cat sample >fild && + cat sample >file && + cat sample >filf && + git checkout -f fild file filf && + test_cmp expect fild && + test_cmp expect filf && + test_cmp sample file +' + test_done -- 2.26.2