]> git.ipfire.org Git - thirdparty/git.git/commitdiff
add-patch: add diff.context command line overrides
authorLeon Michalak <leonmichalak6@gmail.com>
Tue, 29 Jul 2025 07:01:51 +0000 (07:01 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Jul 2025 15:52:45 +0000 (08:52 -0700)
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>
19 files changed:
Documentation/diff-context-options.adoc [new file with mode: 0644]
Documentation/git-add.adoc
Documentation/git-checkout.adoc
Documentation/git-commit.adoc
Documentation/git-reset.adoc
Documentation/git-restore.adoc
Documentation/git-stash.adoc
add-interactive.c
add-interactive.h
add-patch.c
builtin/add.c
builtin/checkout.c
builtin/commit.c
builtin/reset.c
builtin/stash.c
commit.h
parse-options.h
t/t3701-add-interactive.sh
t/t9902-completion.sh

diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc
new file mode 100644 (file)
index 0000000..e161260
--- /dev/null
@@ -0,0 +1,10 @@
+`-U<n>`::
+`--unified=<n>`::
+       Generate diffs with _<n>_ lines of context. Defaults to `diff.context`
+       or 3 if the config option is unset.
+
+`--inter-hunk-context=<n>`::
+       Show the context between diff hunks, up to the specified _<number>_
+       of lines, thereby fusing hunks that are close to each other.
+       Defaults to `diff.interHunkContext` or 0 if the config option
+       is unset.
index eba0b419ce508f5d01438ac5bd285ce4cb3c12e2..b7a735824d6ce07c6438f190c5c7c1ab1395c478 100644 (file)
@@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the
 initial command menu and directly jumps to the `patch` subcommand.
 See ``Interactive mode'' for details.
 
+include::diff-context-options.adoc[]
+
 `-e`::
 `--edit`::
        Open the diff vs. the index in an editor and let the user
index ee83b6d9ba9a9b061ecf0b75bf5733c5bd75ca6a..40e02cfd6562ae939ce3ffd7d8454a2430cf7822 100644 (file)
@@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 Note that this option uses the no overlay mode by default (see also
 `--overlay`), and currently doesn't support overlay mode.
 
+include::diff-context-options.adoc[]
+
 `--ignore-other-worktrees`::
        `git checkout` refuses when the wanted branch is already checked
        out or otherwise in use by another worktree. This option makes
index dc219025f1eb0b7b8d12adb5cc977747242edd7a..ae988a883b5b86f79a0c3b5e125f6d392bb96e2b 100644 (file)
@@ -76,6 +76,8 @@ OPTIONS
        which changes to commit. See linkgit:git-add[1] for
        details.
 
+include::diff-context-options.adoc[]
+
 `-C <commit>`::
 `--reuse-message=<commit>`::
        Take an existing _<commit>_ object, and reuse the log message
index 53ab88c5451c44dbe3af650678fe5bf20f4c9c6b..50e8a0ba6f6612d7a77e73603ec6111bc38e37fe 100644 (file)
@@ -125,6 +125,8 @@ OPTIONS
        separated with _NUL_ character and all other characters are taken
        literally (including newlines and quotes).
 
+include::diff-context-options.adoc[]
+
 `--`::
        Do not interpret any more arguments as options.
 
index 877b7772e66735fc26bf26f7ef00503563b07855..1dcc2bb7aea3875d10d34e9c2544019c3e835b6e 100644 (file)
@@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to
        Mode" section of linkgit:git-add[1] to learn how to operate
        the `--patch` mode.
 
+include::diff-context-options.adoc[]
+
 `-W`::
 `--worktree`::
 `-S`::
index 1a5177f4986ca4b87da7004da29653a3c0e17460..0578c619c410c661b7696e02affe28503073e452 100644 (file)
@@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use
 `--no-keep-index` to override this.
 
+include::diff-context-options.adoc[]
+
 -S::
 --staged::
        This option is only valid for `push` and `save` commands.
index eb3d0d3ada84277b0650ced529df45d0aa21e1e8..3e692b47eca0a3f6b664743eaa6c12d47082ceed 100644 (file)
@@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s,
        free(key);
 }
 
