rebase: fix a memory leak
[gitweb.git] / builtin / checkout.c
index a95ba2c6dc153610569476bfa545967913f71a8e..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,11 +255,64 @@ 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_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)
 {
@@ -257,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");
@@ -297,37 +366,15 @@ 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)) {
                free(ps_matched);
@@ -348,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 {
@@ -371,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"));
@@ -542,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
@@ -569,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;
 }
 
@@ -929,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)
@@ -1065,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}";
@@ -1165,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);
@@ -1186,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");
@@ -1274,6 +1371,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(),
        };
 
@@ -1282,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);
 
@@ -1306,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