Merge branch 'jk/format-patch-ignore-noprefix'

"git format-patch" honors the src/dst prefixes set to nonstandard
values with configuration variables like "diff.noprefix", causing
receiving end of the patch that expects the standard -p1 format to
break.  Teach "format-patch" to ignore end-user configuration and
always use the standard prefixes.

This is a backward compatibility breaking change.

* jk/format-patch-ignore-noprefix:
  rebase: prefer --default-prefix to --{src,dst}-prefix for format-patch
  format-patch: add format.noprefix option
  format-patch: do not respect diff.noprefix
  diff: add --default-prefix option
  t4013: add tests for diff prefix options
  diff: factor out src/dst prefix setup
This commit is contained in:
Junio C Hamano 2023-03-21 14:18:55 -07:00
commit 15108de2fa
8 changed files with 118 additions and 6 deletions

View File

@ -144,3 +144,10 @@ will only show notes from `refs/notes/bar`.
format.mboxrd:: format.mboxrd::
A boolean value which enables the robust "mboxrd" format when A boolean value which enables the robust "mboxrd" format when
`--stdout` is in use to escape "^>+From " lines. `--stdout` is in use to escape "^>+From " lines.
format.noprefix::
If set, do not show any source or destination prefix in patches.
This is equivalent to the `diff.noprefix` option used by `git
diff` (but which is not respected by `format-patch`). Note that
by setting this, the receiver of any patches you generate will
have to apply them using the `-p0` option.

View File

@ -852,6 +852,11 @@ endif::git-format-patch[]
--no-prefix:: --no-prefix::
Do not show any source or destination prefix. Do not show any source or destination prefix.
--default-prefix::
Use the default source and destination prefixes ("a/" and "b/").
This is usually the default already, but may be used to override
config such as `diff.noprefix`.
--line-prefix=<prefix>:: --line-prefix=<prefix>::
Prepend an additional prefix to every line of output. Prepend an additional prefix to every line of output.

View File

@ -58,6 +58,7 @@ static int stdout_mboxrd;
static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_patch_subject_prefix = "PATCH";
static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT; static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
static const char *fmt_pretty; static const char *fmt_pretty;
static int format_no_prefix;
static const char * const builtin_log_usage[] = { static const char * const builtin_log_usage[] = {
N_("git log [<options>] [<revision-range>] [[--] <path>...]"), N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@ -1084,6 +1085,19 @@ static int git_format_config(const char *var, const char *value, void *cb)
stdout_mboxrd = git_config_bool(var, value); stdout_mboxrd = git_config_bool(var, value);
return 0; return 0;
} }
if (!strcmp(var, "format.noprefix")) {
format_no_prefix = 1;
return 0;
}
/*
* ignore some porcelain config which would otherwise be parsed by
* git_diff_ui_config(), via git_log_config(); we can't just avoid
* diff_ui_config completely, because we do care about some ui options
* like color.
*/
if (!strcmp(var, "diff.noprefix"))
return 0;
return git_log_config(var, value, cb); return git_log_config(var, value, cb);
} }
@ -1993,6 +2007,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD"; s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH; s_r_opt.revarg_opt = REVARG_COMMITTISH;
if (format_no_prefix)
diff_set_noprefix(&rev.diffopt);
if (default_attach) { if (default_attach) {
rev.mime_boundary = default_attach; rev.mime_boundary = default_attach;
rev.no_inline = 1; rev.no_inline = 1;

View File

@ -661,7 +661,7 @@ static int run_am(struct rebase_options *opts)
format_patch.git_cmd = 1; format_patch.git_cmd = 1;
strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout", strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only", "--full-index", "--cherry-pick", "--right-only",
"--src-prefix=a/", "--dst-prefix=b/", "--no-renames", "--default-prefix", "--no-renames",
"--no-cover-letter", "--pretty=mboxrd", "--topo-order", "--no-cover-letter", "--pretty=mboxrd", "--topo-order",
"--no-base", NULL); "--no-base", NULL);
if (opts->git_format_patch_opt.len) if (opts->git_format_patch_opt.len)

33
diff.c
View File

