difftool: remove obsolete (and misleading) comment
[gitweb.git] / builtin / checkout.c
index 32c4b7f897f894bf73976e05a74b322a9a0ef192..0e6037b2968f326ca2781fa141d74683e52c983c 100644 (file)
@@ -1,3 +1,4 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "builtin.h"
 #include "config.h"
 #include "checkout.h"
@@ -44,6 +45,8 @@ struct checkout_opts {
        int ignore_skipworktree;
        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.
@@ -115,7 +118,8 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
 
 static int read_tree_some(struct tree *tree, const struct pathspec *pathspec)
 {
-       read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
+       read_tree_recursive(the_repository, tree, "", 0, 0,
+                           pathspec, update_some, NULL);
 
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@ -132,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)) {
@@ -140,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
@@ -165,21 +172,27 @@ 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)
+                         const struct checkout *state, int *nr_checkouts,
+                         int overlay_mode)
 {
        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);
+                       return checkout_entry(active_cache[pos], state,
+                                             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
                return error(_("path '%s' does not have their version"), ce->name);
 }
 
-static int checkout_merged(int pos, const struct checkout *state)
+static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)
 {
        struct cache_entry *ce = active_cache[pos];
        const char *path = ce->name;
@@ -242,14 +255,14 @@ static int checkout_merged(int pos, const struct checkout *state)
        ce = make_transient_cache_entry(mode, &oid, path, 2);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
-       status = checkout_entry(ce, state, NULL);
+       status = checkout_entry(ce, state, NULL, nr_checkouts);
        discard_cache_entry(ce);
        return status;
 }
 
-static void mark_ce_for_checkout(struct cache_entry *ce,
-                                char *ps_matched,
-                                const struct checkout_opts *opts)
+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))
@@ -281,6 +294,25 @@ static void mark_ce_for_checkout(struct cache_entry *ce,
                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)
 {
@@ -291,6 +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, 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");
@@ -318,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"));
 
@@ -332,7 +367,14 @@ static int checkout_paths(const struct checkout_opts *opts,
         * to be checked out.
         */
        for (pos = 0; pos < active_nr; pos++)
-               mark_ce_for_checkout(active_cache[pos], ps_matched, opts);
+               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)) {
                free(ps_matched);
@@ -353,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 {
@@ -376,17 +418,44 @@ static int checkout_paths(const struct checkout_opts *opts,
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce)) {
-                               errs |= checkout_entry(ce, &state, NULL);
+                               errs |= checkout_entry(ce, &state,
+                                                      NULL, &nr_checkouts);
                                continue;
                        }
                        if (opts->writeout_stage)
-                               errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
+                               errs |= checkout_stage(opts->writeout_stage,
+                                                      ce, pos,
+                                                      &state,
+                                                      &nr_checkouts, opts->overlay_mode);
                        else if (opts->merge)
-                               errs |= checkout_merged(pos, &state);
+                               errs |= checkout_merged(pos, &state,
+                                                       &nr_unmerged);
                        pos = skip_same_name(ce, pos) - 1;
                }
        }
-       errs |= finish_delayed_checkout(&state);
+       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_("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 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);
+       }
 
        if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
@@ -547,6 +616,11 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,
         * opts->show_progress only impacts output so doesn't require a merge
         */
 
+       /*
+        * 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
@@ -574,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;
 }
 
@@ -675,7 +757,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);
 
@@ -758,7 +840,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                        free(refname);
                }
                else
-                       create_branch(opts->new_branch, new_branch_info->name,
+                       create_branch(the_repository,
+                                     opts->new_branch, new_branch_info->name,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
@@ -816,7 +899,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                                delete_reflog(old_branch_info->path);
                }
        }
-       remove_branch_state();
+       remove_branch_state(the_repository);
        strbuf_release(&msg);
        if (!opts->quiet &&
            (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
@@ -933,6 +1016,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)
@@ -1069,6 +1155,7 @@ static int parse_branchname_arg(int argc, const char **argv,
                has_dash_dash = 1; /* case (3) or (1) */
        else if (dash_dash_pos >= 2)
                die(_("only one reference expected, %d given."), dash_dash_pos);
+       opts->count_checkout_paths = !opts->quiet && !has_dash_dash;
 
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
@@ -1084,9 +1171,12 @@ static int parse_branchname_arg(int argc, const char **argv,
                 */
                int recover_with_dwim = dwim_new_local_branch_ok;
 
-               if (!has_dash_dash &&
-                   (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
+               int could_be_checkout_paths = !has_dash_dash &&
+                       check_filename(opts->prefix, arg);
+
+               if (!has_dash_dash && !no_wildcard(arg))
                        recover_with_dwim = 0;
+
                /*
                 * Accept "git checkout foo" and "git checkout foo --"
                 * as candidates for dwim.
@@ -1099,6 +1189,10 @@ static int parse_branchname_arg(int argc, const char **argv,
                        const char *remote = unique_tracking_name(arg, rev,
                                                                  dwim_remotes_matched);
                        if (remote) {
+                               if (could_be_checkout_paths)
+                                       die(_("'%s' could be both a local file and a tracking branch.\n"
+                                             "Please use -- (and optionally --no-guess) to disambiguate"),
+                                           arg);
                                *new_branch = arg;
                                arg = remote;
                                /* DWIMmed to create local branch, case (3).(b) */
@@ -1162,6 +1256,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);
@@ -1183,6 +1279,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");
@@ -1233,7 +1333,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        struct checkout_opts opts;
        struct branch_info new_branch_info;
        char *conflict_style = NULL;
-       int dwim_new_local_branch = 1;
+       int dwim_new_local_branch, no_dwim_new_local_branch = 0;
        int dwim_remotes_matched = 0;
        struct option options[] = {
                OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
@@ -1263,14 +1363,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
-               OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
-                               N_("second guess 'git checkout <no-such-branch>'")),
+               OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
+                        N_("do not second guess 'git checkout <no-such-branch>'")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
                { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
                            "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(),
        };
 
@@ -1279,6 +1380,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);
 
@@ -1287,6 +1389,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       dwim_new_local_branch = !no_dwim_new_local_branch;
        if (opts.show_progress < 0) {
                if (opts.quiet)
                        opts.show_progress = 0;
@@ -1302,6 +1405,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