restore: add --worktree and --staged

'git checkout <tree-ish> <pathspec>' updates both index and worktree. But updating the index when you want to restore worktree files is non-intuitive. The index contains the data ready for the next commit, and there's no indication that the user will want to commit the restored versions. 'git restore' therefore by default only touches worktree. The user has the option to update either the index with git restore --staged --source=<tree> <path> (1) or update both with git restore --staged --worktree --source=<tree> <path> (2) PS. Orignally I wanted to make worktree update default and form (1) would add index update while also updating the worktree, and the user would need to do "--staged --no-worktree" to update index only. But it looks really confusing that "--staged" option alone updates both. So now form (2) is used for both, which reads much more obvious. PPS. Yes form (1) overlaps with "git reset <rev> <path>". I don't know if we can ever turn "git reset" back to "_always_ reset HEAD and optionally do something else". Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Apr 25, 2019 at 16:45 UTC 183fb44fd234499ed76d72d745ccb480b25f6d15
1 file changed +68 -6
builtin/checkout.c
+68 -6
@@ -66,6 +66,8 @@ struct checkout_opts {
66 int can_switch_when_in_progress;
67 int orphan_from_empty_tree;
68 int empty_pathspec_ok;
69 + int checkout_index;
70 + int checkout_worktree;
71
72 const char *new_branch;
73 const char *new_branch_force;
@@ -397,6 +399,7 @@ static int checkout_paths(const struct checkout_opts *opts,
399 struct commit *head;
400 int errs = 0;
401 struct lock_file lock_file = LOCK_INIT;
402 + int checkout_index;
403
404 trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
405
@@ -422,9 +425,26 @@ static int checkout_paths(const struct checkout_opts *opts,
425 die(_("Cannot update paths and switch to branch '%s' at the same time."),
426 opts->new_branch);
427
425 - if (opts->patch_mode)
426 - return run_add_interactive(revision, "--patch=checkout",
427 - &opts->pathspec);
428 + if (!opts->checkout_worktree && !opts->checkout_index)
429 + die(_("neither '%s' or '%s' is specified"),
430 + "--staged", "--worktree");
431 +
432 + if (!opts->checkout_worktree && !opts->from_treeish)
433 + die(_("'%s' must be used when '%s' is not specified"),
434 + "--worktree", "--source");
435 +
436 + if (opts->patch_mode) {
437 + const char *patch_mode;
438 +
439 + if (opts->checkout_index && opts->checkout_worktree)
440 + patch_mode = "--patch=checkout";
441 + else if (opts->checkout_index && !opts->checkout_worktree)
442 + patch_mode = "--patch=reset";
443 + else
444 + die(_("'%s' with only '%s' is not currently supported"),
445 + "--patch", "--worktree");
446 + return run_add_interactive(revision, patch_mode, &opts->pathspec);
447 + }
448
449 repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
450 if (read_cache_preload(&opts->pathspec) < 0)
@@ -482,10 +502,30 @@ static int checkout_paths(const struct checkout_opts *opts,
502 return 1;
503
504 /* Now we are committed to check them out */
485 - errs |= checkout_worktree(opts);
505 + if (opts->checkout_worktree)
506 + errs |= checkout_worktree(opts);
507
487 - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
488 - die(_("unable to write new index file"));
508 + /*
509 + * Allow updating the index when checking out from the index.
510 + * This is to save new stat info.
511 + */
512 + if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree)
513 + checkout_index = 1;
514 + else
515 + checkout_index = opts->checkout_index;
516 +
517 + if (checkout_index) {
518 + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
519 + die(_("unable to write new index file"));
520 + } else {
521 + /*
522 + * NEEDSWORK: if --worktree is not specified, we
523 + * should save stat info of checked out files in the
524 + * index to avoid the next (potentially costly)
525 + * refresh. But it's a bit tricker to do...
526 + */
527 + rollback_lock_file(&lock_file);
528 + }
529
530 read_ref_full("HEAD", 0, &rev, NULL);
531 head = lookup_commit_reference_gently(the_repository, &rev, 1);
@@ -1461,6 +1501,20 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
1501 if (opts->overlay_mode == 1 && opts->patch_mode)
1502 die(_("-p and --overlay are mutually exclusive"));
1503
1504 + if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
1505 + if (opts->checkout_index < 0)
1506 + opts->checkout_index = 0;
1507 + if (opts->checkout_worktree < 0)
1508 + opts->checkout_worktree = 0;
1509 + } else {
1510 + if (opts->checkout_index < 0)
1511 + opts->checkout_index = -opts->checkout_index - 1;
1512 + if (opts->checkout_worktree < 0)
1513 + opts->checkout_worktree = -opts->checkout_worktree - 1;
1514 + }
1515 + if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
1516 + BUG("these flags should be non-negative by now");
1517 +
1518 /*
1519 * From here on, new_branch will contain the branch to be checked out,
1520 * and new_branch_force and new_orphan_branch will tell us which one of
@@ -1617,6 +1671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
1671 opts.orphan_from_empty_tree = 0;
1672 opts.empty_pathspec_ok = 1;
1673 opts.overlay_mode = -1;
1674 + opts.checkout_index = -2; /* default on */
1675 + opts.checkout_worktree = -2; /* default on */
1676
1677 options = parse_options_dup(checkout_options);
1678 options = add_common_options(&opts, options);
@@ -1674,6 +1730,10 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
1730 struct option restore_options[] = {
1731 OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
1732 N_("where the checkout from")),
1733 + OPT_BOOL('S', "staged", &opts.checkout_index,
1734 + N_("restore the index")),
1735 + OPT_BOOL('W', "worktree", &opts.checkout_worktree,
1736 + N_("restore the working tree (default)")),
1737 OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
1738 OPT_END()
1739 };
@@ -1684,6 +1744,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
1744 opts.accept_pathspec = 1;
1745 opts.empty_pathspec_ok = 0;
1746 opts.overlay_mode = 0;
1747 + opts.checkout_index = -1; /* default off */
1748 + opts.checkout_worktree = -2; /* default on */
1749
1750 options = parse_options_dup(restore_options);
1751 options = add_common_options(&opts, options);