@matheusbsbs / matheus-bagliano / commits / 38901a4837
Fork 0

checkout --ours/--theirs: allow checking out one side of a conflicting merge

This lets you to check out 'our' (or 'their') version of an unmerged path out of the index while resolving conflicts. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Aug 30, 2008 at 07:48 UTC 38901a48375952ab6c02f22bddfa19ac2bec2c36
3 files changed +72 -3
Documentation/git-checkout.txt
+9 -2
@@ -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' [-f] [<tree-ish>] [--] <paths>...
12 +'git checkout' [-f|--ours|--theirs] [<tree-ish>] [--] <paths>...
13
14 DESCRIPTION
15 -----------
@@ -33,7 +33,9 @@ working tree.
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.
36 +Using -f will ignore these unmerged entries. The contents from a
37 +specific side of the merge can be checked out of the index by
38 +using --ours or --theirs.
39
40 OPTIONS
41 -------
@@ -48,6 +50,11 @@ OPTIONS
50 When checking out paths from the index, do not fail upon unmerged
51 entries; instead, unmerged entries are ignored.
52
53 +--ours::
54 +--theirs::
55 + When checking out paths from the index, check out stage #2
56 + ('ours') or #3 ('theirs') for unmerged paths.
57 +
58 -b::
59 Create a new branch named <new_branch> and start it at
60 <branch>. The new branch name must pass all checks defined
builtin-checkout.c
+38 -1
@@ -24,6 +24,7 @@ struct checkout_opts {
24 int quiet;
25 int merge;
26 int force;
27 + int writeout_stage;
28 int writeout_error;
29
30 const char *new_branch;
@@ -95,6 +96,32 @@ static int skip_same_name(struct cache_entry *ce, int pos)
96 return pos;
97 }
98
99 +static int check_stage(int stage, struct cache_entry *ce, int pos)
100 +{
101 + while (pos < active_nr &&
102 + !strcmp(active_cache[pos]->name, ce->name)) {
103 + if (ce_stage(active_cache[pos]) == stage)
104 + return 0;
105 + pos++;
106 + }
107 + return error("path '%s' does not have %s version",
108 + ce->name,
109 + (stage == 2) ? "our" : "their");
110 +}
111 +
112 +static int checkout_stage(int stage, struct cache_entry *ce, int pos,
113 + struct checkout *state)
114 +{
115 + while (pos < active_nr &&
116 + !strcmp(active_cache[pos]->name, ce->name)) {
117 + if (ce_stage(active_cache[pos]) == stage)
118 + return checkout_entry(active_cache[pos], state, NULL);
119 + pos++;
120 + }
121 + return error("path '%s' does not have %s version",
122 + ce->name,
123 + (stage == 2) ? "our" : "their");
124 +}
125
126 static int checkout_paths(struct tree *source_tree, const char **pathspec,
127 struct checkout_opts *opts)
@@ -106,7 +133,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
133 int flag;
134 struct commit *head;
135 int errs = 0;
109 -
136 + int stage = opts->writeout_stage;
137 int newfd;
138 struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
139
@@ -136,6 +163,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
163 continue;
164 if (opts->force) {
165 warning("path '%s' is unmerged", ce->name);
166 + } else if (stage) {
167 + errs |= check_stage(stage, ce, pos);
168 } else {
169 errs = 1;
170 error("path '%s' is unmerged", ce->name);
@@ -157,6 +186,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
186 errs |= checkout_entry(ce, &state, NULL);
187 continue;
188 }
189 + if (stage)
190 + errs |= checkout_stage(stage, ce, pos, &state);
191 pos = skip_same_name(ce, pos) - 1;
192 }
193 }
@@ -458,6 +489,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
489 OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
490 OPT_SET_INT('t', "track", &opts.track, "track",
491 BRANCH_TRACK_EXPLICIT),
492 + OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
493 + 2),
494 + OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
495 + 3),
496 OPT_BOOLEAN('f', NULL, &opts.force, "force"),
497 OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
498 OPT_END(),
@@ -573,6 +608,8 @@ no_reference:
608 if (new.name && !new.commit) {
609 die("Cannot switch branch to a non-commit.");
610 }
611 + if (opts.writeout_stage)
612 + die("--ours/--theirs is incompatible with switching branches.");
613
614 return switch_branches(&opts, &new);
615 }
t/t7201-co.sh
+25
@@ -382,4 +382,29 @@ test_expect_success 'checkout with an unmerged path can be ignored' '
382 test_cmp sample file
383 '
384
385 +test_expect_success 'checkout unmerged stage' '
386 + rm -f .git/index &&
387 + O=$(echo original | git hash-object -w --stdin) &&
388 + A=$(echo ourside | git hash-object -w --stdin) &&
389 + B=$(echo theirside | git hash-object -w --stdin) &&
390 + (
391 + echo "100644 $A 0 fild" &&
392 + echo "100644 $O 1 file" &&
393 + echo "100644 $A 2 file" &&
394 + echo "100644 $B 3 file" &&
395 + echo "100644 $A 0 filf"
396 + ) | git update-index --index-info &&
397 + echo "none of the above" >sample &&
398 + echo ourside >expect &&
399 + cat sample >fild &&
400 + cat sample >file &&
401 + cat sample >filf &&
402 + git checkout --ours . &&
403 + test_cmp expect fild &&
404 + test_cmp expect filf &&
405 + test_cmp expect file &&
406 + git checkout --theirs file &&
407 + test ztheirside = "z$(cat file)"
408 +'
409 +
410 test_done