diff options
author | Junio C Hamano <gitster@pobox.com> | 2008-08-30 19:44:17 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2008-08-30 19:44:26 -0700 |
commit | 29a1f99b4b81cd28163de1275265234c5ab804b4 (patch) | |
tree | 8179b07340733b5e447b81117a9d82e651d8315c /builtin-checkout.c | |
parent | c236bcd06138bcbc929b86ad1a513635bf4847b2 (diff) | |
parent | 38901a48375952ab6c02f22bddfa19ac2bec2c36 (diff) | |
download | git-29a1f99b4b81cd28163de1275265234c5ab804b4.tar.gz |
Merge branch 'jc/maint-checkout-fix' into 'jc/better-conflict-resolution'
* jc/maint-checkout-fix:
checkout --ours/--theirs: allow checking out one side of a conflicting merge
checkout -f: allow ignoring unmerged paths when checking out of the index
checkout: do not check out unmerged higher stages randomly
Diffstat (limited to 'builtin-checkout.c')
-rw-r--r-- | builtin-checkout.c | 105 |
1 files changed, 87 insertions, 18 deletions
diff --git a/builtin-checkout.c b/builtin-checkout.c index 411cc513c6..16bfbb6605 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -20,6 +20,18 @@ static const char * const checkout_usage[] = { NULL, }; +struct checkout_opts { + int quiet; + int merge; + int force; + int writeout_stage; + 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) { @@ -76,7 +88,43 @@ static int read_tree_some(struct tree *tree, const char **pathspec) return 0; } -static int checkout_paths(struct tree *source_tree, const char **pathspec) +static int skip_same_name(struct cache_entry *ce, int pos) +{ + while (++pos < active_nr && + !strcmp(active_cache[pos]->name, ce->name)) + ; /* skip */ + 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) { int pos; struct checkout state; @@ -85,7 +133,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) 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)); @@ -107,6 +155,26 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) if (report_path_error(ps_matched, pathspec, 0)) return 1; + /* Any unmerged paths? */ + for (pos = 0; pos < active_nr; pos++) { + struct cache_entry *ce = active_cache[pos]; + if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (!ce_stage(ce)) + 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); + } + pos = skip_same_name(ce, pos) - 1; + } + } + if (errs) + return 1; + /* Now we are committed to check them out */ memset(&state, 0, sizeof(state)); state.force = 1; @@ -114,7 +182,13 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { - errs |= checkout_entry(ce, &state, NULL); + if (!ce_stage(ce)) { + errs |= checkout_entry(ce, &state, NULL); + continue; + } + if (stage) + errs |= checkout_stage(stage, ce, pos, &state); + pos = skip_same_name(ce, pos) - 1; } } @@ -151,17 +225,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; @@ -426,6 +489,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) 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(), @@ -527,20 +594,22 @@ 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) { 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); } |