diff options
Diffstat (limited to 'builtin/checkout.c')
-rw-r--r-- | builtin/checkout.c | 188 |
1 files changed, 141 insertions, 47 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c index 6fadf412e8..ffa776c6e1 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,3 +1,4 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "config.h" #include "checkout.h" @@ -45,6 +46,7 @@ struct checkout_opts { int ignore_other_worktrees; int show_progress; int count_checkout_paths; + int overlay_mode; /* * If new checkout options are added, skip_merge_working_tree * should be updated accordingly. @@ -134,7 +136,8 @@ static int skip_same_name(const struct cache_entry *ce, int pos) return pos; } -static int check_stage(int stage, const struct cache_entry *ce, int pos) +static int check_stage(int stage, const struct cache_entry *ce, int pos, + int overlay_mode) { while (pos < active_nr && !strcmp(active_cache[pos]->name, ce->name)) { @@ -142,6 +145,8 @@ static int check_stage(int stage, const struct cache_entry *ce, int pos) return 0; pos++; } + if (!overlay_mode) + return 0; if (stage == 2) return error(_("path '%s' does not have our version"), ce->name); else @@ -167,7 +172,8 @@ static int check_stages(unsigned stages, const struct cache_entry *ce, int pos) } static int checkout_stage(int stage, const struct cache_entry *ce, int pos, - const struct checkout *state, int *nr_checkouts) + const struct checkout *state, int *nr_checkouts, + int overlay_mode) { while (pos < active_nr && !strcmp(active_cache[pos]->name, ce->name)) { @@ -176,6 +182,10 @@ static int checkout_stage(int stage, const struct cache_entry *ce, int pos, NULL, nr_checkouts); pos++; } + if (!overlay_mode) { + unlink_entry(ce); + return 0; + } if (stage == 2) return error(_("path '%s' does not have our version"), ce->name); else @@ -250,6 +260,59 @@ static int checkout_merged(int pos, const struct checkout *state, int *nr_checko return status; } +static void mark_ce_for_checkout_overlay(struct cache_entry *ce, + char *ps_matched, + const struct checkout_opts *opts) +{ + ce->ce_flags &= ~CE_MATCHED; + if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) + return; + if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) + /* + * "git checkout tree-ish -- path", but this entry + * is in the original index but is not in tree-ish + * or does not match the pathspec; it will not be + * checked out to the working tree. We will not do + * anything to this entry at all. + */ + return; + /* + * Either this entry came from the tree-ish we are + * checking the paths out of, or we are checking out + * of the index. + * + * If it comes from the tree-ish, we already know it + * matches the pathspec and could just stamp + * CE_MATCHED to it from update_some(). But we still + * need ps_matched and read_tree_recursive (and + * eventually tree_entry_interesting) cannot fill + * ps_matched yet. Once it can, we can avoid calling + * match_pathspec() for _all_ entries when + * opts->source_tree != NULL. + */ + if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) + ce->ce_flags |= CE_MATCHED; +} + +static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce, + char *ps_matched, + const struct checkout_opts *opts) +{ + ce->ce_flags &= ~CE_MATCHED; + if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) + return; + if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) { + ce->ce_flags |= CE_MATCHED; + if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) + /* + * In overlay mode, but the path is not in + * tree-ish, which means we should remove it + * from the index and the working tree. + */ + ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE; + } +} + static int checkout_paths(const struct checkout_opts *opts, const char *revision) { @@ -260,7 +323,9 @@ static int checkout_paths(const struct checkout_opts *opts, struct commit *head; int errs = 0; struct lock_file lock_file = LOCK_INIT; - int nr_checkouts = 0; + int nr_checkouts = 0, nr_unmerged = 0; + + trace2_cmd_mode(opts->patch_mode ? "patch" : "path"); if (opts->track != BRANCH_TRACK_UNSPECIFIED) die(_("'%s' cannot be used with updating paths"), "--track"); @@ -288,7 +353,7 @@ static int checkout_paths(const struct checkout_opts *opts, return run_add_interactive(revision, "--patch=checkout", &opts->pathspec); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(&opts->pathspec) < 0) return error(_("index file corrupt")); @@ -301,39 +366,17 @@ static int checkout_paths(const struct checkout_opts *opts, * Make sure all pathspecs participated in locating the paths * to be checked out. */ - for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; - ce->ce_flags &= ~CE_MATCHED; - if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) - continue; - if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) - /* - * "git checkout tree-ish -- path", but this entry - * is in the original index; it will not be checked - * out to the working tree and it does not matter - * if pathspec matched this entry. We will not do - * anything to this entry at all. - */ - continue; - /* - * Either this entry came from the tree-ish we are - * checking the paths out of, or we are checking out - * of the index. - * - * If it comes from the tree-ish, we already know it - * matches the pathspec and could just stamp - * CE_MATCHED to it from update_some(). But we still - * need ps_matched and read_tree_recursive (and - * eventually tree_entry_interesting) cannot fill - * ps_matched yet. Once it can, we can avoid calling - * match_pathspec() for _all_ entries when - * opts->source_tree != NULL. - */ - if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) - ce->ce_flags |= CE_MATCHED; - } + for (pos = 0; pos < active_nr; pos++) + if (opts->overlay_mode) + mark_ce_for_checkout_overlay(active_cache[pos], + ps_matched, + opts); + else + mark_ce_for_checkout_no_overlay(active_cache[pos], + ps_matched, + opts); - if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) { + if (report_path_error(ps_matched, &opts->pathspec)) { free(ps_matched); return 1; } @@ -352,7 +395,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->force) { warning(_("path '%s' is unmerged"), ce->name); } else if (opts->writeout_stage) { - errs |= check_stage(opts->writeout_stage, ce, pos); + errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode); } else if (opts->merge) { errs |= check_stages((1<<2) | (1<<3), ce, pos); } else { @@ -382,26 +425,34 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->writeout_stage) errs |= checkout_stage(opts->writeout_stage, ce, pos, - &state, &nr_checkouts); + &state, + &nr_checkouts, opts->overlay_mode); else if (opts->merge) errs |= checkout_merged(pos, &state, - &nr_checkouts); + &nr_unmerged); pos = skip_same_name(ce, pos) - 1; } } + remove_marked_cache_entries(&the_index, 1); + remove_scheduled_dirs(); errs |= finish_delayed_checkout(&state, &nr_checkouts); if (opts->count_checkout_paths) { + if (nr_unmerged) + fprintf_ln(stderr, Q_("Recreated %d merge conflict", + "Recreated %d merge conflicts", + nr_unmerged), + nr_unmerged); if (opts->source_tree) - fprintf_ln(stderr, Q_("Checked out %d path out of %s", - "Checked out %d paths out of %s", + fprintf_ln(stderr, Q_("Updated %d path from %s", + "Updated %d paths from %s", nr_checkouts), nr_checkouts, find_unique_abbrev(&opts->source_tree->object.oid, DEFAULT_ABBREV)); - else - fprintf_ln(stderr, Q_("Checked out %d path out of the index", - "Checked out %d paths out of the index", + else if (!nr_unmerged || nr_checkouts) + fprintf_ln(stderr, Q_("Updated %d path from the index", + "Updated %d paths from the index", nr_checkouts), nr_checkouts); } @@ -566,6 +617,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts, */ /* + * opts->overlay_mode cannot be used with switching branches so is + * not tested here + */ + + /* * If we aren't creating a new branch any changes or updates will * happen in the existing branch. Since that could only be updating * the index and working directory, we don't want to skip those steps @@ -592,6 +648,14 @@ static int skip_merge_working_tree(const struct checkout_opts *opts, * Remaining variables are not checkout options but used to track state */ + /* + * Do the merge if this is the initial checkout. We cannot use + * is_cache_unborn() here because the index hasn't been loaded yet + * so cache_nr and timestamp.sec are always zero. + */ + if (!file_exists(get_index_file())) + return 0; + return 1; } @@ -636,7 +700,7 @@ static int merge_working_tree(const struct checkout_opts *opts, topts.initial_checkout = is_cache_unborn(); topts.update = 1; topts.merge = 1; - topts.gently = opts->merge && old_branch_info->commit; + topts.quiet = opts->merge && old_branch_info->commit; topts.verbose_update = opts->show_progress; topts.fn = twoway_merge; if (opts->overwrite_ignore) { @@ -661,7 +725,10 @@ static int merge_working_tree(const struct checkout_opts *opts, */ struct tree *result; struct tree *work; + struct tree *old_tree; struct merge_options o; + struct strbuf sb = STRBUF_INIT; + if (!opts->merge) return 1; @@ -671,6 +738,19 @@ static int merge_working_tree(const struct checkout_opts *opts, */ if (!old_branch_info->commit) return 1; + old_tree = get_commit_tree(old_branch_info->commit); + + if (repo_index_has_changes(the_repository, old_tree, &sb)) + die(_("cannot continue with staged changes in " + "the following files:\n%s"), sb.buf); + strbuf_release(&sb); + + if (repo_index_has_changes(the_repository, + get_commit_tree(old_branch_info->commit), + &sb)) + warning(_("staged changes in the following files may be lost: %s"), + sb.buf); + strbuf_release(&sb); /* Do more real merge */ @@ -693,7 +773,7 @@ static int merge_working_tree(const struct checkout_opts *opts, * a pain; plumb in an option to set * o.renormalize? */ - init_merge_options(&o); + init_merge_options(&o, the_repository); o.verbosity = 0; work = write_tree_from_memory(&o); @@ -708,7 +788,7 @@ static int merge_working_tree(const struct checkout_opts *opts, ret = merge_trees(&o, get_commit_tree(new_branch_info->commit), work, - get_commit_tree(old_branch_info->commit), + old_tree, &result); if (ret < 0) exit(128); @@ -952,6 +1032,9 @@ static int switch_branches(const struct checkout_opts *opts, void *path_to_free; struct object_id rev; int flag, writeout_error = 0; + + trace2_cmd_mode("branch"); + memset(&old_branch_info, 0, sizeof(old_branch_info)); old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag); if (old_branch_info.path) @@ -1189,6 +1272,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) int status; struct strbuf branch_ref = STRBUF_INIT; + trace2_cmd_mode("unborn"); + if (!opts->new_branch) die(_("You are on a branch yet to be born")); strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); @@ -1210,6 +1295,10 @@ static int checkout_branch(struct checkout_opts *opts, die(_("'%s' cannot be used with switching branches"), "--patch"); + if (!opts->overlay_mode) + die(_("'%s' cannot be used with switching branches"), + "--no-overlay"); + if (opts->writeout_stage) die(_("'%s' cannot be used with switching branches"), "--ours/--theirs"); @@ -1298,6 +1387,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) "checkout", "control recursive updating of submodules", PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater }, OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")), + OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")), OPT_END(), }; @@ -1306,6 +1396,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.overwrite_ignore = 1; opts.prefix = prefix; opts.show_progress = -1; + opts.overlay_mode = -1; git_config(git_checkout_config, &opts); @@ -1330,6 +1421,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1) die(_("-b, -B and --orphan are mutually exclusive")); + if (opts.overlay_mode == 1 && opts.patch_mode) + die(_("-p and --overlay are mutually exclusive")); + /* * From here on, new_branch will contain the branch to be checked out, * and new_branch_force and new_orphan_branch will tell us which one of |