Merge branch 'nd/checkout-m-doc-update'
authorJunio C Hamano <gitster@pobox.com>
Tue, 16 Apr 2019 10:28:10 +0000 (19:28 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 16 Apr 2019 10:28:10 +0000 (19:28 +0900)
Doc about the above.

* nd/checkout-m-doc-update:
  checkout.txt: note about losing staged changes with --merge

1  2 
Documentation/git-checkout.txt
builtin/checkout.c

@@@ -242,6 -242,8 +242,8 @@@ should result in deletion of the path)
  +
  When checking out paths from the index, this option lets you recreate
  the conflicted merge in the specified paths.
+ +
+ When switching branches with `--merge`, staged changes may be lost.
  
  --conflict=<style>::
        The same as --merge option above, but changes the way the
  This means that you can use `git checkout -p` to selectively discard
  edits from your current working tree. See the ``Interactive Mode''
  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 ++
 +Note that this option uses the no overlay mode by default (see also
 +`--[no-]overlay`), and currently doesn't support overlay mode.
  
  --ignore-other-worktrees::
        `git checkout` refuses when the wanted ref is already checked
        Do not attempt to create a branch if a remote tracking branch
        of the same name exists.
  
 +--[no-]overlay::
 +      In the default overlay mode, `git checkout` never
 +      removes files from the index or the working tree.  When
 +      specifying `--no-overlay`, files that appear in the index and
 +      working tree, but not in <tree-ish> are removed, to make them
 +      match <tree-ish> exactly.
 +
  <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
        when prepended with "refs/heads/", is a valid ref), then that
diff --combined builtin/checkout.c
@@@ -46,7 -46,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.
@@@ -136,8 -135,7 +136,8 @@@ static int skip_same_name(const struct 
        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)) {
                        return 0;
                pos++;
        }
 +      if (!overlay_mode)
 +              return 0;
        if (stage == 2)
                return error(_("path '%s' does not have our version"), ce->name);
        else
@@@ -172,8 -168,7 +172,8 @@@ static int check_stages(unsigned stages
  }
  
  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)) {
                                              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
@@@ -260,59 -251,6 +260,59 @@@ static int checkout_merged(int pos, con
        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)
  {
        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");
  
         * 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);
                        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 {
                        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_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) {
@@@ -616,11 -571,6 +616,11 @@@ static int skip_merge_working_tree(cons
         * 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
@@@ -726,6 -676,8 +726,8 @@@ static int merge_working_tree(const str
                        struct tree *result;
                        struct tree *work;
                        struct merge_options o;
+                       struct strbuf sb = STRBUF_INIT;
                        if (!opts->merge)
                                return 1;
  
                        if (!old_branch_info->commit)
                                return 1;
  
+                       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 */
  
                        /*
@@@ -1016,9 -975,6 +1025,9 @@@ static int switch_branches(const struc
        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)
@@@ -1256,8 -1212,6 +1265,8 @@@ static int switch_unborn_to_new_branch(
        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);
@@@ -1279,10 -1233,6 +1288,10 @@@ static int checkout_branch(struct check
                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");
@@@ -1371,7 -1321,6 +1380,7 @@@ int cmd_checkout(int argc, const char *
                            "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(),
        };
  
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
        opts.show_progress = -1;
 +      opts.overlay_mode = -1;
  
        git_config(git_checkout_config, &opts);
  
        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