Skip to content

Commit

Permalink
Merge branch 'tr/reset-checkout-patch'
Browse files Browse the repository at this point in the history
* tr/reset-checkout-patch:
  stash: simplify defaulting to "save" and reject unknown options
  Make test case number unique
  tests: disable interactive hunk selection tests if perl is not available
  DWIM 'git stash save -p' for 'git stash -p'
  Implement 'git stash save --patch'
  Implement 'git checkout --patch'
  Implement 'git reset --patch'
  builtin-add: refactor the meat of interactive_add()
  Add a small patch-mode testing library
  git-apply--interactive: Refactor patch mode code
  Make 'git stash -k' a short form for 'git stash save --keep-index'
  • Loading branch information
Junio C Hamano committed Sep 7, 2009
2 parents 8e4384f + 3c2eb80 commit 54f0bdc
Show file tree
Hide file tree
Showing 14 changed files with 674 additions and 73 deletions.
13 changes: 12 additions & 1 deletion Documentation/git-checkout.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SYNOPSIS
'git checkout' [-q] [-f] [-m] [<branch>]
'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
'git checkout' --patch [<tree-ish>] [--] [<paths>...]

DESCRIPTION
-----------
Expand All @@ -25,7 +26,7 @@ use the --track or --no-track options, which will be passed to `git
branch`. As a convenience, --track without `-b` implies branch
creation; see the description of --track below.

When <paths> are given, this command does *not* switch
When <paths> or --patch are given, this command does *not* switch
branches. It updates the named paths in the working tree from
the index file, or from a named <tree-ish> (most often a commit). In
this case, the `-b` and `--track` options are meaningless and giving
Expand Down Expand Up @@ -115,6 +116,16 @@ the conflicted merge in the specified paths.
"merge" (default) and "diff3" (in addition to what is shown by
"merge" style, shows the original contents).

-p::
--patch::
Interactively select hunks in the difference between the
<tree-ish> (or the index, if unspecified) and the working
tree. The chosen hunks are then applied in reverse to the
working tree (and if a <tree-ish> was specified, the index).
+
This means that you can use `git checkout -p` to selectively discard
edits from your current working tree.

<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
Expand Down
15 changes: 13 additions & 2 deletions Documentation/git-reset.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
'git reset' --patch [<commit>] [--] [<paths>...]

DESCRIPTION
-----------
Expand All @@ -23,8 +24,9 @@ the undo in the history.
If you want to undo a commit other than the latest on a branch,
linkgit:git-revert[1] is your friend.

The second form with 'paths' is used to revert selected paths in
the index from a given commit, without moving HEAD.
The second and third forms with 'paths' and/or --patch are used to
revert selected paths in the index from a given commit, without moving
HEAD.


OPTIONS
Expand All @@ -50,6 +52,15 @@ OPTIONS
and updates the files that are different between the named commit
and the current commit in the working tree.

-p::
--patch::
Interactively select hunks in the difference between the index
and <commit> (defaults to HEAD). The chosen hunks are applied
in reverse to the index.
+
This means that `git reset -p` is the opposite of `git add -p` (see
linkgit:git-add[1]).

-q::
Be quiet, only report errors.

Expand Down
22 changes: 17 additions & 5 deletions Documentation/git-stash.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SYNOPSIS
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
'git stash' [save [--keep-index] [-q|--quiet] [<message>]]
'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
'git stash' clear
'git stash' create

Expand Down Expand Up @@ -42,15 +42,27 @@ is also possible).
OPTIONS
-------