@ -3376,6 +3376,17 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
options->b_prefix = b; options->b_prefix = b;
} }
void diff_set_noprefix(struct diff_options *options)
{
options->a_prefix = options->b_prefix = "";
}
void diff_set_default_prefix(struct diff_options *options)
{
options->a_prefix = "a/";
options->b_prefix = "b/";
}
struct userdiff_driver *get_textconv(struct repository *r, struct userdiff_driver *get_textconv(struct repository *r,
struct diff_filespec *one) struct diff_filespec *one)
{ {
@ -4676,10 +4687,9 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->flags.ignore_untracked_in_submodules = 1; options->flags.ignore_untracked_in_submodules = 1;
if (diff_no_prefix) { if (diff_no_prefix) {
options->a_prefix = options->b_prefix = ""; diff_set_noprefix(options);
} else if (!diff_mnemonic_prefix) { } else if (!diff_mnemonic_prefix) {
options->a_prefix = "a/"; diff_set_default_prefix(options);
options->b_prefix = "b/";
} }
options->color_moved = diff_color_moved_default; options->color_moved = diff_color_moved_default;
@ -5263,8 +5273,18 @@ static int diff_opt_no_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset); BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg); BUG_ON_OPT_ARG(optarg);
options->a_prefix = ""; diff_set_noprefix(options);
options->b_prefix = ""; return 0;
}
static int diff_opt_default_prefix(const struct option *opt,
const char *optarg, int unset)
{
struct diff_options *options = opt->value;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
diff_set_default_prefix(options);
return 0; return 0;
} }
@ -5557,6 +5577,9 @@ struct option *add_diff_options(const struct option *opts,
OPT_CALLBACK_F(0, "no-prefix", options, NULL, OPT_CALLBACK_F(0, "no-prefix", options, NULL,
N_("do not show any source or destination prefix"), N_("do not show any source or destination prefix"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix), PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
OPT_CALLBACK_F(0, "default-prefix", options, NULL,
N_("use default prefixes a/ and b/"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix),
OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext, OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
N_("show context between diff hunks up to the specified number of lines"), N_("show context between diff hunks up to the specified number of lines"),
PARSE_OPT_NONEG), PARSE_OPT_NONEG),

2
diff.h
View File

@ -496,6 +496,8 @@ void diff_tree_combined(const struct object_id *oid, const struct oid_array *par
void diff_tree_combined_merge(const struct commit *commit, struct rev_info *rev); void diff_tree_combined_merge(const struct commit *commit, struct rev_info *rev);
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b); void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
void diff_set_noprefix(struct diff_options *options);
void diff_set_default_prefix(struct diff_options *options);
int diff_can_quit_early(struct diff_options *); int diff_can_quit_early(struct diff_options *);

View File

@ -616,4 +616,46 @@ test_expect_success 'diff -I<regex>: detect malformed regex' '
test_i18ngrep "invalid regex given to -I: " error test_i18ngrep "invalid regex given to -I: " error
' '
# check_prefix <patch> <src> <dst>
# check only lines with paths to avoid dependency on exact oid/contents
check_prefix () {
grep -E '^(diff|---|\+\+\+) ' "$1" >actual.paths &&
cat >expect <<-EOF &&
diff --git $2 $3
--- $2
+++ $3
EOF
test_cmp expect actual.paths
}
test_expect_success 'diff-files does not respect diff.noprefix' '
git -c diff.noprefix diff-files -p >actual &&
check_prefix actual a/file0 b/file0
'
test_expect_success 'diff-files respects --no-prefix' '
git diff-files -p --no-prefix >actual &&
check_prefix actual file0 file0
'
test_expect_success 'diff respects diff.noprefix' '
git -c diff.noprefix diff >actual &&
check_prefix actual file0 file0
'
test_expect_success 'diff --default-prefix overrides diff.noprefix' '
git -c diff.noprefix diff --default-prefix >actual &&
check_prefix actual a/file0 b/file0
'
test_expect_success 'diff respects diff.mnemonicprefix' '
git -c diff.mnemonicprefix diff >actual &&
check_prefix actual i/file0 w/file0
'
test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
git -c diff.mnemonicprefix diff --default-prefix >actual &&
check_prefix actual a/file0 b/file0
'
test_done test_done

View File

@ -2396,4 +2396,20 @@ test_expect_success 'interdiff: solo-patch' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'format-patch does not respect diff.noprefix' '
git -c diff.noprefix format-patch -1 --stdout >actual &&
grep "^--- a/blorp" actual
'
test_expect_success 'format-patch respects format.noprefix' '
git -c format.noprefix format-patch -1 --stdout >actual &&
grep "^--- blorp" actual
'
test_expect_success 'format-patch --default-prefix overrides format.noprefix' '
git -c format.noprefix \
format-patch -1 --default-prefix --stdout >actual &&
grep "^--- a/blorp" actual
'
test_done test_done