Skip to content

Commit 54f0bdc

Browse files
committed
Merge branch 'tr/reset-checkout-patch'
* 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'
2 parents 8e4384f + 3c2eb80 commit 54f0bdc

14 files changed

+674
-73
lines changed

Documentation/git-checkout.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ SYNOPSIS
1111
'git checkout' [-q] [-f] [-m] [<branch>]
1212
'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
1313
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
14+
'git checkout' --patch [<tree-ish>] [--] [<paths>...]
1415

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

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

119+
-p::
120+
--patch::
121+
Interactively select hunks in the difference between the
122+
<tree-ish> (or the index, if unspecified) and the working
123+
tree. The chosen hunks are then applied in reverse to the
124+
working tree (and if a <tree-ish> was specified, the index).
125+
+
126+
This means that you can use `git checkout -p` to selectively discard
127+
edits from your current working tree.
128+
118129
<branch>::
119130
Branch to checkout; if it refers to a branch (i.e., a name that,
120131
when prepended with "refs/heads/", is a valid ref), then that

Documentation/git-reset.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SYNOPSIS
1010
[verse]
1111
'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
1212
'git reset' [-q] [<commit>] [--] <paths>...
13+
'git reset' --patch [<commit>] [--] [<paths>...]
1314

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

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

2931

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

55+
-p::
56+
--patch::
57+
Interactively select hunks in the difference between the index
58+
and <commit> (defaults to HEAD). The chosen hunks are applied
59+
in reverse to the index.
60+
+
61+
This means that `git reset -p` is the opposite of `git add -p` (see
62+
linkgit:git-add[1]).
63+
5364
-q::
5465
Be quiet, only report errors.
5566

Documentation/git-stash.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ SYNOPSIS
1313
'git stash' drop [-q|--quiet] [<stash>]
1414
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
1515
'git stash' branch <branchname> [<stash>]
16-
'git stash' [save [--keep-index] [-q|--quiet] [<message>]]
16+
'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
1717
'git stash' clear
1818
'git stash' create
1919

@@ -42,15 +42,27 @@ is also possible).
4242
OPTIONS
4343
-------
4444

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

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

5567
list [<options>]::
5668

builtin-add.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,27 +131,27 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
131131
return pathspec;
132132
}
133133