-void init_add_i_state(struct add_i_state *s, struct repository *r)
+void init_add_i_state(struct add_i_state *s, struct repository *r,
+                     struct add_p_opt *add_p_opt)
 {
        const char *value;
 
@@ -90,6 +91,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
        repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key);
        if (s->use_single_key)
                setbuf(stdin, NULL);
+
+       if (add_p_opt->context != -1) {
+               if (add_p_opt->context < 0)
+                       die(_("%s cannot be negative"), "--unified");
+               s->context = add_p_opt->context;
+       }
+       if (add_p_opt->interhunkcontext != -1) {
+               if (add_p_opt->interhunkcontext < 0)
+                       die(_("%s cannot be negative"), "--inter-hunk-context");
+               s->interhunkcontext = add_p_opt->interhunkcontext;
+       }
 }
 
 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,
        opts->prompt = N_("Patch update");
        count = list_and_choose(s, files, opts);
        if (count > 0) {
+               struct add_p_opt add_p_opt = {
+                       .context = s->context,
+                       .interhunkcontext = s->interhunkcontext,
+               };
                struct strvec args = STRVEC_INIT;
                struct pathspec ps_selected = { 0 };
 
@@ -988,7 +1004,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
                parse_pathspec(&ps_selected,
                               PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
                               PATHSPEC_LITERAL_PATH, "", args.v);
-               res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
+               res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected);
                strvec_clear(&args);
                clear_pathspec(&ps_selected);
        }
@@ -1023,10 +1039,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
        if (count > 0) {
                struct child_process cmd = CHILD_PROCESS_INIT;
 
-               strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached",
-                            oid_to_hex(!is_initial ? &oid :
-                                       s->r->hash_algo->empty_tree),
-                            "--", NULL);
+               strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL);
+               if (s->context != -1)
+                       strvec_pushf(&cmd.args, "--unified=%i", s->context);
+               if (s->interhunkcontext != -1)
+                       strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext);
+               strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid :
+                            s->r->hash_algo->empty_tree), "--", NULL);
                for (i = 0; i < files->items.nr; i++)
                        if (files->selected[i])
                                strvec_push(&cmd.args,
@@ -1119,7 +1138,8 @@ static void command_prompt_help(struct add_i_state *s)
                         _("(empty) select nothing"));
 }
 
-int run_add_i(struct repository *r, const struct pathspec *ps)
+int run_add_i(struct repository *r, const struct pathspec *ps,
+             struct add_p_opt *add_p_opt)
 {
        struct add_i_state s = { NULL };
        struct print_command_item_data data = { "[", "]" };
@@ -1162,7 +1182,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
                        ->util = util;
        }
 
