add-patch: add diff.context command line overrides
This patch compliments the previous commit, where builtins that use add-patch infrastructure now respect diff.context and diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics commands such as diff and log, which allow for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Leon Michalak committed
Jul 29, 2025 at 07:01 UTC
2b3ae04011c3d679ba601c7ef9e20b9dec125ebb
19 files changed
+241
-35
Documentation/diff-context-options.adoc
new
+10
@@ -0,0 +1,10 @@
1
+`-U<n>`::
2
+`--unified=<n>`::
3
+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context`
4
+ or 3 if the config option is unset.
5
+
6
+`--inter-hunk-context=<n>`::
7
+ Show the context between diff hunks, up to the specified _<number>_
8
+ of lines, thereby fusing hunks that are close to each other.
9
+ Defaults to `diff.interHunkContext` or 0 if the config option
10
+ is unset.
Documentation/git-add.adoc
+2
@@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the
104
initial command menu and directly jumps to the `patch` subcommand.
105
See ``Interactive mode'' for details.
106
107
+include::diff-context-options.adoc[]
108
+
109
`-e`::
110
`--edit`::
111
Open the diff vs. the index in an editor and let the user
Documentation/git-checkout.adoc
+2
@@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
289
Note that this option uses the no overlay mode by default (see also
290
`--overlay`), and currently doesn't support overlay mode.
291
292
+include::diff-context-options.adoc[]
293
+
294
`--ignore-other-worktrees`::
295
`git checkout` refuses when the wanted branch is already checked
296
out or otherwise in use by another worktree. This option makes
Documentation/git-commit.adoc
+2
@@ -76,6 +76,8 @@ OPTIONS
76
which changes to commit. See linkgit:git-add[1] for
77
details.
78
79
+include::diff-context-options.adoc[]
80
+
81
`-C <commit>`::
82
`--reuse-message=<commit>`::
83
Take an existing _<commit>_ object, and reuse the log message
Documentation/git-reset.adoc
+2
@@ -125,6 +125,8 @@ OPTIONS
125
separated with _NUL_ character and all other characters are taken
126
literally (including newlines and quotes).
127
128
+include::diff-context-options.adoc[]
129
+
130
`--`::
131
Do not interpret any more arguments as options.
132
Documentation/git-restore.adoc
+2
@@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to
52
Mode" section of linkgit:git-add[1] to learn how to operate
53
the `--patch` mode.
54
55
+include::diff-context-options.adoc[]
56
+
57
`-W`::
58
`--worktree`::
59
`-S`::
Documentation/git-stash.adoc
+2
@@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode.
208
The `--patch` option implies `--keep-index`. You can use
209
`--no-keep-index` to override this.
210
211
+include::diff-context-options.adoc[]
212
+
213
-S::
214
--staged::
215
This option is only valid for `push` and `save` commands.
add-interactive.c
+28
-8
@@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s,
36
free(key);
37
}
38
39
-void init_add_i_state(struct add_i_state *s, struct repository *r)
39
+void init_add_i_state(struct add_i_state *s, struct repository *r,
40
+ struct add_p_opt *add_p_opt)
41
{
42
const char *value;
43
@@ -90,6 +91,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
91
repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key);
92
if (s->use_single_key)
93
setbuf(stdin, NULL);
94
+
95
+ if (add_p_opt->context != -1) {
96
+ if (add_p_opt->context < 0)
97
+ die(_("%s cannot be negative"), "--unified");
98
+ s->context = add_p_opt->context;
99
+ }
100
+ if (add_p_opt->interhunkcontext != -1) {
101
+ if (add_p_opt->interhunkcontext < 0)
102
+ die(_("%s cannot be negative"), "--inter-hunk-context");
103
+ s->interhunkcontext = add_p_opt->interhunkcontext;
104
+ }
105
}
106
107
void clear_add_i_state(struct add_i_state *s)
@@ -978,6 +990,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
990
opts->prompt = N_("Patch update");
991
count = list_and_choose(s, files, opts);
992
if (count > 0) {
993
+ struct add_p_opt add_p_opt = {
994
+ .context = s->context,
995
+ .interhunkcontext = s->interhunkcontext,
996
+ };
997
struct strvec args = STRVEC_INIT;
998
struct pathspec ps_selected = { 0 };
999
@@ -988,7 +1004,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
1004
parse_pathspec(&ps_selected,
1005
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
1006
PATHSPEC_LITERAL_PATH, "", args.v);
991
- res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
1007
+ res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected);
1008
strvec_clear(&args);
1009
clear_pathspec(&ps_selected);
1010
}
@@ -1023,10 +1039,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
1039
if (count > 0) {
1040
struct child_process cmd = CHILD_PROCESS_INIT;
1041
1026
- strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached",
1027
- oid_to_hex(!is_initial ? &oid :
1028
- s->r->hash_algo->empty_tree),
1029
- "--", NULL);
1042
+ strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL);
1043
+ if (s->context != -1)
1044
+ strvec_pushf(&cmd.args, "--unified=%i", s->context);
1045
+ if (s->interhunkcontext != -1)
1046
+ strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext);
1047
+ strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid :
1048
+ s->r->hash_algo->empty_tree), "--", NULL);
1049
for (i = 0; i < files->items.nr; i++)
1050
if (files->selected[i])
1051
strvec_push(&cmd.args,
@@ -1119,7 +1138,8 @@ static void command_prompt_help(struct add_i_state *s)
1138
_("(empty) select nothing"));
1139
}
1140
1122
-int run_add_i(struct repository *r, const struct pathspec *ps)
1141
+int run_add_i(struct repository *r, const struct pathspec *ps,
1142
+ struct add_p_opt *add_p_opt)
1143
{
1144
struct add_i_state s = { NULL };
1145
struct print_command_item_data data = { "[", "]" };
@@ -1162,7 +1182,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
1182
->util = util;
1183
}
1184
1165
- init_add_i_state(&s, r);
1185
+ init_add_i_state(&s, r, add_p_opt);
1186
1187
/*
1188
* When color was asked for, use the prompt color for
add-interactive.h
+13
-3
@@ -3,6 +3,13 @@
3
4
#include "color.h"
5
6
+struct add_p_opt {
7
+ int context;
8
+ int interhunkcontext;
9
+};
10
+
11
+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }
12
+
13
struct add_i_state {
14
struct repository *r;
15
int use_color;
@@ -21,12 +28,14 @@ struct add_i_state {
28
int context, interhunkcontext;
29
};
30
24
-void init_add_i_state(struct add_i_state *s, struct repository *r);
31
+void init_add_i_state(struct add_i_state *s, struct repository *r,
32
+ struct add_p_opt *add_p_opt);
33
void clear_add_i_state(struct add_i_state *s);
34
35
struct repository;
36
struct pathspec;
29
-int run_add_i(struct repository *r, const struct pathspec *ps);
37
+int run_add_i(struct repository *r, const struct pathspec *ps,
38
+ struct add_p_opt *add_p_opt);
39
40
enum add_p_mode {
41
ADD_P_ADD,
@@ -37,6 +46,7 @@ enum add_p_mode {
46
};
47
48
int run_add_p(struct repository *r, enum add_p_mode mode,
40
- const char *revision, const struct pathspec *ps);
49
+ struct add_p_opt *o, const char *revision,
50
+ const struct pathspec *ps);
51
52
#endif
add-patch.c
+3
-2
@@ -1763,14 +1763,15 @@ soft_increment:
1763
}
1764
1765
int run_add_p(struct repository *r, enum add_p_mode mode,
1766
- const char *revision, const struct pathspec *ps)
1766
+ struct add_p_opt *o, const char *revision,
1767
+ const struct pathspec *ps)
1768
{
1769
struct add_p_state s = {
1770
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
1771
};
1772
size_t i, binary_count = 0;
1773
1773
- init_add_i_state(&s.s, r);
1774
+ init_add_i_state(&s.s, r, o);
1775
1776
if (mode == ADD_P_STASH)
1777
s.mode = &patch_mode_stash;
builtin/add.c
+17
-4
@@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = {
29
NULL
30
};
31
static int patch_interactive, add_interactive, edit_interactive;
32
+static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
33
static int take_worktree_changes;
34
static int add_renormalize;
35
static int pathspec_file_nul;
@@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec *
158
int interactive_add(struct repository *repo,
159
const char **argv,
160
const char *prefix,
160
- int patch)
161
+ int patch, struct add_p_opt *add_p_opt)
162
{
163
struct pathspec pathspec;
164
int ret;
@@ -169,9 +170,9 @@ int interactive_add(struct repository *repo,
170
prefix, argv);
171
172
if (patch)
172
- ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec);
173
+ ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec);
174
else
174
- ret = !!run_add_i(repo, &pathspec);
175
+ ret = !!run_add_i(repo, &pathspec, add_p_opt);
176
177
clear_pathspec(&pathspec);
178
return ret;
@@ -253,6 +254,8 @@ static struct option builtin_add_options[] = {
254
OPT_GROUP(""),
255
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
256
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
257
+ OPT_DIFF_UNIFIED(&add_p_opt.context),
258
+ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
259
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
260
OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
261
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
@@ -394,6 +397,11 @@ int cmd_add(int argc,
397
prepare_repo_settings(repo);
398
repo->settings.command_requires_full_index = 0;
399
400
+ if (add_p_opt.context < -1)
401
+ die(_("'%s' cannot be negative"), "--unified");
402
+ if (add_p_opt.interhunkcontext < -1)
403
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
404
+
405
if (patch_interactive)
406
add_interactive = 1;
407
if (add_interactive) {
@@ -401,7 +409,12 @@ int cmd_add(int argc,
409
die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
410
if (pathspec_from_file)
411
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
404
- exit(interactive_add(repo, argv + 1, prefix, patch_interactive));
412
+ exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt));
413
+ } else {
414
+ if (add_p_opt.context != -1)
415
+ die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
416
+ if (add_p_opt.interhunkcontext != -1)
417
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
418
}
419
420
if (edit_interactive) {
builtin/checkout.c
+28
-3
@@ -61,6 +61,8 @@ static const char * const restore_usage[] = {
61
62
struct checkout_opts {
63
int patch_mode;
64
+ int patch_context;
65
+ int patch_interhunk_context;
66
int quiet;
67
int merge;
68
int force;
@@ -104,7 +106,12 @@ struct checkout_opts {
106
struct tree *source_tree;
107
};
108
107
-#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
109
+#define CHECKOUT_OPTS_INIT { \
110
+ .conflict_style = -1, \
111
+ .merge = -1, \
112
+ .patch_context = -1, \
113
+ .patch_interhunk_context = -1, \
114
+}
115
116
struct branch_info {
117
char *name; /* The short name used */
@@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts,
546
547
if (opts->patch_mode) {
548
enum add_p_mode patch_mode;
549
+ struct add_p_opt add_p_opt = {
550
+ .context = opts->patch_context,
551
+ .interhunkcontext = opts->patch_interhunk_context,
552
+ };
553
const char *rev = new_branch_info->name;
554
char rev_oid[GIT_MAX_HEXSZ + 1];
555
@@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts,
575
else
576
BUG("either flag must have been set, worktree=%d, index=%d",
577
opts->checkout_worktree, opts->checkout_index);
567
- return !!run_add_p(the_repository, patch_mode, rev,
568
- &opts->pathspec);
578
+ return !!run_add_p(the_repository, patch_mode, &add_p_opt,
579
+ rev, &opts->pathspec);
580
}
581
582
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -1738,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
1749
N_("checkout their version for unmerged files"),
1750
3, PARSE_OPT_NONEG),
1751
OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
1752
+ OPT_DIFF_UNIFIED(&opts->patch_context),
1753
+ OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context),
1754
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
1755
N_("do not limit pathspecs to sparse entries only")),
1756
OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
@@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
1793
argc = parse_options(argc, argv, prefix, options,
1794
usagestr, parseopt_flags);
1795
1796
+ if (opts->patch_context < -1)
1797
+ die(_("'%s' cannot be negative"), "--unified");
1798
+ if (opts->patch_interhunk_context < -1)
1799
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
1800
+
1801
+ if (!opts->patch_mode) {
1802
+ if (opts->patch_context != -1)
1803
+ die(_("the option '%s' requires '%s'"), "--unified", "--patch");
1804
+ if (opts->patch_interhunk_context != -1)
1805
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1806
+ }
1807
+
1808
if (opts->show_progress < 0) {
1809
if (opts->quiet)
1810
opts->show_progress = 0;
builtin/commit.c
+15
-1
@@ -19,6 +19,7 @@
19
#include "environment.h"
20
#include "diff.h"
21
#include "commit.h"
22
+#include "add-interactive.h"
23
#include "gettext.h"
24
#include "revision.h"
25
#include "wt-status.h"
@@ -122,6 +123,7 @@ static const char *edit_message, *use_message;
123
static char *fixup_message, *fixup_commit, *squash_message;
124
static const char *fixup_prefix;
125
static int all, also, interactive, patch_interactive, only, amend, signoff;
126
+static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
127
static int edit_flag = -1; /* unspecified */
128
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
129
static int config_commit_verbose = -1; /* unspecified */
@@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix,
356
const char *ret;
357
char *path = NULL;
358
359
+ if (add_p_opt.context < -1)
360
+ die(_("'%s' cannot be negative"), "--unified");
361
+ if (add_p_opt.interhunkcontext < -1)
362
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
363
+
364
if (is_status)
365
refresh_flags |= REFRESH_UNMERGED;
366
parse_pathspec(&pathspec, 0,
@@ -400,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
407
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
408
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
409
403
- if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0)
410
+ if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0)
411
die(_("interactive add failed"));
412
413
the_repository->index_file = old_repo_index_file;
@@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix,
431
commit_style = COMMIT_NORMAL;
432
ret = get_lock_file_path(&index_lock);
433
goto out;
434
+ } else {
435
+ if (add_p_opt.context != -1)
436
+ die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
437
+ if (add_p_opt.interhunkcontext != -1)
438
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
439
}
440
441
/*
@@ -1722,6 +1734,8 @@ int cmd_commit(int argc,
1734
OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
1735
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
1736
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
1737
+ OPT_DIFF_UNIFIED(&add_p_opt.context),
1738
+ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
1739
OPT_BOOL('o', "only", &only, N_("commit only specified files")),
1740
OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
1741
OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
builtin/reset.c
+15
-2
@@ -346,6 +346,7 @@ int cmd_reset(int argc,
346
struct object_id oid;
347
struct pathspec pathspec;
348
int intent_to_add = 0;
349
+ struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
350
const struct option options[] = {
351
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
352
OPT_BOOL(0, "no-refresh", &no_refresh,
@@ -370,6 +371,8 @@ int cmd_reset(int argc,
371
PARSE_OPT_OPTARG,
372
option_parse_recurse_submodules_worktree_updater),
373
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
374
+ OPT_DIFF_UNIFIED(&add_p_opt.context),
375
+ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
376
OPT_BOOL('N', "intent-to-add", &intent_to_add,
377
N_("record only the fact that removed paths will be added later")),
378
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
@@ -420,6 +423,11 @@ int cmd_reset(int argc,
423
oidcpy(&oid, &tree->object.oid);
424
}
425
426
+ if (add_p_opt.context < -1)
427
+ die(_("'%s' cannot be negative"), "--unified");
428
+ if (add_p_opt.interhunkcontext < -1)
429
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
430
+
431
prepare_repo_settings(the_repository);
432
the_repository->settings.command_requires_full_index = 0;
433
@@ -427,9 +435,14 @@ int cmd_reset(int argc,
435
if (reset_type != NONE)
436
die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
437
trace2_cmd_mode("patch-interactive");
430
- update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
431
- &pathspec);
438
+ update_ref_status = !!run_add_p(the_repository, ADD_P_RESET,
439
+ &add_p_opt, rev, &pathspec);
440
goto cleanup;
441
+ } else {
442
+ if (add_p_opt.context != -1)
443
+ die(_("the option '%s' requires '%s'"), "--unified", "--patch");
444
+ if (add_p_opt.interhunkcontext != -1)
445
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
446
}
447
448
/* git reset tree [--] paths... can be used to
builtin/stash.c
+45
-11
@@ -1242,7 +1242,8 @@ done:
1242
}
1243
1244
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
1245
- struct strbuf *out_patch, int quiet)
1245
+ struct strbuf *out_patch, int quiet,
1246
+ struct add_p_opt *add_p_opt)
1247
{
1248
int ret = 0;
1249
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
@@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
1268
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
1269
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
1270
1270
- ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
1271
+ ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps);
1272
1273
the_repository->index_file = old_repo_index_file;
1274
if (old_index_env && *old_index_env)
@@ -1362,8 +1363,8 @@ done:
1363
}
1364
1365
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
1365
- int include_untracked, int patch_mode, int only_staged,
1366
- struct stash_info *info, struct strbuf *patch,
1366
+ int include_untracked, int patch_mode, struct add_p_opt *add_p_opt,
1367
+ int only_staged, struct stash_info *info, struct strbuf *patch,
1368
int quiet)
1369
{
1370
int ret = 0;
@@ -1439,7 +1440,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
1440
untracked_commit_option = 1;
1441
}
1442
if (patch_mode) {
1442
- ret = stash_patch(info, ps, patch, quiet);
1443
+ ret = stash_patch(info, ps, patch, quiet, add_p_opt);
1444
if (ret < 0) {
1445
if (!quiet)
1446
fprintf_ln(stderr, _("Cannot save the current "
@@ -1513,7 +1514,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
1514
if (!check_changes_tracked_files(&ps))
1515
return 0;
1516
1516
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
1517
+ ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
1518
NULL, 0);
1519
if (!ret)
1520
printf_ln("%s", oid_to_hex(&info.w_commit));
@@ -1524,7 +1525,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
1525
}
1526
1527
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
1527
- int keep_index, int patch_mode, int include_untracked, int only_staged)
1528
+ int keep_index, int patch_mode, struct add_p_opt *add_p_opt,
1529
+ int include_untracked, int only_staged)
1530
{
1531
int ret = 0;
1532
struct stash_info info = STASH_INFO_INIT;
@@ -1594,8 +1596,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
1596
1597
if (stash_msg)
1598
strbuf_addstr(&stash_msg_buf, stash_msg);
1597
- if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
1598
- &info, &patch, quiet)) {
1599
+ if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
1600
+ add_p_opt, only_staged, &info, &patch, quiet)) {
1601
ret = -1;
1602
goto done;
1603
}
@@ -1768,6 +1770,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1770
const char *stash_msg = NULL;
1771
char *pathspec_from_file = NULL;
1772
struct pathspec ps;
1773
+ struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
1774
struct option options[] = {
1775
OPT_BOOL('k', "keep-index", &keep_index,
1776
N_("keep index")),
@@ -1775,6 +1778,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1778
N_("stash staged changes only")),
1779
OPT_BOOL('p', "patch", &patch_mode,
1780
N_("stash in patch mode")),
1781
+ OPT_DIFF_UNIFIED(&add_p_opt.context),
1782
+ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
1783
OPT__QUIET(&quiet, N_("quiet mode")),
1784
OPT_BOOL('u', "include-untracked", &include_untracked,
1785
N_("include untracked files in stash")),
@@ -1826,8 +1831,20 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1831
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
1832
}
1833
1834
+ if (!patch_mode) {
1835
+ if (add_p_opt.context != -1)
1836
+ die(_("the option '%s' requires '%s'"), "--unified", "--patch");
1837
+ if (add_p_opt.interhunkcontext != -1)
1838
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1839
+ }
1840
+
1841
+ if (add_p_opt.context < -1)
1842
+ die(_("'%s' cannot be negative"), "--unified");
1843
+ if (add_p_opt.interhunkcontext < -1)
1844
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
1845
+
1846
ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
1830
- include_untracked, only_staged);
1847
+ &add_p_opt, include_untracked, only_staged);
1848
1849
clear_pathspec(&ps);
1850
free(pathspec_from_file);
@@ -1852,6 +1869,7 @@ static int save_stash(int argc, const char **argv, const char *prefix,
1869
const char *stash_msg = NULL;
1870
struct pathspec ps;
1871
struct strbuf stash_msg_buf = STRBUF_INIT;
1872
+ struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
1873
struct option options[] = {
1874
OPT_BOOL('k', "keep-index", &keep_index,
1875
N_("keep index")),
@@ -1859,6 +1877,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
1877
N_("stash staged changes only")),
1878
OPT_BOOL('p', "patch", &patch_mode,
1879
N_("stash in patch mode")),
1880
+ OPT_DIFF_UNIFIED(&add_p_opt.context),
1881
+ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
1882
OPT__QUIET(&quiet, N_("quiet mode")),
1883
OPT_BOOL('u', "include-untracked", &include_untracked,
1884
N_("include untracked files in stash")),
@@ -1877,8 +1897,22 @@ static int save_stash(int argc, const char **argv, const char *prefix,
1897
stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
1898
1899
memset(&ps, 0, sizeof(ps));
1900
+
1901
+ if (add_p_opt.context < -1)
1902
+ die(_("'%s' cannot be negative"), "--unified");
1903
+ if (add_p_opt.interhunkcontext < -1)
1904
+ die(_("'%s' cannot be negative"), "--inter-hunk-context");
1905
+
1906
+ if (!patch_mode) {
1907
+ if (add_p_opt.context != -1)
1908
+ die(_("the option '%s' requires '%s'"), "--unified", "--patch");
1909
+ if (add_p_opt.interhunkcontext != -1)
1910
+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
1911
+ }
1912
+
1913
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
1881
- patch_mode, include_untracked, only_staged);
1914
+ patch_mode, &add_p_opt, include_untracked,
1915
+ only_staged);
1916
1917
strbuf_release(&stash_msg_buf);
1918
return ret;
commit.h
+2
-1
@@ -2,6 +2,7 @@
2
#define COMMIT_H
3
4
#include "object.h"
5
+#include "add-interactive.h"
6
7
struct signature_check;
8
struct strbuf;
@@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *);
258
int interactive_add(struct repository *repo,
259
const char **argv,
260
const char *prefix,
260
- int patch);
261
+ int patch, struct add_p_opt *add_p_opt);
262
263
struct commit_extra_header {
264
struct commit_extra_header *next;
parse-options.h
+2
@@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
616
#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
617
#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
618
#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
619
+#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG)
620
+#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG)
621
622
#define OPT_IPVERSION(v) \
623
OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \
t/t3701-add-interactive.sh
+49
@@ -1252,4 +1252,53 @@ test_expect_success 'add -p rejects negative diff.context' '
1252
test_grep "diff.context cannot be negative" output
1253
'
1254
1255
+for cmd in add checkout restore 'commit -m file'
1256
+do
1257
+ test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" '
1258
+ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file &&
1259
+ git add file &&
1260
+ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file &&
1261
+ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
1262
+ $cmd -p -U 4 --inter-hunk-context 2 >actual &&
1263
+ test_grep "@@ -2,20 +2,20 @@" actual
1264
+ '
1265
+done
1266
+
1267
+test_expect_success 'reset accepts -U and --inter-hunk-context' '
1268
+ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file &&
1269
+ git commit -m file file &&
1270
+ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file &&
1271
+ git add file &&
1272
+ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
1273
+ reset -p -U 4 --inter-hunk-context 2 >actual &&
1274
+ test_grep "@@ -2,20 +2,20 @@" actual
1275
+'
1276
+
1277
+test_expect_success 'stash accepts -U and --inter-hunk-context' '
1278
+ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file &&
1279
+ git commit -m file file &&
1280
+ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file &&
1281
+ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
1282
+ stash -p -U 4 --inter-hunk-context 2 >actual &&
1283
+ test_grep "@@ -2,20 +2,20 @@" actual
1284
+'
1285
+
1286
+for cmd in add checkout commit reset restore "stash save" "stash push"
1287
+do
1288
+ test_expect_success "$cmd rejects invalid context options" '
1289
+ test_must_fail git $cmd -p -U -3 2>actual &&
1290
+ cat actual | echo &&
1291
+ test_grep -e ".--unified. cannot be negative" actual &&
1292
+
1293
+ test_must_fail git $cmd -p --inter-hunk-context -3 2>actual &&
1294
+ test_grep -e ".--inter-hunk-context. cannot be negative" actual &&
1295
+
1296
+ test_must_fail git $cmd -U 7 2>actual &&
1297
+ test_grep -E ".--unified. requires .(--interactive/)?--patch." actual &&
1298
+
1299
+ test_must_fail git $cmd --inter-hunk-context 2 2>actual &&
1300
+ test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual
1301
+ '
1302
+done
1303
+
1304
test_done
t/t9902-completion.sh
+2
@@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' '
2596
--merge Z
2597
--conflict=Z
2598
--patch Z
2599
+ --unified=Z
2600
+ --inter-hunk-context=Z
2601
--ignore-skip-worktree-bits Z
2602
--ignore-other-worktrees Z
2603
--recurse-submodules Z