134-
int interactive_add(int argc, const char **argv, const char *prefix)
134+
int run_add_interactive(const char *revision, const char *patch_mode,
135+
const char **pathspec)
135136
{
136-
int status, ac;
137+
int status, ac, pc = 0;
137138
const char **args;
138-
const char **pathspec = NULL;
139139

140-
if (argc) {
141-
pathspec = validate_pathspec(argc, argv, prefix);
142-
if (!pathspec)
143-
return -1;
144-
}
140+
if (pathspec)
141+
while (pathspec[pc])
142+
pc++;
145143

146-
args = xcalloc(sizeof(const char *), (argc + 4));
144+
args = xcalloc(sizeof(const char *), (pc + 5));
147145
ac = 0;
148146
args[ac++] = "add--interactive";
149-
if (patch_interactive)
150-
args[ac++] = "--patch";
147+
if (patch_mode)
148+
args[ac++] = patch_mode;
149+
if (revision)
150+
args[ac++] = revision;
151151
args[ac++] = "--";
152-
if (argc) {
153-
memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
154-
ac += argc;
152+
if (pc) {
153+
memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
154+
ac += pc;
155155
}
156156
args[ac] = NULL;
157157

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

163+
int interactive_add(int argc, const char **argv, const char *prefix)
164+
{
165+
const char **pathspec = NULL;
166+
167+
if (argc) {
168+
pathspec = validate_pathspec(argc, argv, prefix);
169+
if (!pathspec)
170+
return -1;
171+
}
172+
173+
return run_add_interactive(NULL,
174+
patch_interactive ? "--patch" : NULL,
175+
pathspec);
176+
}
177+
163178
static int edit_patch(int argc, const char **argv, const char *prefix)
164179
{
165180
char *file = xstrdup(git_path("ADD_EDIT.patch"));

builtin-checkout.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,13 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
566566
return git_xmerge_config(var, value, cb);
567567
}
568568

569+
static int interactive_checkout(const char *revision, const char **pathspec,
570+
struct checkout_opts *opts)
571+
{
572+
return run_add_interactive(revision, "--patch=checkout", pathspec);
573+
}
574+
575+
569576
int cmd_checkout(int argc, const char **argv, const char *prefix)
570577
{
571578
struct checkout_opts opts;
@@ -574,6 +581,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
574581
struct branch_info new;
575582
struct tree *source_tree = NULL;
576583
char *conflict_style = NULL;
584+
int patch_mode = 0;
577585
struct option options[] = {
578586
OPT__QUIET(&opts.quiet),
579587
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
@@ -588,6 +596,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
588596
OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
589597
OPT_STRING(0, "conflict", &conflict_style, "style",
590598
"conflict style (merge or diff3)"),
599+
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
591600
OPT_END(),
592601
};
593602
int has_dash_dash;
@@ -602,6 +611,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
602611
argc = parse_options(argc, argv, prefix, options, checkout_usage,
603612
PARSE_OPT_KEEP_DASHDASH);
604613

614+
if (patch_mode && (opts.track > 0 || opts.new_branch
615+
|| opts.new_branch_log || opts.merge || opts.force))
616+
die ("--patch is incompatible with all other options");
617+
605618
/* --track without -b should DWIM */
606619
if (0 < opts.track && !opts.new_branch) {
607620
const char *argv0 = argv[0];
@@ -708,6 +721,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
708721
if (!pathspec)
709722
die("invalid path specification");
710723

724+
if (patch_mode)
725+
return interactive_checkout(new.name, pathspec, &opts);
726+
711727
/* Checkout paths */
712728
if (opts.new_branch) {
713729
if (argc == 1) {
@@ -723,6 +739,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
723739
return checkout_paths(source_tree, pathspec, &opts);
724740
}
725741

742+
if (patch_mode)
743+
return interactive_checkout(new.name, NULL, &opts);
744+
726745
if (opts.new_branch) {
727746
struct strbuf buf = STRBUF_INIT;
728747
if (strbuf_check_branch_ref(&buf, opts.new_branch))

builtin-reset.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,17 @@ static void update_index_from_diff(struct diff_queue_struct *q,
143143
}
144144
}
145145

146+
static int interactive_reset(const char *revision, const char **argv,
147+
const char *prefix)
148+
{
149+
const char **pathspec = NULL;
150+
151+
if (*argv)
152+
pathspec = get_pathspec(prefix, argv);
153+
154+
return run_add_interactive(revision, "--patch=reset", pathspec);
155+
}
156+
146157
static int read_from_tree(const char *prefix, const char **argv,
147158
unsigned char *tree_sha1, int refresh_flags)
148159
{
@@ -184,6 +195,7 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
184195
int cmd_reset(int argc, const char **argv, const char *prefix)
185196
{
186197
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
198+
int patch_mode = 0;
187199
const char *rev = "HEAD";
188200
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
189201
*old_orig = NULL, sha1_old_orig[20];
@@ -199,6 +211,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
199211
"reset HEAD, index and working tree", MERGE),
200212
OPT_BOOLEAN('q', NULL, &quiet,
201213
"disable showing new HEAD in hard reset and progress message"),
214+
OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
202215
OPT_END()
203216
};
204217

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

268+
if (patch_mode) {
269+
if (reset_type != NONE)
270+
die("--patch is incompatible with --{hard,mixed,soft}");
271+
return interactive_reset(rev, argv + i, prefix);
272+
}
273+
255274
/* git reset tree [--] paths... can be used to
256275
* load chosen paths from the tree into the index without
257276
* affecting the working tree nor HEAD. */

commit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ int is_descendant_of(struct commit *, struct commit_list *);
140140
int in_merge_bases(struct commit *, struct commit **, int);
141141

142142
extern int interactive_add(int argc, const char **argv, const char *prefix);
143+
extern int run_add_interactive(const char *revision, const char *patch_mode,
144+
const char **pathspec);
143145

144146
static inline int single_parent(struct commit *commit)
145147
{

0 commit comments

Comments
 (0)