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