Git fork

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

+324 -83
+10
Documentation/diff-context-options.adoc
··· 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.
+2
Documentation/git-add.adoc
··· 104 104 initial command menu and directly jumps to the `patch` subcommand. 105 105 See ``Interactive mode'' for details. 106 106 107 + include::diff-context-options.adoc[] 108 + 107 109 `-e`:: 108 110 `--edit`:: 109 111 Open the diff vs. the index in an editor and let the user
+2
Documentation/git-checkout.adoc
··· 289 289 Note that this option uses the no overlay mode by default (see also 290 290 `--overlay`), and currently doesn't support overlay mode. 291 291 292 + include::diff-context-options.adoc[] 293 + 292 294 `--ignore-other-worktrees`:: 293 295 `git checkout` refuses when the wanted branch is already checked 294 296 out or otherwise in use by another worktree. This option makes
+2
Documentation/git-commit.adoc
··· 76 76 which changes to commit. See linkgit:git-add[1] for 77 77 details. 78 78 79 + include::diff-context-options.adoc[] 80 + 79 81 `-C <commit>`:: 80 82 `--reuse-message=<commit>`:: 81 83 Take an existing _<commit>_ object, and reuse the log message
+2
Documentation/git-reset.adoc
··· 125 125 separated with _NUL_ character and all other characters are taken 126 126 literally (including newlines and quotes). 127 127 128 + include::diff-context-options.adoc[] 129 + 128 130 `--`:: 129 131 Do not interpret any more arguments as options. 130 132
+2
Documentation/git-restore.adoc
··· 50 50 Mode" section of linkgit:git-add[1] to learn how to operate 51 51 the `--patch` mode. 52 52 53 + include::diff-context-options.adoc[] 54 + 53 55 `-W`:: 54 56 `--worktree`:: 55 57 `-S`::
+2
Documentation/git-stash.adoc
··· 222 222 The `--patch` option implies `--keep-index`. You can use 223 223 `--no-keep-index` to override this. 224 224 225 + include::diff-context-options.adoc[] 226 + 225 227 -S:: 226 228 --staged:: 227 229 This option is only valid for `push` and `save` commands.
+37 -8
add-interactive.c
··· 36 36 free(key); 37 37 } 38 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) 40 41 { 41 42 const char *value; 42 43 43 44 s->r = r; 45 + s->context = -1; 46 + s->interhunkcontext = -1; 44 47 45 48 if (repo_config_get_value(r, "color.interactive", &value)) 46 49 s->use_color = -1; ··· 78 81 repo_config_get_string(r, "diff.algorithm", 79 82 &s->interactive_diff_algorithm); 80 83 84 + if (!repo_config_get_int(r, "diff.context", &s->context)) 85 + if (s->context < 0) 86 + die(_("%s cannot be negative"), "diff.context"); 87 + if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext)) 88 + if (s->interhunkcontext < 0) 89 + die(_("%s cannot be negative"), "diff.interHunkContext"); 90 + 81 91 repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); 82 92 if (s->use_single_key) 83 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 + } 84 105 } 85 106 86 107 void clear_add_i_state(struct add_i_state *s) ··· 969 990 opts->prompt = N_("Patch update"); 970 991 count = list_and_choose(s, files, opts); 971 992 if (count > 0) { 993 + struct add_p_opt add_p_opt = { 994 + .context = s->context, 995 + .interhunkcontext = s->interhunkcontext, 996 + }; 972 997 struct strvec args = STRVEC_INIT; 973 998 struct pathspec ps_selected = { 0 }; 974 999 ··· 979 1004 parse_pathspec(&ps_selected, 980 1005 PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, 981 1006 PATHSPEC_LITERAL_PATH, "", args.v); 982 - 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); 983 1008 strvec_clear(&args); 984 1009 clear_pathspec(&ps_selected); 985 1010 } ··· 1014 1039 if (count > 0) { 1015 1040 struct child_process cmd = CHILD_PROCESS_INIT; 1016 1041 1017 - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", 1018 - oid_to_hex(!is_initial ? &oid : 1019 - s->r->hash_algo->empty_tree), 1020 - "--", 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); 1021 1049 for (i = 0; i < files->items.nr; i++) 1022 1050 if (files->selected[i]) 1023 1051 strvec_push(&cmd.args, ··· 1110 1138 _("(empty) select nothing")); 1111 1139 } 1112 1140 1113 - 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) 1114 1143 { 1115 1144 struct add_i_state s = { NULL }; 1116 1145 struct print_command_item_data data = { "[", "]" }; ··· 1153 1182 ->util = util; 1154 1183 } 1155 1184 1156 - init_add_i_state(&s, r); 1185 + init_add_i_state(&s, r, add_p_opt); 1157 1186 1158 1187 /* 1159 1188 * When color was asked for, use the prompt color for
+14 -3
add-interactive.h
··· 3 3 4 4 #include "color.h" 5 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 + 6 13 struct add_i_state { 7 14 struct repository *r; 8 15 int use_color; ··· 18 25 19 26 int use_single_key; 20 27 char *interactive_diff_filter, *interactive_diff_algorithm; 28 + int context, interhunkcontext; 21 29 }; 22 30 23 - 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); 24 33 void clear_add_i_state(struct add_i_state *s); 25 34 26 35 struct repository; 27 36 struct pathspec; 28 - 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); 29 39 30 40 enum add_p_mode { 31 41 ADD_P_ADD, ··· 36 46 }; 37 47 38 48 int run_add_p(struct repository *r, enum add_p_mode mode, 39 - const char *revision, const struct pathspec *ps); 49 + struct add_p_opt *o, const char *revision, 50 + const struct pathspec *ps); 40 51 41 52 #endif
+9 -5
add-patch.c
··· 414 414 static int parse_diff(struct add_p_state *s, const struct pathspec *ps) 415 415 { 416 416 struct strvec args = STRVEC_INIT; 417 - const char *diff_algorithm = s->s.interactive_diff_algorithm; 418 417 struct strbuf *plain = &s->plain, *colored = NULL; 419 418 struct child_process cp = CHILD_PROCESS_INIT; 420 419 char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; ··· 424 423 int res; 425 424 426 425 strvec_pushv(&args, s->mode->diff_cmd); 427 - if (diff_algorithm) 428 - strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); 426 + if (s->s.context != -1) 427 + strvec_pushf(&args, "--unified=%i", s->s.context); 428 + if (s->s.interhunkcontext != -1) 429 + strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext); 430 + if (s->s.interactive_diff_algorithm) 431 + strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm); 429 432 if (s->revision) { 430 433 struct object_id oid; 431 434 strvec_push(&args, ··· 1760 1763 } 1761 1764 1762 1765 int run_add_p(struct repository *r, enum add_p_mode mode, 1763 - const char *revision, const struct pathspec *ps) 1766 + struct add_p_opt *o, const char *revision, 1767 + const struct pathspec *ps) 1764 1768 { 1765 1769 struct add_p_state s = { 1766 1770 { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT 1767 1771 }; 1768 1772 size_t i, binary_count = 0; 1769 1773 1770 - init_add_i_state(&s.s, r); 1774 + init_add_i_state(&s.s, r, o); 1771 1775 1772 1776 if (mode == ADD_P_STASH) 1773 1777 s.mode = &patch_mode_stash;
+17 -4
builtin/add.c
··· 30 30 NULL 31 31 }; 32 32 static int patch_interactive, add_interactive, edit_interactive; 33 + static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 33 34 static int take_worktree_changes; 34 35 static int add_renormalize; 35 36 static int pathspec_file_nul; ··· 158 159 int interactive_add(struct repository *repo, 159 160 const char **argv, 160 161 const char *prefix, 161 - int patch) 162 + int patch, struct add_p_opt *add_p_opt) 162 163 { 163 164 struct pathspec pathspec; 164 165 int ret; ··· 170 171 prefix, argv); 171 172 172 173 if (patch) 173 - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); 174 + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); 174 175 else 175 - ret = !!run_add_i(repo, &pathspec); 176 + ret = !!run_add_i(repo, &pathspec, add_p_opt); 176 177 177 178 clear_pathspec(&pathspec); 178 179 return ret; ··· 254 255 OPT_GROUP(""), 255 256 OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), 256 257 OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), 258 + OPT_DIFF_UNIFIED(&add_p_opt.context), 259 + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 257 260 OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), 258 261 OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), 259 262 OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), ··· 395 398 prepare_repo_settings(repo); 396 399 repo->settings.command_requires_full_index = 0; 397 400 401 + if (add_p_opt.context < -1) 402 + die(_("'%s' cannot be negative"), "--unified"); 403 + if (add_p_opt.interhunkcontext < -1) 404 + die(_("'%s' cannot be negative"), "--inter-hunk-context"); 405 + 398 406 if (patch_interactive) 399 407 add_interactive = 1; 400 408 if (add_interactive) { ··· 402 410 die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); 403 411 if (pathspec_from_file) 404 412 die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); 405 - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); 413 + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); 414 + } else { 415 + if (add_p_opt.context != -1) 416 + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); 417 + if (add_p_opt.interhunkcontext != -1) 418 + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); 406 419 } 407 420 408 421 if (edit_interactive) {
+28 -3
builtin/checkout.c
··· 61 61 62 62 struct checkout_opts { 63 63 int patch_mode; 64 + int patch_context; 65 + int patch_interhunk_context; 64 66 int quiet; 65 67 int merge; 66 68 int force; ··· 104 106 struct tree *source_tree; 105 107 }; 106 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 + } 108 115 109 116 struct branch_info { 110 117 char *name; /* The short name used */ ··· 539 546 540 547 if (opts->patch_mode) { 541 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 + }; 542 553 const char *rev = new_branch_info->name; 543 554 char rev_oid[GIT_MAX_HEXSZ + 1]; 544 555 ··· 564 575 else 565 576 BUG("either flag must have been set, worktree=%d, index=%d", 566 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); 569 580 } 570 581 571 582 repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); ··· 1738 1749 N_("checkout their version for unmerged files"), 1739 1750 3, PARSE_OPT_NONEG), 1740 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), 1741 1754 OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree, 1742 1755 N_("do not limit pathspecs to sparse entries only")), 1743 1756 OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), ··· 1779 1792 1780 1793 argc = parse_options(argc, argv, prefix, options, 1781 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 + } 1782 1807 1783 1808 if (opts->show_progress < 0) { 1784 1809 if (opts->quiet)
+15 -1
builtin/commit.c
··· 19 19 #include "environment.h" 20 20 #include "diff.h" 21 21 #include "commit.h" 22 + #include "add-interactive.h" 22 23 #include "gettext.h" 23 24 #include "revision.h" 24 25 #include "wt-status.h" ··· 122 123 static char *fixup_message, *fixup_commit, *squash_message; 123 124 static const char *fixup_prefix; 124 125 static int all, also, interactive, patch_interactive, only, amend, signoff; 126 + static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 125 127 static int edit_flag = -1; /* unspecified */ 126 128 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; 127 129 static int config_commit_verbose = -1; /* unspecified */ ··· 354 356 const char *ret; 355 357 char *path = NULL; 356 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 + 357 364 if (is_status) 358 365 refresh_flags |= REFRESH_UNMERGED; 359 366 parse_pathspec(&pathspec, 0, ··· 400 407 old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); 401 408 setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); 402 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) 404 411 die(_("interactive add failed")); 405 412 406 413 the_repository->index_file = old_repo_index_file; ··· 424 431 commit_style = COMMIT_NORMAL; 425 432 ret = get_lock_file_path(&index_lock); 426 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"); 427 439 } 428 440 429 441 /* ··· 1726 1738 OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), 1727 1739 OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), 1728 1740 OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), 1741 + OPT_DIFF_UNIFIED(&add_p_opt.context), 1742 + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 1729 1743 OPT_BOOL('o', "only", &only, N_("commit only specified files")), 1730 1744 OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), 1731 1745 OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")),
+15 -2
builtin/reset.c
··· 346 346 struct object_id oid; 347 347 struct pathspec pathspec; 348 348 int intent_to_add = 0; 349 + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 349 350 const struct option options[] = { 350 351 OPT__QUIET(&quiet, N_("be quiet, only report errors")), 351 352 OPT_BOOL(0, "no-refresh", &no_refresh, ··· 370 371 PARSE_OPT_OPTARG, 371 372 option_parse_recurse_submodules_worktree_updater), 372 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), 373 376 OPT_BOOL('N', "intent-to-add", &intent_to_add, 374 377 N_("record only the fact that removed paths will be added later")), 375 378 OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), ··· 420 423 oidcpy(&oid, &tree->object.oid); 421 424 } 422 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 + 423 431 prepare_repo_settings(the_repository); 424 432 the_repository->settings.command_requires_full_index = 0; 425 433 ··· 427 435 if (reset_type != NONE) 428 436 die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); 429 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); 432 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"); 433 446 } 434 447 435 448 /* git reset tree [--] paths... can be used to
+45 -11
builtin/stash.c
··· 1301 1301 } 1302 1302 1303 1303 static int stash_patch(struct stash_info *info, const struct pathspec *ps, 1304 - struct strbuf *out_patch, int quiet) 1304 + struct strbuf *out_patch, int quiet, 1305 + struct add_p_opt *add_p_opt) 1305 1306 { 1306 1307 int ret = 0; 1307 1308 struct child_process cp_read_tree = CHILD_PROCESS_INIT; ··· 1326 1327 old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); 1327 1328 setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); 1328 1329 1329 - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); 1330 + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); 1330 1331 1331 1332 the_repository->index_file = old_repo_index_file; 1332 1333 if (old_index_env && *old_index_env) ··· 1421 1422 } 1422 1423 1423 1424 static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, 1424 - int include_untracked, int patch_mode, int only_staged, 1425 - struct stash_info *info, struct strbuf *patch, 1425 + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, 1426 + int only_staged, struct stash_info *info, struct strbuf *patch, 1426 1427 int quiet) 1427 1428 { 1428 1429 int ret = 0; ··· 1503 1504 untracked_commit_option = 1; 1504 1505 } 1505 1506 if (patch_mode) { 1506 - ret = stash_patch(info, ps, patch, quiet); 1507 + ret = stash_patch(info, ps, patch, quiet, add_p_opt); 1507 1508 if (ret < 0) { 1508 1509 if (!quiet) 1509 1510 fprintf_ln(stderr, _("Cannot save the current " ··· 1578 1579 if (!check_changes_tracked_files(&ps)) 1579 1580 return 0; 1580 1581 1581 - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, 1582 + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, 1582 1583 NULL, 0); 1583 1584 if (!ret) 1584 1585 printf_ln("%s", oid_to_hex(&info.w_commit)); ··· 1589 1590 } 1590 1591 1591 1592 static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, 1592 - int keep_index, int patch_mode, int include_untracked, int only_staged) 1593 + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, 1594 + int include_untracked, int only_staged) 1593 1595 { 1594 1596 int ret = 0; 1595 1597 struct stash_info info = STASH_INFO_INIT; ··· 1659 1661 1660 1662 if (stash_msg) 1661 1663 strbuf_addstr(&stash_msg_buf, stash_msg); 1662 - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, 1663 - &info, &patch, quiet)) { 1664 + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, 1665 + add_p_opt, only_staged, &info, &patch, quiet)) { 1664 1666 ret = -1; 1665 1667 goto done; 1666 1668 } ··· 1833 1835 const char *stash_msg = NULL; 1834 1836 char *pathspec_from_file = NULL; 1835 1837 struct pathspec ps; 1838 + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 1836 1839 struct option options[] = { 1837 1840 OPT_BOOL('k', "keep-index", &keep_index, 1838 1841 N_("keep index")), ··· 1840 1843 N_("stash staged changes only")), 1841 1844 OPT_BOOL('p', "patch", &patch_mode, 1842 1845 N_("stash in patch mode")), 1846 + OPT_DIFF_UNIFIED(&add_p_opt.context), 1847 + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 1843 1848 OPT__QUIET(&quiet, N_("quiet mode")), 1844 1849 OPT_BOOL('u', "include-untracked", &include_untracked, 1845 1850 N_("include untracked files in stash")), ··· 1895 1900 die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); 1896 1901 } 1897 1902 1903 + if (!patch_mode) { 1904 + if (add_p_opt.context != -1) 1905 + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); 1906 + if (add_p_opt.interhunkcontext != -1) 1907 + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); 1908 + } 1909 + 1910 + if (add_p_opt.context < -1) 1911 + die(_("'%s' cannot be negative"), "--unified"); 1912 + if (add_p_opt.interhunkcontext < -1) 1913 + die(_("'%s' cannot be negative"), "--inter-hunk-context"); 1914 + 1898 1915 ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, 1899 - include_untracked, only_staged); 1916 + &add_p_opt, include_untracked, only_staged); 1900 1917 1901 1918 clear_pathspec(&ps); 1902 1919 free(pathspec_from_file); ··· 1921 1938 const char *stash_msg = NULL; 1922 1939 struct pathspec ps; 1923 1940 struct strbuf stash_msg_buf = STRBUF_INIT; 1941 + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 1924 1942 struct option options[] = { 1925 1943 OPT_BOOL('k', "keep-index", &keep_index, 1926 1944 N_("keep index")), ··· 1928 1946 N_("stash staged changes only")), 1929 1947 OPT_BOOL('p', "patch", &patch_mode, 1930 1948 N_("stash in patch mode")), 1949 + OPT_DIFF_UNIFIED(&add_p_opt.context), 1950 + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 1931 1951 OPT__QUIET(&quiet, N_("quiet mode")), 1932 1952 OPT_BOOL('u', "include-untracked", &include_untracked, 1933 1953 N_("include untracked files in stash")), ··· 1946 1966 stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); 1947 1967 1948 1968 memset(&ps, 0, sizeof(ps)); 1969 + 1970 + if (add_p_opt.context < -1) 1971 + die(_("'%s' cannot be negative"), "--unified"); 1972 + if (add_p_opt.interhunkcontext < -1) 1973 + die(_("'%s' cannot be negative"), "--inter-hunk-context"); 1974 + 1975 + if (!patch_mode) { 1976 + if (add_p_opt.context != -1) 1977 + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); 1978 + if (add_p_opt.interhunkcontext != -1) 1979 + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); 1980 + } 1981 + 1949 1982 ret = do_push_stash(&ps, stash_msg, quiet, keep_index, 1950 - patch_mode, include_untracked, only_staged); 1983 + patch_mode, &add_p_opt, include_untracked, 1984 + only_staged); 1951 1985 1952 1986 strbuf_release(&stash_msg_buf); 1953 1987 return ret;
+2 -1
commit.h
··· 2 2 #define COMMIT_H 3 3 4 4 #include "object.h" 5 + #include "add-interactive.h" 5 6 6 7 struct signature_check; 7 8 struct strbuf; ··· 257 258 int interactive_add(struct repository *repo, 258 259 const char **argv, 259 260 const char *prefix, 260 - int patch); 261 + int patch, struct add_p_opt *add_p_opt); 261 262 262 263 struct commit_extra_header { 263 264 struct commit_extra_header *next;
+2
parse-options.h
··· 623 623 #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file")) 624 624 #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")) 625 625 #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) 626 + #define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) 627 + #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) 626 628 627 629 #define OPT_IPVERSION(v) \ 628 630 OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \
+95 -24
t/t3701-add-interactive.sh
··· 63 63 ' 64 64 test_expect_success 'status works (initial)' ' 65 65 git add -i </dev/null >output && 66 - grep "+1/-0 *+2/-0 file" output 66 + test_grep "+1/-0 *+2/-0 file" output 67 67 ' 68 68 69 69 test_expect_success 'setup expected' ' ··· 86 86 git add file && 87 87 test_write_lines r 1 | git add -i && 88 88 git ls-files >output && 89 - ! grep . output 89 + test_grep ! . output 90 90 ' 91 91 92 92 test_expect_success 'add untracked (multiple)' ' ··· 109 109 ' 110 110 test_expect_success 'status works (commit)' ' 111 111 git add -i </dev/null >output && 112 - grep "+1/-0 *+2/-0 file" output 112 + test_grep "+1/-0 *+2/-0 file" output 113 113 ' 114 114 115 115 test_expect_success 'update can stage deletions' ' ··· 141 141 git add file && 142 142 test_write_lines r 1 | git add -i && 143 143 git add -i </dev/null >output && 144 - grep "unchanged *+3/-0 file" output 144 + test_grep "unchanged *+3/-0 file" output 145 145 ' 146 146 147 147 test_expect_success 'reject multi-key input' ' ··· 185 185 test_expect_success 'bad edit rejected' ' 186 186 git reset && 187 187 test_write_lines e n d | git add -p >output && 188 - grep "hunk does not apply" output 188 + test_grep "hunk does not apply" output 189 189 ' 190 190 191 191 test_expect_success 'setup patch' ' ··· 198 198 test_expect_success 'garbage edit rejected' ' 199 199 git reset && 200 200 test_write_lines e n d | git add -p >output && 201 - grep "hunk does not apply" output 201 + test_grep "hunk does not apply" output 202 202 ' 203 203 204 204 test_expect_success 'setup patch' ' ··· 313 313 chmod +x file && 314 314 printf "y\\ny\\n" | git add -p && 315 315 git diff --cached file >out && 316 - grep "new mode" out && 317 - grep "+content" out && 316 + test_grep "new mode" out && 317 + test_grep "+content" out && 318 318 git diff file >out && 319 319 test_must_be_empty out 320 320 ' ··· 636 636 printf "%s\n" s e q n q q | 637 637 EDITOR=: git add -p && 638 638 git diff >actual && 639 - ! grep "^+15" actual 639 + test_grep ! "^+15" actual 640 640 ' 641 641 642 642 test_expect_success 'split hunk "add -p (no, yes, edit)"' ' ··· 648 648 EDITOR=: git add -p 2>error && 649 649 test_must_be_empty error && 650 650 git diff >actual && 651 - ! grep "^+31" actual 651 + test_grep ! "^+31" actual 652 652 ' 653 653 654 654 test_expect_success 'split hunk with incomplete line at end' ' ··· 682 682 EDITOR=./fake_editor.sh git add -p 2>error && 683 683 test_must_be_empty error && 684 684 git diff --cached >actual && 685 - grep "^+22" actual 685 + test_grep "^+22" actual 686 686 ' 687 687 688 688 test_expect_success 'patch mode ignores unmerged entries' ' ··· 696 696 test_must_fail git merge side && 697 697 echo changed >non-conflict.t && 698 698 echo y | git add -p >output && 699 - ! grep a/conflict.t output && 699 + test_grep ! a/conflict.t output && 700 700 cat >expected <<-\EOF && 701 701 * Unmerged path conflict.t 702 702 diff --git a/non-conflict.t b/non-conflict.t ··· 728 728 729 729 # We do not want to depend on the exact coloring scheme 730 730 # git uses for diffs, so just check that we saw some kind of color. 731 - grep "$(printf "\\033")" output 731 + test_grep "$(printf "\\033")" output 732 732 ' 733 733 734 734 test_expect_success 'colors can be overridden' ' ··· 743 743 -c color.interactive.error=blue \ 744 744 add -i 2>err.raw <input && 745 745 test_decode_color <err.raw >err && 746 - grep "<BLUE>Huh (trigger)?<RESET>" err && 746 + test_grep "<BLUE>Huh (trigger)?<RESET>" err && 747 747 748 748 test_write_lines help quit >input && 749 749 force_color git \ ··· 863 863 printf y >y && 864 864 force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && 865 865 test_decode_color <output.raw >output && 866 - grep "old<" output 866 + test_grep "old<" output 867 867 ' 868 868 869 869 test_expect_success 'diffFilter filters diff' ' ··· 876 876 877 877 # avoid depending on the exact coloring or content of the prompts, 878 878 # and just make sure we saw our diff prefixed 879 - grep foo:.*content output 879 + test_grep foo:.*content output 880 880 ' 881 881 882 882 test_expect_success 'detect bogus diffFilter output' ' ··· 886 886 test_config interactive.diffFilter "sed 6d" && 887 887 printf y >y && 888 888 force_color test_must_fail git add -p <y >output 2>&1 && 889 - grep "mismatched output" output 889 + test_grep "mismatched output" output 890 890 ' 891 891 892 892 test_expect_success 'handle iffy colored hunk headers' ' ··· 896 896 printf n >n && 897 897 force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ 898 898 add -p >output 2>&1 <n && 899 - grep "^XX$" output 899 + test_grep "^XX$" output 900 900 ' 901 901 902 902 test_expect_success 'handle very large filtered diff' ' ··· 1002 1002 # update it, but we want to be sure that our "." pathspec 1003 1003 # was not expanded into the argument list of any command. 1004 1004 # So look only for "not-changed". 1005 - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out 1005 + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out 1006 1006 ' 1007 1007 1008 1008 test_expect_success 'hunk-editing handles custom comment char' ' ··· 1072 1072 1073 1073 test_expect_success 'status ignores dirty submodules (except HEAD)' ' 1074 1074 git -C for-submodules add -i </dev/null >output && 1075 - grep dirty-head output && 1076 - grep dirty-both-ways output && 1077 - ! grep dirty-otherwise output 1075 + test_grep dirty-head output && 1076 + test_grep dirty-both-ways output && 1077 + test_grep ! dirty-otherwise output 1078 1078 ' 1079 1079 1080 1080 test_expect_success 'handle submodules' ' 1081 1081 echo 123 >>for-submodules/dirty-otherwise/initial.t && 1082 1082 1083 1083 force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && 1084 - grep "No changes" output && 1084 + test_grep "No changes" output && 1085 1085 1086 1086 force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && 1087 1087 git -C for-submodules ls-files --stage dirty-head >actual && 1088 1088 rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && 1089 - grep "$rev" actual 1089 + test_grep "$rev" actual 1090 1090 ' 1091 1091 1092 1092 test_expect_success 'set up pathological context' ' ··· 1229 1229 test_write_lines a b "" c D "" e G "" >expect && 1230 1230 test_cmp expect actual 1231 1231 ' 1232 + 1233 + test_expect_success 'add -p respects diff.context' ' 1234 + test_write_lines a b c d e f g h i j k l m >file && 1235 + git add file && 1236 + test_write_lines a b c d e f G h i j k l m >file && 1237 + echo y | git -c diff.context=5 add -p >actual && 1238 + test_grep "@@ -2,11 +2,11 @@" actual 1239 + ' 1240 + 1241 + test_expect_success 'add -p respects diff.interHunkContext' ' 1242 + test_write_lines a b c d e f g h i j k l m n o p q r s >file && 1243 + git add file && 1244 + test_write_lines a b c d E f g i i j k l m N o p q r s >file && 1245 + echo y | git -c diff.interhunkcontext=2 add -p >actual && 1246 + test_grep "@@ -2,16 +2,16 @@" actual 1247 + ' 1248 + 1249 + test_expect_success 'add -p rejects negative diff.context' ' 1250 + test_config diff.context -1 && 1251 + test_must_fail git add -p 2>output && 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 1232 1303 1233 1304 test_done
+21 -21
t/t4055-diff-context.sh
··· 38 38 39 39 test_expect_success 'the default number of context lines is 3' ' 40 40 git diff >output && 41 - ! grep "^ d" output && 42 - grep "^ e" output && 43 - grep "^ j" output && 44 - ! grep "^ k" output 41 + test_grep ! "^ d" output && 42 + test_grep "^ e" output && 43 + test_grep "^ j" output && 44 + test_grep ! "^ k" output 45 45 ' 46 46 47 47 test_expect_success 'diff.context honored by "log"' ' 48 48 git log -1 -p >output && 49 - ! grep firstline output && 50 - git config diff.context 8 && 49 + test_grep ! firstline output && 50 + test_config diff.context 8 && 51 51 git log -1 -p >output && 52 - grep "^ firstline" output 52 + test_grep "^ firstline" output 53 53 ' 54 54 55 55 test_expect_success 'The -U option overrides diff.context' ' 56 - git config diff.context 8 && 56 + test_config diff.context 8 && 57 57 git log -U4 -1 >output && 58 - ! grep "^ firstline" output 58 + test_grep ! "^ firstline" output 59 59 ' 60 60 61 61 test_expect_success 'diff.context honored by "diff"' ' 62 - git config diff.context 8 && 62 + test_config diff.context 8 && 63 63 git diff >output && 64 - grep "^ firstline" output 64 + test_grep "^ firstline" output 65 65 ' 66 66 67 67 test_expect_success 'plumbing not affected' ' 68 - git config diff.context 8 && 68 + test_config diff.context 8 && 69 69 git diff-files -p >output && 70 - ! grep "^ firstline" output 70 + test_grep ! "^ firstline" output 71 71 ' 72 72 73 73 test_expect_success 'non-integer config parsing' ' 74 - git config diff.context no && 74 + test_config diff.context no && 75 75 test_must_fail git diff 2>output && 76 76 test_grep "bad numeric config value" output 77 77 ' 78 78 79 79 test_expect_success 'negative integer config parsing' ' 80 - git config diff.context -1 && 80 + test_config diff.context -1 && 81 81 test_must_fail git diff 2>output && 82 82 test_grep "bad config variable" output 83 83 ' 84 84 85 85 test_expect_success '-U0 is valid, so is diff.context=0' ' 86 - git config diff.context 0 && 86 + test_config diff.context 0 && 87 87 git diff >output && 88 - grep "^-ADDED" output && 89 - grep "^+MODIFIED" output 88 + test_grep "^-ADDED" output && 89 + test_grep "^+MODIFIED" output 90 90 ' 91 91 92 92 test_expect_success '-U2147483647 works' ' ··· 94 94 test_line_count = 16 x && 95 95 git diff -U2147483647 >output && 96 96 test_line_count = 22 output && 97 - grep "^-ADDED" output && 98 - grep "^+MODIFIED" output && 99 - grep "^+APPENDED" output 97 + test_grep "^-ADDED" output && 98 + test_grep "^+MODIFIED" output && 99 + test_grep "^+APPENDED" output 100 100 ' 101 101 102 102 test_done
+2
t/t9902-completion.sh
··· 2596 2596 --merge Z 2597 2597 --conflict=Z 2598 2598 --patch Z 2599 + --unified=Z 2600 + --inter-hunk-context=Z 2599 2601 --ignore-skip-worktree-bits Z 2600 2602 --ignore-other-worktrees Z 2601 2603 --recurse-submodules Z