Merge branch 'lm/add-p-context'

"git add/etc -p" now honor the diff.context configuration variable,
and also they learn to honor the -U<n> command-line option.

* lm/add-p-context:
  add-patch: add diff.context command line overrides
  add-patch: respect diff.context configuration
  t: use test_config in t4055
  t: use test_grep in t3701 and t4055
maint
Junio C Hamano 2025-08-04 08:10:33 -07:00
commit d63f334a82
20 changed files with 324 additions and 83 deletions

View File

@ -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.

View File

@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the
initial command menu and directly jumps to the `patch` subcommand. initial command menu and directly jumps to the `patch` subcommand.
See ``Interactive mode'' for details. See ``Interactive mode'' for details.


include::diff-context-options.adoc[]

`-e`:: `-e`::
`--edit`:: `--edit`::
Open the diff vs. the index in an editor and let the user Open the diff vs. the index in an editor and let the user

View 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 Note that this option uses the no overlay mode by default (see also
`--overlay`), and currently doesn't support overlay mode. `--overlay`), and currently doesn't support overlay mode.


include::diff-context-options.adoc[]

`--ignore-other-worktrees`:: `--ignore-other-worktrees`::
`git checkout` refuses when the wanted branch is already checked `git checkout` refuses when the wanted branch is already checked
out or otherwise in use by another worktree. This option makes out or otherwise in use by another worktree. This option makes

View File

@ -76,6 +76,8 @@ OPTIONS
which changes to commit. See linkgit:git-add[1] for which changes to commit. See linkgit:git-add[1] for
details. details.


include::diff-context-options.adoc[]

`-C <commit>`:: `-C <commit>`::
`--reuse-message=<commit>`:: `--reuse-message=<commit>`::
Take an existing _<commit>_ object, and reuse the log message Take an existing _<commit>_ object, and reuse the log message

View File

@ -125,6 +125,8 @@ OPTIONS
separated with _NUL_ character and all other characters are taken separated with _NUL_ character and all other characters are taken
literally (including newlines and quotes). literally (including newlines and quotes).


include::diff-context-options.adoc[]

`--`:: `--`::
Do not interpret any more arguments as options. Do not interpret any more arguments as options.



View File

@ -50,6 +50,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 Mode" section of linkgit:git-add[1] to learn how to operate
the `--patch` mode. the `--patch` mode.


include::diff-context-options.adoc[]

`-W`:: `-W`::
`--worktree`:: `--worktree`::
`-S`:: `-S`::

View File

@ -222,6 +222,8 @@ to learn how to operate the `--patch` mode.
The `--patch` option implies `--keep-index`. You can use The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this. `--no-keep-index` to override this.


include::diff-context-options.adoc[]

-S:: -S::
--staged:: --staged::
This option is only valid for `push` and `save` commands. This option is only valid for `push` and `save` commands.

View File

@ -36,11 +36,14 @@ static void init_color(struct repository *r, struct add_i_state *s,
free(key); 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; const char *value;


s->r = r; s->r = r;
s->context = -1;
s->interhunkcontext = -1;


if (repo_config_get_value(r, "color.interactive", &value)) if (repo_config_get_value(r, "color.interactive", &value))
s->use_color = -1; s->use_color = -1;
@ -78,9 +81,27 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
repo_config_get_string(r, "diff.algorithm", repo_config_get_string(r, "diff.algorithm",
&s->interactive_diff_algorithm); &s->interactive_diff_algorithm);


if (!repo_config_get_int(r, "diff.context", &s->context))
if (s->context < 0)
die(_("%s cannot be negative"), "diff.context");
if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext))
if (s->interhunkcontext < 0)
die(_("%s cannot be negative"), "diff.interHunkContext");

repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key);
if (s->use_single_key) if (s->use_single_key)
setbuf(stdin, NULL); 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) void clear_add_i_state(struct add_i_state *s)
@ -969,6 +990,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
opts->prompt = N_("Patch update"); opts->prompt = N_("Patch update");
count = list_and_choose(s, files, opts); count = list_and_choose(s, files, opts);
if (count > 0) { if (count > 0) {
struct add_p_opt add_p_opt = {
.context = s->context,
.interhunkcontext = s->interhunkcontext,
};
struct strvec args = STRVEC_INIT; struct strvec args = STRVEC_INIT;
struct pathspec ps_selected = { 0 }; struct pathspec ps_selected = { 0 };


@ -979,7 +1004,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
parse_pathspec(&ps_selected, parse_pathspec(&ps_selected,
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
PATHSPEC_LITERAL_PATH, "", args.v); 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); strvec_clear(&args);
clear_pathspec(&ps_selected); clear_pathspec(&ps_selected);
} }
@ -1014,10 +1039,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
if (count > 0) { if (count > 0) {
struct child_process cmd = CHILD_PROCESS_INIT; struct child_process cmd = CHILD_PROCESS_INIT;


strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL);
oid_to_hex(!is_initial ? &oid : if (s->context != -1)
s->r->hash_algo->empty_tree), strvec_pushf(&cmd.args, "--unified=%i", s->context);
"--", NULL); 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++) for (i = 0; i < files->items.nr; i++)
if (files->selected[i]) if (files->selected[i])
strvec_push(&cmd.args, strvec_push(&cmd.args,
@ -1110,7 +1138,8 @@ static void command_prompt_help(struct add_i_state *s)
_("(empty) select nothing")); _("(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 add_i_state s = { NULL };
struct print_command_item_data data = { "[", "]" }; struct print_command_item_data data = { "[", "]" };
@ -1153,7 +1182,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
->util = util; ->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 * When color was asked for, use the prompt color for

View File

@ -3,6 +3,13 @@


#include "color.h" #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 add_i_state {
struct repository *r; struct repository *r;
int use_color; int use_color;
@ -18,14 +25,17 @@ struct add_i_state {


int use_single_key; int use_single_key;
char *interactive_diff_filter, *interactive_diff_algorithm; char *interactive_diff_filter, *interactive_diff_algorithm;
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); void clear_add_i_state(struct add_i_state *s);


struct repository; struct repository;
struct pathspec; 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 { enum add_p_mode {
ADD_P_ADD, ADD_P_ADD,
@ -36,6 +46,7 @@ enum add_p_mode {
}; };


int run_add_p(struct repository *r, enum add_p_mode 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 #endif

View File

@ -414,7 +414,6 @@ static int normalize_marker(const char *p)
static int parse_diff(struct add_p_state *s, const struct pathspec *ps) static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
{ {
struct strvec args = STRVEC_INIT; struct strvec args = STRVEC_INIT;
const char *diff_algorithm = s->s.interactive_diff_algorithm;
struct strbuf *plain = &s->plain, *colored = NULL; struct strbuf *plain = &s->plain, *colored = NULL;
struct child_process cp = CHILD_PROCESS_INIT; struct child_process cp = CHILD_PROCESS_INIT;
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
@ -424,8 +423,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
int res; int res;


strvec_pushv(&args, s->mode->diff_cmd); strvec_pushv(&args, s->mode->diff_cmd);
if (diff_algorithm) if (s->s.context != -1)
strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); strvec_pushf(&args, "--unified=%i", s->s.context);
if (s->s.interhunkcontext != -1)
strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext);
if (s->s.interactive_diff_algorithm)
strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm);
if (s->revision) { if (s->revision) {
struct object_id oid; struct object_id oid;
strvec_push(&args, strvec_push(&args,
@ -1760,14 +1763,15 @@ soft_increment:
} }


int run_add_p(struct repository *r, enum add_p_mode 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)
{ {
struct add_p_state s = { struct add_p_state s = {
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
}; };
size_t i, binary_count = 0; 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) if (mode == ADD_P_STASH)
s.mode = &patch_mode_stash; s.mode = &patch_mode_stash;

View File

@ -30,6 +30,7 @@ static const char * const builtin_add_usage[] = {
NULL NULL
}; };
static int patch_interactive, add_interactive, edit_interactive; 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 take_worktree_changes;
static int add_renormalize; static int add_renormalize;
static int pathspec_file_nul; static int pathspec_file_nul;
@ -158,7 +159,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec *
int interactive_add(struct repository *repo, int interactive_add(struct repository *repo,
const char **argv, const char **argv,
const char *prefix, const char *prefix,
int patch) int patch, struct add_p_opt *add_p_opt)
{ {
struct pathspec pathspec; struct pathspec pathspec;
int ret; int ret;
@ -170,9 +171,9 @@ int interactive_add(struct repository *repo,
prefix, argv); prefix, argv);


if (patch) 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 else
ret = !!run_add_i(repo, &pathspec); ret = !!run_add_i(repo, &pathspec, add_p_opt);


clear_pathspec(&pathspec); clear_pathspec(&pathspec);
return ret; return ret;
@ -254,6 +255,8 @@ static struct option builtin_add_options[] = {
OPT_GROUP(""), OPT_GROUP(""),
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), 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_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
@ -395,6 +398,11 @@ int cmd_add(int argc,
prepare_repo_settings(repo); prepare_repo_settings(repo);
repo->settings.command_requires_full_index = 0; 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) if (patch_interactive)
add_interactive = 1; add_interactive = 1;
if (add_interactive) { if (add_interactive) {
@ -402,7 +410,12 @@ int cmd_add(int argc,
die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
if (pathspec_from_file) if (pathspec_from_file)
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); 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) { if (edit_interactive) {

View File

@ -61,6 +61,8 @@ static const char * const restore_usage[] = {


struct checkout_opts { struct checkout_opts {
int patch_mode; int patch_mode;
int patch_context;
int patch_interhunk_context;
int quiet; int quiet;
int merge; int merge;
int force; int force;
@ -104,7 +106,12 @@ struct checkout_opts {
struct tree *source_tree; 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 { struct branch_info {
char *name; /* The short name used */ char *name; /* The short name used */
@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts,


if (opts->patch_mode) { if (opts->patch_mode) {
enum add_p_mode 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; const char *rev = new_branch_info->name;
char rev_oid[GIT_MAX_HEXSZ + 1]; char rev_oid[GIT_MAX_HEXSZ + 1];


@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts,
else else
BUG("either flag must have been set, worktree=%d, index=%d", BUG("either flag must have been set, worktree=%d, index=%d",
opts->checkout_worktree, opts->checkout_index); opts->checkout_worktree, opts->checkout_index);
return !!run_add_p(the_repository, patch_mode, rev, return !!run_add_p(the_repository, patch_mode, &add_p_opt,
&opts->pathspec); rev, &opts->pathspec);
} }


repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); 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"), N_("checkout their version for unmerged files"),
3, PARSE_OPT_NONEG), 3, PARSE_OPT_NONEG),
OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), 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, OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")), N_("do not limit pathspecs to sparse entries only")),
OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), 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, argc = parse_options(argc, argv, prefix, options,
usagestr, parseopt_flags); 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->show_progress < 0) {
if (opts->quiet) if (opts->quiet)
opts->show_progress = 0; opts->show_progress = 0;

View File

@ -19,6 +19,7 @@
#include "environment.h" #include "environment.h"
#include "diff.h" #include "diff.h"
#include "commit.h" #include "commit.h"
#include "add-interactive.h"
#include "gettext.h" #include "gettext.h"
#include "revision.h" #include "revision.h"
#include "wt-status.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 char *fixup_message, *fixup_commit, *squash_message;
static const char *fixup_prefix; static const char *fixup_prefix;
static int all, also, interactive, patch_interactive, only, amend, signoff; 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 edit_flag = -1; /* unspecified */
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */ 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; const char *ret;
char *path = NULL; 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) if (is_status)
refresh_flags |= REFRESH_UNMERGED; refresh_flags |= REFRESH_UNMERGED;
parse_pathspec(&pathspec, 0, 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)); old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); 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")); die(_("interactive add failed"));


the_repository->index_file = old_repo_index_file; 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; commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock); ret = get_lock_file_path(&index_lock);
goto out; 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");
} }


/* /*
@ -1726,6 +1738,8 @@ int cmd_commit(int argc,
OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")),
OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")),
OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), 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('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('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")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),

View File

@ -346,6 +346,7 @@ int cmd_reset(int argc,
struct object_id oid; struct object_id oid;
struct pathspec pathspec; struct pathspec pathspec;
int intent_to_add = 0; int intent_to_add = 0;
struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
const struct option options[] = { const struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "no-refresh", &no_refresh, OPT_BOOL(0, "no-refresh", &no_refresh,
@ -370,6 +371,8 @@ int cmd_reset(int argc,
PARSE_OPT_OPTARG, PARSE_OPT_OPTARG,
option_parse_recurse_submodules_worktree_updater), option_parse_recurse_submodules_worktree_updater),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), 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, OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")), N_("record only the fact that removed paths will be added later")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
@ -420,6 +423,11 @@ int cmd_reset(int argc,
oidcpy(&oid, &tree->object.oid); 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); prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0; the_repository->settings.command_requires_full_index = 0;


@ -427,9 +435,14 @@ int cmd_reset(int argc,
if (reset_type != NONE) if (reset_type != NONE)
die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive"); trace2_cmd_mode("patch-interactive");
update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, update_ref_status = !!run_add_p(the_repository, ADD_P_RESET,
&pathspec); &add_p_opt, rev, &pathspec);
goto cleanup; 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 /* git reset tree [--] paths... can be used to

View File

@ -1301,7 +1301,8 @@ done:
} }


static int stash_patch(struct stash_info *info, const struct pathspec *ps, 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; int ret = 0;
struct child_process cp_read_tree = CHILD_PROCESS_INIT; struct child_process cp_read_tree = CHILD_PROCESS_INIT;
@ -1326,7 +1327,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); 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; the_repository->index_file = old_repo_index_file;
if (old_index_env && *old_index_env) if (old_index_env && *old_index_env)
@ -1421,8 +1422,8 @@ done:
} }


static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
int include_untracked, int patch_mode, int only_staged, int include_untracked, int patch_mode, struct add_p_opt *add_p_opt,
struct stash_info *info, struct strbuf *patch, int only_staged, struct stash_info *info, struct strbuf *patch,
int quiet) int quiet)
{ {
int ret = 0; int ret = 0;
@ -1503,7 +1504,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
untracked_commit_option = 1; untracked_commit_option = 1;
} }
if (patch_mode) { if (patch_mode) {
ret = stash_patch(info, ps, patch, quiet); ret = stash_patch(info, ps, patch, quiet, add_p_opt);
if (ret < 0) { if (ret < 0) {
if (!quiet) if (!quiet)
fprintf_ln(stderr, _("Cannot save the current " fprintf_ln(stderr, _("Cannot save the current "
@ -1578,7 +1579,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED,
if (!check_changes_tracked_files(&ps)) if (!check_changes_tracked_files(&ps))
return 0; 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); NULL, 0);
if (!ret) if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit)); printf_ln("%s", oid_to_hex(&info.w_commit));
@ -1589,7 +1590,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, 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; int ret = 0;
struct stash_info info = STASH_INFO_INIT; struct stash_info info = STASH_INFO_INIT;
@ -1659,8 +1661,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q


if (stash_msg) if (stash_msg)
strbuf_addstr(&stash_msg_buf, stash_msg); strbuf_addstr(&stash_msg_buf, stash_msg);
if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
&info, &patch, quiet)) { add_p_opt, only_staged, &info, &patch, quiet)) {
ret = -1; ret = -1;
goto done; goto done;
} }
@ -1833,6 +1835,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
const char *stash_msg = NULL; const char *stash_msg = NULL;
char *pathspec_from_file = NULL; char *pathspec_from_file = NULL;
struct pathspec ps; struct pathspec ps;
struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
struct option options[] = { struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index, OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")), N_("keep index")),
@ -1840,6 +1843,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
N_("stash staged changes only")), N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode, OPT_BOOL('p', "patch", &patch_mode,
N_("stash in 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__QUIET(&quiet, N_("quiet mode")),
OPT_BOOL('u', "include-untracked", &include_untracked, OPT_BOOL('u', "include-untracked", &include_untracked,
N_("include untracked files in stash")), N_("include untracked files in stash")),
@ -1895,8 +1900,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"); 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, 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); clear_pathspec(&ps);
free(pathspec_from_file); free(pathspec_from_file);
@ -1921,6 +1938,7 @@ static int save_stash(int argc, const char **argv, const char *prefix,
const char *stash_msg = NULL; const char *stash_msg = NULL;
struct pathspec ps; struct pathspec ps;
struct strbuf stash_msg_buf = STRBUF_INIT; struct strbuf stash_msg_buf = STRBUF_INIT;
struct add_p_opt add_p_opt = ADD_P_OPT_INIT;
struct option options[] = { struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index, OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")), N_("keep index")),
@ -1928,6 +1946,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,
N_("stash staged changes only")), N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode, OPT_BOOL('p', "patch", &patch_mode,
N_("stash in 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__QUIET(&quiet, N_("quiet mode")),
OPT_BOOL('u', "include-untracked", &include_untracked, OPT_BOOL('u', "include-untracked", &include_untracked,
N_("include untracked files in stash")), N_("include untracked files in stash")),
@ -1946,8 +1966,22 @@ static int save_stash(int argc, const char **argv, const char *prefix,
stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');


memset(&ps, 0, sizeof(ps)); 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, 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); strbuf_release(&stash_msg_buf);
return ret; return ret;

View File

@ -2,6 +2,7 @@
#define COMMIT_H #define COMMIT_H


#include "object.h" #include "object.h"
#include "add-interactive.h"


struct signature_check; struct signature_check;
struct strbuf; struct strbuf;
@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *);
int interactive_add(struct repository *repo, int interactive_add(struct repository *repo,
const char **argv, const char **argv,
const char *prefix, const char *prefix,
int patch); int patch, struct add_p_opt *add_p_opt);


struct commit_extra_header { struct commit_extra_header {
struct commit_extra_header *next; struct commit_extra_header *next;

View File

@ -623,6 +623,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_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_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_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) \ #define OPT_IPVERSION(v) \
OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \

View File

@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' '
' '
test_expect_success 'status works (initial)' ' test_expect_success 'status works (initial)' '
git add -i </dev/null >output && git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output test_grep "+1/-0 *+2/-0 file" output
' '


test_expect_success 'setup expected' ' test_expect_success 'setup expected' '
@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' '
git add file && git add file &&
test_write_lines r 1 | git add -i && test_write_lines r 1 | git add -i &&
git ls-files >output && git ls-files >output &&
! grep . output test_grep ! . output
' '


test_expect_success 'add untracked (multiple)' ' test_expect_success 'add untracked (multiple)' '
@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' '
' '
test_expect_success 'status works (commit)' ' test_expect_success 'status works (commit)' '
git add -i </dev/null >output && git add -i </dev/null >output &&
grep "+1/-0 *+2/-0 file" output test_grep "+1/-0 *+2/-0 file" output
' '


test_expect_success 'update can stage deletions' ' test_expect_success 'update can stage deletions' '
@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' '
git add file && git add file &&
test_write_lines r 1 | git add -i && test_write_lines r 1 | git add -i &&
git add -i </dev/null >output && git add -i </dev/null >output &&
grep "unchanged *+3/-0 file" output test_grep "unchanged *+3/-0 file" output
' '


test_expect_success 'reject multi-key input' ' test_expect_success 'reject multi-key input' '
@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' '
test_expect_success 'bad edit rejected' ' test_expect_success 'bad edit rejected' '
git reset && git reset &&
test_write_lines e n d | git add -p >output && test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output test_grep "hunk does not apply" output
' '


test_expect_success 'setup patch' ' test_expect_success 'setup patch' '
@ -198,7 +198,7 @@ test_expect_success 'setup patch' '
test_expect_success 'garbage edit rejected' ' test_expect_success 'garbage edit rejected' '
git reset && git reset &&
test_write_lines e n d | git add -p >output && test_write_lines e n d | git add -p >output &&
grep "hunk does not apply" output test_grep "hunk does not apply" output
' '


test_expect_success 'setup patch' ' test_expect_success 'setup patch' '
@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' '
chmod +x file && chmod +x file &&
printf "y\\ny\\n" | git add -p && printf "y\\ny\\n" | git add -p &&
git diff --cached file >out && git diff --cached file >out &&
grep "new mode" out && test_grep "new mode" out &&
grep "+content" out && test_grep "+content" out &&
git diff file >out && git diff file >out &&
test_must_be_empty out test_must_be_empty out
' '
@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' '
printf "%s\n" s e q n q q | printf "%s\n" s e q n q q |
EDITOR=: git add -p && EDITOR=: git add -p &&
git diff >actual && git diff >actual &&
! grep "^+15" actual test_grep ! "^+15" actual
' '


test_expect_success 'split hunk "add -p (no, yes, edit)"' ' test_expect_success 'split hunk "add -p (no, yes, edit)"' '
@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' '
EDITOR=: git add -p 2>error && EDITOR=: git add -p 2>error &&
test_must_be_empty error && test_must_be_empty error &&
git diff >actual && git diff >actual &&
! grep "^+31" actual test_grep ! "^+31" actual
' '


test_expect_success 'split hunk with incomplete line at end' ' test_expect_success 'split hunk with incomplete line at end' '
@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' '
EDITOR=./fake_editor.sh git add -p 2>error && EDITOR=./fake_editor.sh git add -p 2>error &&
test_must_be_empty error && test_must_be_empty error &&
git diff --cached >actual && git diff --cached >actual &&
grep "^+22" actual test_grep "^+22" actual
' '


test_expect_success 'patch mode ignores unmerged entries' ' test_expect_success 'patch mode ignores unmerged entries' '
@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' '
test_must_fail git merge side && test_must_fail git merge side &&
echo changed >non-conflict.t && echo changed >non-conflict.t &&
echo y | git add -p >output && echo y | git add -p >output &&
! grep a/conflict.t output && test_grep ! a/conflict.t output &&
cat >expected <<-\EOF && cat >expected <<-\EOF &&
* Unmerged path conflict.t * Unmerged path conflict.t
diff --git a/non-conflict.t b/non-conflict.t diff --git a/non-conflict.t b/non-conflict.t
@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' '


# We do not want to depend on the exact coloring scheme # We do not want to depend on the exact coloring scheme
# git uses for diffs, so just check that we saw some kind of color. # git uses for diffs, so just check that we saw some kind of color.
grep "$(printf "\\033")" output test_grep "$(printf "\\033")" output
' '


test_expect_success 'colors can be overridden' ' test_expect_success 'colors can be overridden' '
@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' '
-c color.interactive.error=blue \ -c color.interactive.error=blue \
add -i 2>err.raw <input && add -i 2>err.raw <input &&
test_decode_color <err.raw >err && test_decode_color <err.raw >err &&
grep "<BLUE>Huh (trigger)?<RESET>" err && test_grep "<BLUE>Huh (trigger)?<RESET>" err &&


test_write_lines help quit >input && test_write_lines help quit >input &&
force_color git \ force_color git \
@ -863,7 +863,7 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
printf y >y && printf y >y &&
force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y &&
test_decode_color <output.raw >output && test_decode_color <output.raw >output &&
grep "old<" output test_grep "old<" output
' '


test_expect_success 'diffFilter filters diff' ' test_expect_success 'diffFilter filters diff' '
@ -876,7 +876,7 @@ test_expect_success 'diffFilter filters diff' '


# avoid depending on the exact coloring or content of the prompts, # avoid depending on the exact coloring or content of the prompts,
# and just make sure we saw our diff prefixed # and just make sure we saw our diff prefixed
grep foo:.*content output test_grep foo:.*content output
' '


test_expect_success 'detect bogus diffFilter output' ' test_expect_success 'detect bogus diffFilter output' '
@ -886,7 +886,7 @@ test_expect_success 'detect bogus diffFilter output' '
test_config interactive.diffFilter "sed 6d" && test_config interactive.diffFilter "sed 6d" &&
printf y >y && printf y >y &&
force_color test_must_fail git add -p <y >output 2>&1 && force_color test_must_fail git add -p <y >output 2>&1 &&
grep "mismatched output" output test_grep "mismatched output" output
' '


test_expect_success 'handle iffy colored hunk headers' ' test_expect_success 'handle iffy colored hunk headers' '
@ -896,7 +896,7 @@ test_expect_success 'handle iffy colored hunk headers' '
printf n >n && printf n >n &&
force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \
add -p >output 2>&1 <n && add -p >output 2>&1 <n &&
grep "^XX$" output test_grep "^XX$" output
' '


test_expect_success 'handle very large filtered diff' ' test_expect_success 'handle very large filtered diff' '
@ -1002,7 +1002,7 @@ test_expect_success 'add -p does not expand argument lists' '
# update it, but we want to be sure that our "." pathspec # update it, but we want to be sure that our "." pathspec
# was not expanded into the argument list of any command. # was not expanded into the argument list of any command.
# So look only for "not-changed". # So look only for "not-changed".
! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out
' '


test_expect_success 'hunk-editing handles custom comment char' ' test_expect_success 'hunk-editing handles custom comment char' '
@ -1072,21 +1072,21 @@ test_expect_success 'setup different kinds of dirty submodules' '


test_expect_success 'status ignores dirty submodules (except HEAD)' ' test_expect_success 'status ignores dirty submodules (except HEAD)' '
git -C for-submodules add -i </dev/null >output && git -C for-submodules add -i </dev/null >output &&
grep dirty-head output && test_grep dirty-head output &&
grep dirty-both-ways output && test_grep dirty-both-ways output &&
! grep dirty-otherwise output test_grep ! dirty-otherwise output
' '


test_expect_success 'handle submodules' ' test_expect_success 'handle submodules' '
echo 123 >>for-submodules/dirty-otherwise/initial.t && echo 123 >>for-submodules/dirty-otherwise/initial.t &&


force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 &&
grep "No changes" output && test_grep "No changes" output &&


force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y &&
git -C for-submodules ls-files --stage dirty-head >actual && git -C for-submodules ls-files --stage dirty-head >actual &&
rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" &&
grep "$rev" actual test_grep "$rev" actual
' '


test_expect_success 'set up pathological context' ' test_expect_success 'set up pathological context' '
@ -1230,4 +1230,75 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' '
test_cmp expect actual test_cmp expect actual
' '


test_expect_success 'add -p respects diff.context' '
test_write_lines a b c d e f g h i j k l m >file &&
git add file &&
test_write_lines a b c d e f G h i j k l m >file &&
echo y | git -c diff.context=5 add -p >actual &&
test_grep "@@ -2,11 +2,11 @@" actual
'

test_expect_success 'add -p respects diff.interHunkContext' '
test_write_lines a b c d e f g h i j k l m n o p q r s >file &&
git add file &&
test_write_lines a b c d E f g i i j k l m N o p q r s >file &&
echo y | git -c diff.interhunkcontext=2 add -p >actual &&
test_grep "@@ -2,16 +2,16 @@" actual
'

test_expect_success 'add -p rejects negative diff.context' '
test_config diff.context -1 &&
test_must_fail git add -p 2>output &&
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 test_done

View File

@ -38,55 +38,55 @@ test_expect_success 'setup' '


test_expect_success 'the default number of context lines is 3' ' test_expect_success 'the default number of context lines is 3' '
git diff >output && git diff >output &&
! grep "^ d" output && test_grep ! "^ d" output &&
grep "^ e" output && test_grep "^ e" output &&
grep "^ j" output && test_grep "^ j" output &&
! grep "^ k" output test_grep ! "^ k" output
' '


test_expect_success 'diff.context honored by "log"' ' test_expect_success 'diff.context honored by "log"' '
git log -1 -p >output && git log -1 -p >output &&
! grep firstline output && test_grep ! firstline output &&
git config diff.context 8 && test_config diff.context 8 &&
git log -1 -p >output && git log -1 -p >output &&
grep "^ firstline" output test_grep "^ firstline" output
' '


test_expect_success 'The -U option overrides diff.context' ' test_expect_success 'The -U option overrides diff.context' '
git config diff.context 8 && test_config diff.context 8 &&
git log -U4 -1 >output && git log -U4 -1 >output &&
! grep "^ firstline" output test_grep ! "^ firstline" output
' '


test_expect_success 'diff.context honored by "diff"' ' test_expect_success 'diff.context honored by "diff"' '
git config diff.context 8 && test_config diff.context 8 &&
git diff >output && git diff >output &&
grep "^ firstline" output test_grep "^ firstline" output
' '


test_expect_success 'plumbing not affected' ' test_expect_success 'plumbing not affected' '
git config diff.context 8 && test_config diff.context 8 &&
git diff-files -p >output && git diff-files -p >output &&
! grep "^ firstline" output test_grep ! "^ firstline" output
' '


test_expect_success 'non-integer config parsing' ' test_expect_success 'non-integer config parsing' '
git config diff.context no && test_config diff.context no &&
test_must_fail git diff 2>output && test_must_fail git diff 2>output &&
test_grep "bad numeric config value" output test_grep "bad numeric config value" output
' '


test_expect_success 'negative integer config parsing' ' test_expect_success 'negative integer config parsing' '
git config diff.context -1 && test_config diff.context -1 &&
test_must_fail git diff 2>output && test_must_fail git diff 2>output &&
test_grep "bad config variable" output test_grep "bad config variable" output
' '


test_expect_success '-U0 is valid, so is diff.context=0' ' test_expect_success '-U0 is valid, so is diff.context=0' '
git config diff.context 0 && test_config diff.context 0 &&
git diff >output && git diff >output &&
grep "^-ADDED" output && test_grep "^-ADDED" output &&
grep "^+MODIFIED" output test_grep "^+MODIFIED" output
' '


test_expect_success '-U2147483647 works' ' test_expect_success '-U2147483647 works' '
@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' '
test_line_count = 16 x && test_line_count = 16 x &&
git diff -U2147483647 >output && git diff -U2147483647 >output &&
test_line_count = 22 output && test_line_count = 22 output &&
grep "^-ADDED" output && test_grep "^-ADDED" output &&
grep "^+MODIFIED" output && test_grep "^+MODIFIED" output &&
grep "^+APPENDED" output test_grep "^+APPENDED" output
' '


test_done test_done

View File

@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' '
--merge Z --merge Z
--conflict=Z --conflict=Z
--patch Z --patch Z
--unified=Z
--inter-hunk-context=Z
--ignore-skip-worktree-bits Z --ignore-skip-worktree-bits Z
--ignore-other-worktrees Z --ignore-other-worktrees Z
--recurse-submodules Z --recurse-submodules Z