-       init_add_i_state(&s, r);
+       init_add_i_state(&s, r, add_p_opt);
 
        /*
         * When color was asked for, use the prompt color for
index c63f35b14be86806ac16d55214d459da22f7a72f..4213dcd67b9a8e4ccbcb7186da8625d7caeaef54 100644 (file)
@@ -3,6 +3,13 @@
 
 #include "color.h"
 
+struct add_p_opt {
+       int context;
+       int interhunkcontext;
+};
+
+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }
+
 struct add_i_state {
        struct repository *r;
        int use_color;
@@ -21,12 +28,14 @@ struct add_i_state {
        int context, interhunkcontext;
 };
 
-void init_add_i_state(struct add_i_state *s, struct repository *r);
+void init_add_i_state(struct add_i_state *s, struct repository *r,
+                     struct add_p_opt *add_p_opt);
 void clear_add_i_state(struct add_i_state *s);
 
 struct repository;
 struct pathspec;
-int run_add_i(struct repository *r, const struct pathspec *ps);
+int run_add_i(struct repository *r, const struct pathspec *ps,
+             struct add_p_opt *add_p_opt);
 
 enum add_p_mode {
        ADD_P_ADD,
@@ -37,6 +46,7 @@ enum add_p_mode {
 };
 
 int run_add_p(struct repository *r, enum add_p_mode mode,
-             const char *revision, const struct pathspec *ps);
+             struct add_p_opt *o, const char *revision,
+             const struct pathspec *ps);
 
 #endif
index b0125b51ba45e0492b48112aa67f82b6c35ddcbc..302e6ba7d9a35322b951681418c1ab62cd9fed40 100644 (file)
@@ -1763,14 +1763,15 @@ soft_increment:
 }
 
 int run_add_p(struct repository *r, enum add_p_mode mode,
-             const char *revision, const struct pathspec *ps)
+             struct add_p_opt *o, const char *revision,
+             const struct pathspec *ps)
 {
        struct add_p_state s = {
                { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
        };
        size_t i, binary_count = 0;
 
-       init_add_i_state(&s.s, r);
+       init_add_i_state(&s.s, r, o);
 
        if (mode == ADD_P_STASH)
                s.mode = &patch_mode_stash;
index 7c292ffdc6c2c92e6c9a156d5ba713e07a629fac..a00dab9b8fa094ec810dd4006d0fb29372da6662 100644 (file)
@@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = {
        NULL
 };
 static int patch_interactive, add_interactive, edit_interactive;
+static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
 static int take_worktree_changes;
 static int add_renormalize;
 static int pathspec_file_nul;
@@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec *
 int interactive_add(struct repository *repo,
                    const char **argv,
                    const char *prefix,
-                   int patch)
+                   int patch, struct add_p_opt *add_p_opt)
 {
        struct pathspec pathspec;
        int ret;
@@ -169,9 +170,9 @@ int interactive_add(struct repository *repo,
                       prefix, argv);
 
        if (patch)
-               ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec);
+               ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec);
        else
-               ret = !!run_add_i(repo, &pathspec);
+               ret = !!run_add_i(repo, &pathspec, add_p_opt);
 
        clear_pathspec(&pathspec);
        return ret;
@@ -253,6 +254,8 @@ static struct option builtin_add_options[] = {
        OPT_GROUP(""),
        OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
        OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
+       OPT_DIFF_UNIFIED(&add_p_opt.context),
+       OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
@@ -394,6 +397,11 @@ int cmd_add(int argc,
        prepare_repo_settings(repo);
        repo->settings.command_requires_full_index = 0;
 
+       if (add_p_opt.context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (add_p_opt.interhunkcontext < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
        if (patch_interactive)
                add_interactive = 1;
        if (add_interactive) {
@@ -401,7 +409,12 @@ int cmd_add(int argc,
                        die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
                if (pathspec_from_file)
                        die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
-               exit(interactive_add(repo, argv + 1, prefix, patch_interactive));
+               exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt));
+       } else {
+               if (add_p_opt.context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
+               if (add_p_opt.interhunkcontext != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
        }
 
        if (edit_interactive) {
index d185982f3a6372ff8939b95c032b554f4d7f67b1..2dc99ec7bfae57429e43e0463f39744dce06c416 100644 (file)
@@ -61,6 +61,8 @@ static const char * const restore_usage[] = {
 
 struct checkout_opts {
        int patch_mode;
+       int patch_context;
+       int patch_interhunk_context;
        int quiet;
        int merge;
        int force;
@@ -104,7 +106,12 @@ struct checkout_opts {
        struct tree *source_tree;
 };
 
-#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
+#define CHECKOUT_OPTS_INIT { \
+       .conflict_style = -1, \
+       .merge = -1, \
+       .patch_context = -1, \
+       .patch_interhunk_context = -1, \
+}
 
 struct branch_info {
        char *name; /* The short name used */
@@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        if (opts->patch_mode) {
                enum add_p_mode patch_mode;
+               struct add_p_opt add_p_opt = {
+                       .context = opts->patch_context,
+                       .interhunkcontext = opts->patch_interhunk_context,
+               };
                const char *rev = new_branch_info->name;
                char rev_oid[GIT_MAX_HEXSZ + 1];
 
@@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts,
                else
                        BUG("either flag must have been set, worktree=%d, index=%d",
                            opts->checkout_worktree, opts->checkout_index);
-               return !!run_add_p(the_repository, patch_mode, rev,
-                                  &opts->pathspec);
+               return !!run_add_p(the_repository, patch_mode, &add_p_opt,
+                                  rev, &opts->pathspec);
        }
 
        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,
                              N_("checkout their version for unmerged files"),
                              3, PARSE_OPT_NONEG),
                OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
