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>
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 1303f3b..16bfbb6 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -24,6 +24,7 @@
 	int quiet;
 	int merge;
 	int force;
+	int writeout_stage;
 	int writeout_error;
 
 	const char *new_branch;
@@ -95,6 +96,32 @@
 	return pos;
 }
 
+static int check_stage(int stage, struct cache_entry *ce, int pos)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return 0;
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
+
+static int checkout_stage(int stage, struct cache_entry *ce, int pos,
+			  struct checkout *state)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return checkout_entry(active_cache[pos], state, NULL);
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
 
 static int checkout_paths(struct tree *source_tree, const char **pathspec,
 			  struct checkout_opts *opts)
@@ -106,7 +133,7 @@
 	int flag;
 	struct commit *head;
 	int errs = 0;
-
+	int stage = opts->writeout_stage;
 	int newfd;
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
@@ -136,6 +163,8 @@
 				continue;
 			if (opts->force) {
 				warning("path '%s' is unmerged", ce->name);
+			} else if (stage) {
+				errs |= check_stage(stage, ce, pos);
 			} else {
 				errs = 1;
 				error("path '%s' is unmerged", ce->name);
@@ -157,6 +186,8 @@
 				errs |= checkout_entry(ce, &state, NULL);
 				continue;
 			}
+			if (stage)
+				errs |= checkout_stage(stage, ce, pos, &state);
 			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
@@ -458,6 +489,10 @@
 		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 		OPT_SET_INT('t', "track",  &opts.track, "track",
 			BRANCH_TRACK_EXPLICIT),
+		OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
+			    2),
+		OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
+			    3),
 		OPT_BOOLEAN('f', NULL, &opts.force, "force"),
 		OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
 		OPT_END(),
@@ -573,6 +608,8 @@
 	if (new.name && !new.commit) {
 		die("Cannot switch branch to a non-commit.");
 	}
+	if (opts.writeout_stage)
+		die("--ours/--theirs is incompatible with switching branches.");
 
 	return switch_branches(&opts, &new);
 }