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

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 <gitster@pobox.com>

Junio C Hamano committed Aug 30, 2008 at 07:46 UTC db9410990ee41f2b253763621c0023c782ec86e2
3 files changed +59 -24
Documentation/git-checkout.txt
+13 -6
@@ -9,7 +9,7 @@ SYNOPSIS
9 --------
10 [verse]
11 'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
12 -'git checkout' [<tree-ish>] [--] <paths>...
12 +'git checkout' [-f] [<tree-ish>] [--] <paths>...
13
14 DESCRIPTION
15 -----------
@@ -23,14 +23,17 @@ options, which will be passed to `git branch`.
23
24 When <paths> are given, this command does *not* switch
25 branches. It updates the named paths in the working tree from
26 -the index file (i.e. it runs `git checkout-index -f -u`), or
27 -from a named commit. In
28 -this case, the `-f` and `-b` options are meaningless and giving
26 +the index file, or from a named commit. In
27 +this case, the `-b` options is meaningless and giving
28 either of them results in an error. <tree-ish> argument can be
29 used to specify a specific tree-ish (i.e. commit, tag or tree)
30 to update the index for the given paths before updating the
31 working tree.
32
33 +The index may contain unmerged entries after a failed merge. By
34 +default, if you try to check out such an entry from the index, the
35 +checkout operation will fail and nothing will be checked out.
36 +Using -f will ignore these unmerged entries.
37
38 OPTIONS
39 -------
@@ -38,8 +41,12 @@ OPTIONS
41 Quiet, suppress feedback messages.
42
43 -f::
41 - Proceed even if the index or the working tree differs
42 - from HEAD. This is used to throw away local changes.
44 + When switching branches, proceed even if the index or the
45 + working tree differs from HEAD. This is used to throw away
46 + local changes.
47 ++
48 +When checking out paths from the index, do not fail upon unmerged
49 +entries; instead, unmerged entries are ignored.
50
51 -b::
52 Create a new branch named <new_branch> and start it at
builtin-checkout.c
+23 -18
@@ -20,6 +20,17 @@ static const char * const checkout_usage[] = {
20 NULL,
21 };
22
23 +struct checkout_opts {
24 + int quiet;
25 + int merge;
26 + int force;
27 + int writeout_error;
28 +
29 + const char *new_branch;
30 + int new_branch_log;
31 + enum branch_track track;
32 +};
33 +
34 static int post_checkout_hook(struct commit *old, struct commit *new,
35 int changed)
36 {
@@ -85,7 +96,8 @@ static int skip_same_name(struct cache_entry *ce, int pos)
96 }
97
98
88 -static int checkout_paths(struct tree *source_tree, const char **pathspec)
99 +static int checkout_paths(struct tree *source_tree, const char **pathspec,
100 + struct checkout_opts *opts)
101 {
102 int pos;
103 struct checkout state;
@@ -122,8 +134,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
134 if (pathspec_match(pathspec, NULL, ce->name, 0)) {
135 if (!ce_stage(ce))
136 continue;
125 - errs = 1;
126 - error("path '%s' is unmerged", ce->name);
137 + if (opts->force) {
138 + warning("path '%s' is unmerged", ce->name);
139 + } else {
140 + errs = 1;
141 + error("path '%s' is unmerged", ce->name);
142 + }
143 pos = skip_same_name(ce, pos) - 1;
144 }
145 }
@@ -178,17 +194,6 @@ static void describe_detached_head(char *msg, struct commit *commit)
194 strbuf_release(&sb);
195 }
196
181 -struct checkout_opts {
182 - int quiet;
183 - int merge;
184 - int force;
185 - int writeout_error;
186 -
187 - char *new_branch;
188 - int new_branch_log;
189 - enum branch_track track;
190 -};
191 -
197 static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)
198 {
199 struct unpack_trees_options opts;
@@ -554,15 +559,15 @@ no_reference:
559 die("invalid path specification");
560
561 /* Checkout paths */
557 - if (opts.new_branch || opts.force || opts.merge) {
562 + if (opts.new_branch || opts.merge) {
563 if (argc == 1) {
559 - 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]);
564 + 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]);
565 } else {
561 - die("git checkout: updating paths is incompatible with switching branches/forcing");
566 + die("git checkout: updating paths is incompatible with switching branches.");
567 }
568 }
569
565 - return checkout_paths(source_tree, pathspec);
570 + return checkout_paths(source_tree, pathspec, &opts);
571 }
572
573 if (new.name && !new.commit) {
t/t7201-co.sh
+23
@@ -359,4 +359,27 @@ test_expect_success 'checkout an unmerged path should fail' '
359 test_cmp sample file
360 '
361
362 +test_expect_success 'checkout with an unmerged path can be ignored' '
363 + rm -f .git/index &&
364 + O=$(echo original | git hash-object -w --stdin) &&
365 + A=$(echo ourside | git hash-object -w --stdin) &&
366 + B=$(echo theirside | git hash-object -w --stdin) &&
367 + (
368 + echo "100644 $A 0 fild" &&
369 + echo "100644 $O 1 file" &&
370 + echo "100644 $A 2 file" &&
371 + echo "100644 $B 3 file" &&
372 + echo "100644 $A 0 filf"
373 + ) | git update-index --index-info &&
374 + echo "none of the above" >sample &&
375 + echo ourside >expect &&
376 + cat sample >fild &&
377 + cat sample >file &&
378 + cat sample >filf &&
379 + git checkout -f fild file filf &&
380 + test_cmp expect fild &&
381 + test_cmp expect filf &&
382 + test_cmp sample file
383 +'
384 +
385 test_done