summaryrefslogtreecommitdiff
path: root/diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'diff.c')
-rw-r--r--diff.c250
1 files changed, 146 insertions, 104 deletions
diff --git a/diff.c b/diff.c
index 7e88d7e27a..9b14543e6e 100644
--- a/diff.c
+++ b/diff.c
@@ -264,7 +264,8 @@ void init_diff_ui_defaults(void)
diff_detect_rename_default = DIFF_DETECT_RENAME;
}
-int git_diff_heuristic_config(const char *var, const char *value, void *cb)
+int git_diff_heuristic_config(const char *var, const char *value,
+ void *cb UNUSED)
{
if (!strcmp(var, "diff.indentheuristic"))
diff_indent_heuristic = git_config_bool(var, value);
@@ -603,7 +604,7 @@ static unsigned long diff_filespec_size(struct repository *r,
return one->size;
}
-static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+static int count_trailing_blank(mmfile_t *mf)
{
char *ptr = mf->ptr;
long size = mf->size;
@@ -621,7 +622,7 @@ static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
if (*prev_eol == '\n')
break;
- if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+ if (!ws_blank_line(prev_eol + 1, ptr - prev_eol))
break;
cnt++;
ptr = prev_eol - 1;
@@ -633,9 +634,8 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
struct emit_callback *ecbdata)
{
int l1, l2, at;
- unsigned ws_rule = ecbdata->ws_rule;
- l1 = count_trailing_blank(mf1, ws_rule);
- l2 = count_trailing_blank(mf2, ws_rule);
+ l1 = count_trailing_blank(mf1);
+ l2 = count_trailing_blank(mf2);
if (l2 <= l1) {
ecbdata->blank_at_eof_in_preimage = 0;
ecbdata->blank_at_eof_in_postimage = 0;
@@ -916,7 +916,7 @@ struct interned_diff_symbol {
static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *keydata)
+ const void *keydata UNUSED)
{
const struct diff_options *diffopt = hashmap_cmp_fn_data;
const struct emitted_diff_symbol *a, *b;
@@ -1289,7 +1289,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
{
static const char *nneof = " No newline at end of file\n";
const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
- struct strbuf sb = STRBUF_INIT;
enum diff_symbol s = eds->s;
const char *line = eds->line;
@@ -1521,7 +1520,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
default:
BUG("unknown diff symbol");
}
- strbuf_release(&sb);
}
static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
@@ -1584,7 +1582,7 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line
ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
return 0;
- return ws_blank_line(line, len, ecbdata->ws_rule);
+ return ws_blank_line(line, len);
}
static void emit_add_line(struct emit_callback *ecbdata,
@@ -1956,7 +1954,7 @@ static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
static void fn_out_diff_words_aux(void *priv,
long minus_first, long minus_len,
long plus_first, long plus_len,
- const char *func, long funclen)
+ const char *func UNUSED, long funclen UNUSED)
{
struct diff_words_data *diff_words = priv;
struct diff_words_style *style = diff_words->style;
@@ -2489,6 +2487,9 @@ static int diffstat_consume(void *priv, char *line, unsigned long len)
struct diffstat_t *diffstat = priv;
struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
+ if (!len)
+ BUG("xdiff fed us an empty line");
+
if (line[0] == '+')
x->added++;
else if (line[0] == '-')
@@ -2675,6 +2676,11 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
* making the line longer than the maximum width.
*/
+ /*
+ * NEEDSWORK: line_prefix is often used for "log --graph" output
+ * and contains ANSI-colored string. utf8_strnwidth() should be
+ * used to correctly count the display width instead of strlen().
+ */
if (options->stat_width == -1)
width = term_columns() - strlen(line_prefix);
else
@@ -2750,6 +2756,16 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
char *slash;
prefix = "...";
len -= 3;
+ /*
+ * NEEDSWORK: (name_len - len) counts the display
+ * width, which would be shorter than the byte
+ * length of the corresponding substring.
+ * Advancing "name" by that number of bytes does
+ * *NOT* skip over that many columns, so it is
+ * very likely that chomping the pathname at the
+ * slash we will find starting from "name" will
+ * leave the resulting string still too long.
+ */
name += name_len - len;
slash = strchr(name, '/');
if (slash)
@@ -3168,8 +3184,9 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l
}
static void checkdiff_consume_hunk(void *priv,
- long ob, long on, long nb, long nn,
- const char *func, long funclen)
+ long ob UNUSED, long on UNUSED,
+ long nb, long nn UNUSED,
+ const char *func UNUSED, long funclen UNUSED)
{
struct checkdiff_t *data = priv;
@@ -3367,23 +3384,23 @@ struct userdiff_driver *get_textconv(struct repository *r,
return userdiff_get_textconv(r, one->driver);
}
-static struct strbuf *additional_headers(struct diff_options *o,
- const char *path)
+static struct string_list *additional_headers(struct diff_options *o,
+ const char *path)
{
if (!o->additional_path_headers)
return NULL;
return strmap_get(o->additional_path_headers, path);
}
-static void add_formatted_headers(struct strbuf *msg,
- struct strbuf *more_headers,
+static void add_formatted_header(struct strbuf *msg,
+ const char *header,
const char *line_prefix,
const char *meta,
const char *reset)
{
- char *next, *newline;
+ const char *next, *newline;
- for (next = more_headers->buf; *next; next = newline) {
+ for (next = header; *next; next = newline) {
newline = strchrnul(next, '\n');
strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta,
(int)(newline - next), next, reset);
@@ -3392,6 +3409,34 @@ static void add_formatted_headers(struct strbuf *msg,
}
}
+static void add_formatted_headers(struct strbuf *msg,
+ struct string_list *more_headers,
+ const char *line_prefix,
+ const char *meta,
+ const char *reset)
+{
+ int i;
+
+ for (i = 0; i < more_headers->nr; i++)
+ add_formatted_header(msg, more_headers->items[i].string,
+ line_prefix, meta, reset);
+}
+
+static int diff_filepair_is_phoney(struct diff_filespec *one,
+ struct diff_filespec *two)
+{
+ /*
+ * This function specifically looks for pairs injected by
+ * create_filepairs_for_header_only_notifications(). Such
+ * pairs are "phoney" in that they do not represent any
+ * content or even mode difference, but were inserted because
+ * diff_queued_diff previously had no pair associated with
+ * that path but we needed some pair to avoid losing the
+ * "remerge CONFLICT" header associated with the path.
+ */
+ return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two);
+}
+
static void builtin_diff(const char *name_a,
const char *name_b,
struct diff_filespec *one,
@@ -3423,14 +3468,16 @@ static void builtin_diff(const char *name_a,
if (o->submodule_format == DIFF_SUBMODULE_LOG &&
(!one->mode || S_ISGITLINK(one->mode)) &&
- (!two->mode || S_ISGITLINK(two->mode))) {
+ (!two->mode || S_ISGITLINK(two->mode)) &&
+ (!diff_filepair_is_phoney(one, two))) {
show_submodule_diff_summary(o, one->path ? one->path : two->path,
&one->oid, &two->oid,
two->dirty_submodule);
return;
} else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
(!one->mode || S_ISGITLINK(one->mode)) &&
- (!two->mode || S_ISGITLINK(two->mode))) {
+ (!two->mode || S_ISGITLINK(two->mode)) &&
+ (!diff_filepair_is_phoney(one, two))) {
show_submodule_inline_diff(o, one->path ? one->path : two->path,
&one->oid, &two->oid,
two->dirty_submodule);
@@ -3450,12 +3497,12 @@ static void builtin_diff(const char *name_a,
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
- if (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two)) {
+ if (diff_filepair_is_phoney(one, two)) {
/*
- * We should only reach this point for pairs from
+ * We should only reach this point for pairs generated from
* create_filepairs_for_header_only_notifications(). For
- * these, we should avoid the "/dev/null" special casing
- * above, meaning we avoid showing such pairs as either
+ * these, we want to avoid the "/dev/null" special casing
+ * above, because we do not want such pairs shown as either
* "new file" or "deleted file" below.
*/
lbl[0] = a_one;
@@ -4141,18 +4188,13 @@ static void prep_temp_blob(struct index_state *istate,
int mode)
{
struct strbuf buf = STRBUF_INIT;
- struct strbuf tempfile = STRBUF_INIT;
char *path_dup = xstrdup(path);
const char *base = basename(path_dup);
struct checkout_metadata meta;
init_checkout_metadata(&meta, NULL, NULL, oid);
- /* Generate "XXXXXX_basename.ext" */
- strbuf_addstr(&tempfile, "XXXXXX_");
- strbuf_addstr(&tempfile, base);
-
- temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
+ temp->tempfile = mks_tempfile_dt("git-blob-XXXXXX", base);
if (!temp->tempfile)
die_errno("unable to create temp-file");
if (convert_to_working_tree(istate, path,
@@ -4167,7 +4209,6 @@ static void prep_temp_blob(struct index_state *istate,
oid_to_hex_r(temp->hex, oid);
xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
strbuf_release(&buf);
- strbuf_release(&tempfile);
free(path_dup);
}
@@ -4260,35 +4301,34 @@ static void run_external_diff(const char *pgm,
const char *xfrm_msg,
struct diff_options *o)
{
- struct strvec argv = STRVEC_INIT;
- struct strvec env = STRVEC_INIT;
+ struct child_process cmd = CHILD_PROCESS_INIT;
struct diff_queue_struct *q = &diff_queued_diff;
- strvec_push(&argv, pgm);
- strvec_push(&argv, name);
+ strvec_push(&cmd.args, pgm);
+ strvec_push(&cmd.args, name);
if (one && two) {
- add_external_diff_name(o->repo, &argv, name, one);
+ add_external_diff_name(o->repo, &cmd.args, name, one);
if (!other)
- add_external_diff_name(o->repo, &argv, name, two);
+ add_external_diff_name(o->repo, &cmd.args, name, two);
else {
- add_external_diff_name(o->repo, &argv, other, two);
- strvec_push(&argv, other);
- strvec_push(&argv, xfrm_msg);
+ add_external_diff_name(o->repo, &cmd.args, other, two);
+ strvec_push(&cmd.args, other);
+ strvec_push(&cmd.args, xfrm_msg);
}
}
- strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
- strvec_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+ strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d",
+ ++o->diff_path_counter);
+ strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
diff_free_filespec_data(one);
diff_free_filespec_data(two);
- if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))
+ cmd.use_shell = 1;
+ if (run_command(&cmd))
die(_("external diff died, stopping at %s"), name);
remove_tempfile();
- strvec_clear(&argv);
- strvec_clear(&env);
}
static int similarity_index(struct diff_filepair *p)
@@ -4325,7 +4365,7 @@ static void fill_metainfo(struct strbuf *msg,
const char *set = diff_get_color(use_color, DIFF_METAINFO);
const char *reset = diff_get_color(use_color, DIFF_RESET);
const char *line_prefix = diff_line_prefix(o);
- struct strbuf *more_headers = NULL;
+ struct string_list *more_headers = NULL;
*must_show_header = 1;
strbuf_init(msg, PATH_MAX * 2 + 300);
@@ -4575,8 +4615,6 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
}
-static void prep_parse_options(struct diff_options *options);
-
void repo_diff_setup(struct repository *r, struct diff_options *options)
{
memcpy(options, &default_diff_options, sizeof(*options));
@@ -4622,8 +4660,6 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->color_moved = diff_color_moved_default;
options->color_moved_ws_handling = diff_color_moved_ws_default;
-
- prep_parse_options(options);
}
static const char diff_status_letters[] = {
@@ -4781,8 +4817,6 @@ void diff_setup_done(struct diff_options *options)
options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON];
options->filter &= ~options->filter_not;
}
-
- FREE_AND_NULL(options->parseopts);
}
int parse_long_opt(const char *opt, const char **argv,
@@ -5379,7 +5413,8 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns
return 0;
}
-static void prep_parse_options(struct diff_options *options)
+struct option *add_diff_options(const struct option *opts,
+ struct diff_options *options)
{
struct option parseopts[] = {
OPT_GROUP(N_("Diff output format options")),
@@ -5649,22 +5684,25 @@ static void prep_parse_options(struct diff_options *options)
OPT_END()
};
- ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
- memcpy(options->parseopts, parseopts, sizeof(parseopts));
+ return parse_options_concat(opts, parseopts);
}
int diff_opt_parse(struct diff_options *options,
const char **av, int ac, const char *prefix)
{
+ struct option no_options[] = { OPT_END() };
+ struct option *parseopts = add_diff_options(no_options, options);
+
if (!prefix)
prefix = "";
- ac = parse_options(ac, av, prefix, options->parseopts, NULL,
+ ac = parse_options(ac, av, prefix, parseopts, NULL,
PARSE_OPT_KEEP_DASHDASH |
- PARSE_OPT_KEEP_UNKNOWN |
+ PARSE_OPT_KEEP_UNKNOWN_OPT |
PARSE_OPT_NO_INTERNAL_HELP |
PARSE_OPT_ONE_SHOT |
PARSE_OPT_STOP_AT_NON_OPTION);
+ free(parseopts);
return ac;
}
@@ -5732,6 +5770,13 @@ void diff_free_filepair(struct diff_filepair *p)
free(p);
}
+void diff_free_queue(struct diff_queue_struct *q)
+{
+ for (int i = 0; i < q->nr; i++)
+ diff_free_filepair(q->queue[i]);
+ free(q->queue);
+}
+
const char *diff_aligned_abbrev(const struct object_id *oid, int len)
{
int abblen;
@@ -5852,6 +5897,7 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
{
int include_conflict_headers =
(additional_headers(o, p->one->path) &&
+ !o->pickaxe_opts &&
(!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
/*
@@ -5907,6 +5953,8 @@ int diff_queue_is_empty(struct diff_options *o)
int i;
int include_conflict_headers =
(o->additional_path_headers &&
+ strmap_get_size(o->additional_path_headers) &&
+ !o->pickaxe_opts &&
(!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
if (include_conflict_headers)
@@ -6185,7 +6233,7 @@ static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode)
}
/* returns 0 upon success, and writes result into oid */
-static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
+static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
{
struct diff_queue_struct *q = &diff_queued_diff;
int i;
@@ -6232,66 +6280,63 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
if (p->one->mode == 0) {
patch_id_add_string(&ctx, "newfilemode");
patch_id_add_mode(&ctx, p->two->mode);
- patch_id_add_string(&ctx, "---/dev/null");
- patch_id_add_string(&ctx, "+++b/");
- the_hash_algo->update_fn(&ctx, p->two->path, len2);
} else if (p->two->mode == 0) {
patch_id_add_string(&ctx, "deletedfilemode");
patch_id_add_mode(&ctx, p->one->mode);
- patch_id_add_string(&ctx, "---a/");
- the_hash_algo->update_fn(&ctx, p->one->path, len1);
- patch_id_add_string(&ctx, "+++/dev/null");
- } else {
- patch_id_add_string(&ctx, "---a/");
- the_hash_algo->update_fn(&ctx, p->one->path, len1);
- patch_id_add_string(&ctx, "+++b/");
- the_hash_algo->update_fn(&ctx, p->two->path, len2);
+ } else if (p->one->mode != p->two->mode) {
+ patch_id_add_string(&ctx, "oldmode");
+ patch_id_add_mode(&ctx, p->one->mode);
+ patch_id_add_string(&ctx, "newmode");
+ patch_id_add_mode(&ctx, p->two->mode);
}
- if (diff_header_only)
- continue;
-
- if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
- fill_mmfile(options->repo, &mf2, p->two) < 0)
- return error("unable to read files to diff");
-
- if (diff_filespec_is_binary(options->repo, p->one) ||
+ if (diff_header_only) {
+ /* don't do anything since we're only populating header info */
+ } else if (diff_filespec_is_binary(options->repo, p->one) ||
diff_filespec_is_binary(options->repo, p->two)) {
the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),
the_hash_algo->hexsz);
the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),
the_hash_algo->hexsz);
- continue;
- }
-
- xpp.flags = 0;
- xecfg.ctxlen = 3;
- xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
- if (xdi_diff_outf(&mf1, &mf2, NULL,
- patch_id_consume, &data, &xpp, &xecfg))
- return error("unable to generate patch-id diff for %s",
- p->one->path);
+ } else {
+ if (p->one->mode == 0) {
+ patch_id_add_string(&ctx, "---/dev/null");
+ patch_id_add_string(&ctx, "+++b/");
+ the_hash_algo->update_fn(&ctx, p->two->path, len2);
+ } else if (p->two->mode == 0) {
+ patch_id_add_string(&ctx, "---a/");
+ the_hash_algo->update_fn(&ctx, p->one->path, len1);
+ patch_id_add_string(&ctx, "+++/dev/null");
+ } else {
+ patch_id_add_string(&ctx, "---a/");
+ the_hash_algo->update_fn(&ctx, p->one->path, len1);
+ patch_id_add_string(&ctx, "+++b/");
+ the_hash_algo->update_fn(&ctx, p->two->path, len2);
+ }
- if (stable)
- flush_one_hunk(oid, &ctx);
+ if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
+ fill_mmfile(options->repo, &mf2, p->two) < 0)
+ return error("unable to read files to diff");
+ xpp.flags = 0;
+ xecfg.ctxlen = 3;
+ xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
+ if (xdi_diff_outf(&mf1, &mf2, NULL,
+ patch_id_consume, &data, &xpp, &xecfg))
+ return error("unable to generate patch-id diff for %s",
+ p->one->path);
+ }
+ flush_one_hunk(oid, &ctx);
}
- if (!stable)
- the_hash_algo->final_oid_fn(oid, &ctx);
-
return 0;
}
-int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable)
+int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
{
struct diff_queue_struct *q = &diff_queued_diff;
- int i;
- int result = diff_get_patch_id(options, oid, diff_header_only, stable);
+ int result = diff_get_patch_id(options, oid, diff_header_only);
- for (i = 0; i < q->nr; i++)
- diff_free_filepair(q->queue[i]);
-
- free(q->queue);
+ diff_free_queue(q);
DIFF_QUEUE_CLEAR(q);
return result;
@@ -6466,7 +6511,6 @@ void diff_free(struct diff_options *options)
diff_free_file(options);
diff_free_ignore_regex(options);
clear_pathspec(&options->pathspec);
- FREE_AND_NULL(options->parseopts);
}
void diff_flush(struct diff_options *options)
@@ -6560,10 +6604,8 @@ void diff_flush(struct diff_options *options)
if (output_format & DIFF_FORMAT_CALLBACK)
options->format_callback(q, options, options->format_callback_data);
- for (i = 0; i < q->nr; i++)
- diff_free_filepair(q->queue[i]);
free_queue:
- free(q->queue);
+ diff_free_queue(q);
DIFF_QUEUE_CLEAR(q);
diff_free(options);