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