save [--keep-index] [-q|--quiet] [<message>]::
save [--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::

Save your local modifications to a new 'stash', and run `git reset
--hard` to revert them. This is the default action when no
subcommand is given. The <message> part is optional and gives
the description along with the stashed state.
--hard` to revert them. The <message> part is optional and gives
the description along with the stashed state. For quickly making
a snapshot, you can omit _both_ "save" and <message>, but giving
only <message> does not trigger this action to prevent a misspelled
subcommand from making an unwanted stash.
+
If the `--keep-index` option is used, all changes already added to the
index are left intact.
+
With `--patch`, you can interactively select hunks from in the diff
between HEAD and the working tree to be stashed. The stash entry is
constructed such that its index state is the same as the index state
of your repository, and its worktree contains only the changes you
selected interactively. The selected changes are then rolled back
from your worktree.
+
The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this.

list [<options>]::

Expand Down
43 changes: 29 additions & 14 deletions builtin-add.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,27 +131,27 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
return pathspec;
}

int interactive_add(int argc, const char **argv, const char *prefix)
int run_add_interactive(const char *revision, const char *patch_mode,
const char **pathspec)
{
int status, ac;
int status, ac, pc = 0;
const char **args;
const char **pathspec = NULL;

if (argc) {
pathspec = validate_pathspec(argc, argv, prefix);
if (!pathspec)
return -1;
}
if (pathspec)
while (pathspec[pc])
pc++;

args = xcalloc(sizeof(const char *), (argc + 4));
args = xcalloc(sizeof(const char *), (pc + 5));
ac = 0;
args[ac++] = "add--interactive";
if (patch_interactive)
args[ac++] = "--patch";
if (patch_mode)
args[ac++] = patch_mode;
if (revision)
args[ac++] = revision;
args[ac++] = "--";
if (argc) {
memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
ac += argc;
if (pc) {
memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
ac += pc;
}
args[ac] = NULL;

Expand All @@ -160,6 +160,21 @@ int interactive_add(int argc, const char **argv, const char *prefix)
return status;
}

int interactive_add(int argc, const char **argv, const char *prefix)
{
const char **pathspec = NULL;

if (argc) {
pathspec = validate_pathspec(argc, argv, prefix);
if (!pathspec)
return -1;
}

return run_add_interactive(NULL,
patch_interactive ? "--patch" : NULL,
pathspec);
}

static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
Expand Down
19 changes: 19 additions & 0 deletions builtin-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,13 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return git_xmerge_config(var, value, cb);
}

static int interactive_checkout(const char *revision, const char **pathspec,
struct checkout_opts *opts)
{
return run_add_interactive(revision, "--patch=checkout", pathspec);
}


int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
Expand All @@ -574,6 +581,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
struct branch_info new;
struct tree *source_tree = NULL;
char *conflict_style = NULL;
int patch_mode = 0;
struct option options[] = {
OPT__QUIET(&opts.quiet),
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
Expand All @@ -588,6 +596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
OPT_STRING(0, "conflict", &conflict_style, "style",
"conflict style (merge or diff3)"),
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END(),
};
int has_dash_dash;
Expand All @@ -602,6 +611,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);

if (patch_mode && (opts.track > 0 || opts.new_branch
|| opts.new_branch_log || opts.merge || opts.force))
die ("--patch is incompatible with all other options");

/* --track without -b should DWIM */
if (0 < opts.track && !opts.new_branch) {
const char *argv0 = argv[0];
Expand Down Expand Up @@ -708,6 +721,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
if (!pathspec)
die("invalid path specification");

if (patch_mode)
return interactive_checkout(new.name, pathspec, &opts);

/* Checkout paths */
if (opts.new_branch) {
if (argc == 1) {
Expand All @@ -723,6 +739,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
return checkout_paths(source_tree, pathspec, &opts);
}

if (patch_mode)
return interactive_checkout(new.name, NULL, &opts);

if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
if (strbuf_check_branch_ref(&buf, opts.new_branch))
Expand Down
19 changes: 19 additions & 0 deletions builtin-reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ static void update_index_from_diff(struct diff_queue_struct *q,
}
}

static int interactive_reset(const char *revision, const char **argv,
const char *prefix)
{
const char **pathspec = NULL;

if (*argv)
pathspec = get_pathspec(prefix, argv);

return run_add_interactive(revision, "--patch=reset", pathspec);
}

static int read_from_tree(const char *prefix, const char **argv,
unsigned char *tree_sha1, int refresh_flags)
{
Expand Down Expand Up @@ -184,6 +195,7 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
int patch_mode = 0;
const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
Expand All @@ -199,6 +211,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
"reset HEAD, index and working tree", MERGE),
OPT_BOOLEAN('q', NULL, &quiet,
"disable showing new HEAD in hard reset and progress message"),
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END()
};

Expand Down Expand Up @@ -252,6 +265,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die("Could not parse object '%s'.", rev);
hashcpy(sha1, commit->object.sha1);

if (patch_mode) {
if (reset_type != NONE)
die("--patch is incompatible with --{hard,mixed,soft}");
return interactive_reset(rev, argv + i, prefix);
}

/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
* affecting the working tree nor HEAD. */
Expand Down
2 changes: 2 additions & 0 deletions commit.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ int is_descendant_of(struct commit *, struct commit_list *);
int in_merge_bases(struct commit *, struct commit **, int);

extern int interactive_add(int argc, const char **argv, const char *prefix);
extern int run_add_interactive(const char *revision, const char *patch_mode,
const char **pathspec);

static inline int single_parent(struct commit *commit)
{
Expand Down
Loading

0 comments on commit 54f0bdc

Please sign in to comment.