+               OPT_DIFF_UNIFIED(&opts->patch_context),
+               OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context),
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
                OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
@@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        argc = parse_options(argc, argv, prefix, options,
                             usagestr, parseopt_flags);
 
+       if (opts->patch_context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (opts->patch_interhunk_context < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
+       if (!opts->patch_mode) {
+               if (opts->patch_context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--patch");
+               if (opts->patch_interhunk_context != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
+       }
+
        if (opts->show_progress < 0) {
                if (opts->quiet)
                        opts->show_progress = 0;
index fba0dded64a718e5e19bcbe22d7b69bea31d7275..73673bc7db9ff8254b43b35341d1afc51d16929d 100644 (file)
@@ -19,6 +19,7 @@
 #include "environment.h"
 #include "diff.h"
 #include "commit.h"
+#include "add-interactive.h"
 #include "gettext.h"
 #include "revision.h"
 #include "wt-status.h"
@@ -122,6 +123,7 @@ static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
+static struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
 static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
@@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix,
        const char *ret;
        char *path = NULL;
 
+       if (add_p_opt.context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (add_p_opt.interhunkcontext < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
        if (is_status)
                refresh_flags |= REFRESH_UNMERGED;
        parse_pathspec(&pathspec, 0,
@@ -400,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
                old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
                setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
 
-               if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0)
+               if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0)
                        die(_("interactive add failed"));
 
                the_repository->index_file = old_repo_index_file;
@@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix,
                commit_style = COMMIT_NORMAL;
                ret = get_lock_file_path(&index_lock);
                goto out;
+       } else {
+               if (add_p_opt.context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch");
+               if (add_p_opt.interhunkcontext != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch");
        }
 
        /*
@@ -1722,6 +1734,8 @@ int cmd_commit(int argc,
                OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
                OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
                OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")),
+               OPT_DIFF_UNIFIED(&add_p_opt.context),
+               OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
                OPT_BOOL('o', "only", &only, N_("commit only specified files")),
                OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")),
                OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
index dc50ffc1ac59e848ce0f04654c7577d76f9c0336..9fb32795c9c53d5ac5c3f59fdf7bf980be004039 100644 (file)
@@ -346,6 +346,7 @@ int cmd_reset(int argc,
        struct object_id oid;
        struct pathspec pathspec;
        int intent_to_add = 0;
+       struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
        const struct option options[] = {
                OPT__QUIET(&quiet, N_("be quiet, only report errors")),
                OPT_BOOL(0, "no-refresh", &no_refresh,
@@ -370,6 +371,8 @@ int cmd_reset(int argc,
                               PARSE_OPT_OPTARG,
                               option_parse_recurse_submodules_worktree_updater),
                OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
+               OPT_DIFF_UNIFIED(&add_p_opt.context),
+               OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
                OPT_BOOL('N', "intent-to-add", &intent_to_add,
                                N_("record only the fact that removed paths will be added later")),
                OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
@@ -420,6 +423,11 @@ int cmd_reset(int argc,
                oidcpy(&oid, &tree->object.oid);
        }
 
+       if (add_p_opt.context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (add_p_opt.interhunkcontext < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
@@ -427,9 +435,14 @@ int cmd_reset(int argc,
                if (reset_type != NONE)
                        die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
                trace2_cmd_mode("patch-interactive");
-               update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev,
-                                  &pathspec);
+               update_ref_status = !!run_add_p(the_repository, ADD_P_RESET,
+                                               &add_p_opt, rev, &pathspec);
                goto cleanup;
+       } else {
+               if (add_p_opt.context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--patch");
+               if (add_p_opt.interhunkcontext != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
        }
 
        /* git reset tree [--] paths... can be used to
index cfbd92852a655700354286d4aa9c112e1f2dcf32..745421187cc10644a21a7623f6d71eab0023434c 100644 (file)
@@ -1242,7 +1242,8 @@ done:
 }
 
 static int stash_patch(struct stash_info *info, const struct pathspec *ps,
-                      struct strbuf *out_patch, int quiet)
+                      struct strbuf *out_patch, int quiet,
+                      struct add_p_opt *add_p_opt)
 {
        int ret = 0;
        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,
        old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
        setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
 
-       ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);
+       ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps);
 
        the_repository->index_file = old_repo_index_file;
        if (old_index_env && *old_index_env)
@@ -1362,8 +1363,8 @@ done:
 }
 
 static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
-                          int include_untracked, int patch_mode, int only_staged,
-                          struct stash_info *info, struct strbuf *patch,
+                          int include_untracked, int patch_mode, struct add_p_opt *add_p_opt,
+                          int only_staged, struct stash_info *info, struct strbuf *patch,
                           int quiet)
 {
        int ret = 0;
@@ -1439,7 +1440,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
                untracked_commit_option = 1;
        }
        if (patch_mode) {
-               ret = stash_patch(info, ps, patch, quiet);
+               ret = stash_patch(info, ps, patch, quiet, add_p_opt);
                if (ret < 0) {
                        if (!quiet)
                                fprintf_ln(stderr, _("Cannot save the current "
@@ -1513,7 +1514,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
        if (!check_changes_tracked_files(&ps))
                return 0;
 
-       ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
+       ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info,
                              NULL, 0);
        if (!ret)
                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,
 }
 
 static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
-                        int keep_index, int patch_mode, int include_untracked, int only_staged)
+                        int keep_index, int patch_mode, struct add_p_opt *add_p_opt,
+                        int include_untracked, int only_staged)
 {
        int ret = 0;
        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
 
        if (stash_msg)
                strbuf_addstr(&stash_msg_buf, stash_msg);
-       if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
-                           &info, &patch, quiet)) {
+       if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+                           add_p_opt, only_staged, &info, &patch, quiet)) {
                ret = -1;
                goto done;
        }
@@ -1768,6 +1770,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
        const char *stash_msg = NULL;
        char *pathspec_from_file = NULL;
        struct pathspec ps;
+       struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
        struct option options[] = {
                OPT_BOOL('k', "keep-index", &keep_index,
                         N_("keep index")),
@@ -1775,6 +1778,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                         N_("stash staged changes only")),
                OPT_BOOL('p', "patch", &patch_mode,
                         N_("stash in patch mode")),
+               OPT_DIFF_UNIFIED(&add_p_opt.context),
+               OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
                OPT__QUIET(&quiet, N_("quiet mode")),
                OPT_BOOL('u', "include-untracked", &include_untracked,
                         N_("include untracked files in stash")),
@@ -1826,8 +1831,20 @@ static int push_stash(int argc, const char **argv, const char *prefix,
                die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
+       if (!patch_mode) {
+               if (add_p_opt.context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--patch");
+               if (add_p_opt.interhunkcontext != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
+       }
+
+       if (add_p_opt.context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (add_p_opt.interhunkcontext < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
        ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
-                           include_untracked, only_staged);
+                           &add_p_opt, include_untracked, only_staged);
 
        clear_pathspec(&ps);
        free(pathspec_from_file);
@@ -1852,6 +1869,7 @@ static int save_stash(int argc, const char **argv, const char *prefix,
        const char *stash_msg = NULL;
        struct pathspec ps;
        struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
        struct option options[] = {
                OPT_BOOL('k', "keep-index", &keep_index,
                         N_("keep index")),
@@ -1859,6 +1877,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
                         N_("stash staged changes only")),
                OPT_BOOL('p', "patch", &patch_mode,
                         N_("stash in patch mode")),
+               OPT_DIFF_UNIFIED(&add_p_opt.context),
+               OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),
                OPT__QUIET(&quiet, N_("quiet mode")),
                OPT_BOOL('u', "include-untracked", &include_untracked,
                         N_("include untracked files in stash")),
@@ -1877,8 +1897,22 @@ static int save_stash(int argc, const char **argv, const char *prefix,
                stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
 
        memset(&ps, 0, sizeof(ps));
+
+       if (add_p_opt.context < -1)
+               die(_("'%s' cannot be negative"), "--unified");
+       if (add_p_opt.interhunkcontext < -1)
+               die(_("'%s' cannot be negative"), "--inter-hunk-context");
+
+       if (!patch_mode) {
+               if (add_p_opt.context != -1)
+                       die(_("the option '%s' requires '%s'"), "--unified", "--patch");
+               if (add_p_opt.interhunkcontext != -1)
+                       die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch");
+       }
+
        ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
-                           patch_mode, include_untracked, only_staged);
+                           patch_mode, &add_p_opt, include_untracked,
+                           only_staged);
 
        strbuf_release(&stash_msg_buf);
        return ret;
index 70c870dae4d4b972a1f608983fc17024ad04d2e9..7a7fedbc2f1484eb1bac1d84cebccf59dbcb1850 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -2,6 +2,7 @@
 #define COMMIT_H
 
 #include "object.h"
+#include "add-interactive.h"
 
 struct signature_check;
 struct strbuf;
@@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *);
 int interactive_add(struct repository *repo,
                    const char **argv,
                    const char *prefix,
-                   int patch);
+                   int patch, struct add_p_opt *add_p_opt);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
index 91c3e3c29b3dda1f679ae4c77e250180ed6f9639..bdae8f116198d615f066fb6833e15a1163fdf149 100644 (file)
@@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
 #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
 #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"))
 #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
+#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG)
+#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)
 
 #define OPT_IPVERSION(v) \
        OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \
index 18dc329ea1f697ef8f066d8797a8e45a6741a3d9..04d2a1983525313afbdd19da4fa0368c8e495361 100755 (executable)
@@ -1252,4 +1252,53 @@ test_expect_success 'add -p rejects negative diff.context' '
        test_grep "diff.context cannot be negative" output
 '
 
+for cmd in add checkout restore 'commit -m file'
+do
+       test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" '
+               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 &&
+               git add file &&
+               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 &&
+               echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
+                       $cmd -p -U 4 --inter-hunk-context 2 >actual &&
+               test_grep "@@ -2,20 +2,20 @@" actual
+       '
+done
+
+test_expect_success 'reset accepts -U and --inter-hunk-context' '
+       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 &&
+       git commit -m file file &&
+       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 &&
+       git add file &&
+       echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
+               reset -p -U 4 --inter-hunk-context 2 >actual &&
+       test_grep "@@ -2,20 +2,20 @@" actual
+'
+
+test_expect_success 'stash accepts -U and --inter-hunk-context' '
+       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 &&
+       git commit -m file file &&
+       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 &&
+       echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \
+               stash -p -U 4 --inter-hunk-context 2 >actual &&
+       test_grep "@@ -2,20 +2,20 @@" actual
+'
+
+for cmd in add checkout commit reset restore "stash save" "stash push"
+do
+       test_expect_success "$cmd rejects invalid context options" '
+               test_must_fail git $cmd -p -U -3 2>actual &&
+               cat actual | echo &&
+               test_grep -e ".--unified. cannot be negative" actual &&
+
+               test_must_fail git $cmd -p --inter-hunk-context -3 2>actual &&
+               test_grep -e ".--inter-hunk-context. cannot be negative" actual &&
+
+               test_must_fail git $cmd -U 7 2>actual &&
+               test_grep -E ".--unified. requires .(--interactive/)?--patch." actual &&
+
+               test_must_fail git $cmd --inter-hunk-context 2 2>actual &&
+               test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual
+       '
+done
+
 test_done
index 343b8cd1912bd540de24ccf1714dae69b77fdce7..6650d33fba69c8e23671116595c3a7f8a2830562 100755 (executable)
@@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' '
        --merge Z
        --conflict=Z
        --patch Z
+       --unified=Z
+       --inter-hunk-context=Z
        --ignore-skip-worktree-bits Z
        --ignore-other-worktrees Z
        --recurse-submodules Z