diff: add ability to insert additional headers for paths
When additional headers are provided, we need to * add diff_filepairs to diff_queued_diff for each paths in the additional headers map which, unless that path is part of another diff_filepair already found in diff_queued_diff * format the headers (colorization, line_prefix for --graph) * make sure the various codepaths that attempt to return early if there are "no changes" take into account the headers that need to be shown. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
6054d1aac3
commit
95433eeed9
124
diff.c
124
diff.c
|
@ -27,6 +27,7 @@
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
|
#include "strmap.h"
|
||||||
|
|
||||||
#ifdef NO_FAST_WORKING_DIRECTORY
|
#ifdef NO_FAST_WORKING_DIRECTORY
|
||||||
#define FAST_WORKING_DIRECTORY 0
|
#define FAST_WORKING_DIRECTORY 0
|
||||||
|
@ -3406,6 +3407,31 @@ struct userdiff_driver *get_textconv(struct repository *r,
|
||||||
return userdiff_get_textconv(r, one->driver);
|
return userdiff_get_textconv(r, one->driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct strbuf *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,
|
||||||
|
const char *line_prefix,
|
||||||
|
const char *meta,
|
||||||
|
const char *reset)
|
||||||
|
{
|
||||||
|
char *next, *newline;
|
||||||
|
|
||||||
|
for (next = more_headers->buf; *next; next = newline) {
|
||||||
|
newline = strchrnul(next, '\n');
|
||||||
|
strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta,
|
||||||
|
(int)(newline - next), next, reset);
|
||||||
|
if (*newline)
|
||||||
|
newline++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void builtin_diff(const char *name_a,
|
static void builtin_diff(const char *name_a,
|
||||||
const char *name_b,
|
const char *name_b,
|
||||||
struct diff_filespec *one,
|
struct diff_filespec *one,
|
||||||
|
@ -3464,6 +3490,17 @@ static void builtin_diff(const char *name_a,
|
||||||
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
|
b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
|
||||||
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
|
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
|
||||||
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
|
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
|
||||||
|
if (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two)) {
|
||||||
|
/*
|
||||||
|
* We should only reach this point for pairs 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
|
||||||
|
* "new file" or "deleted file" below.
|
||||||
|
*/
|
||||||
|
lbl[0] = a_one;
|
||||||
|
lbl[1] = b_two;
|
||||||
|
}
|
||||||
strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
|
strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
|
||||||
if (lbl[0][0] == '/') {
|
if (lbl[0][0] == '/') {
|
||||||
/* /dev/null */
|
/* /dev/null */
|
||||||
|
@ -4328,6 +4365,7 @@ static void fill_metainfo(struct strbuf *msg,
|
||||||
const char *set = diff_get_color(use_color, DIFF_METAINFO);
|
const char *set = diff_get_color(use_color, DIFF_METAINFO);
|
||||||
const char *reset = diff_get_color(use_color, DIFF_RESET);
|
const char *reset = diff_get_color(use_color, DIFF_RESET);
|
||||||
const char *line_prefix = diff_line_prefix(o);
|
const char *line_prefix = diff_line_prefix(o);
|
||||||
|
struct strbuf *more_headers = NULL;
|
||||||
|
|
||||||
*must_show_header = 1;
|
*must_show_header = 1;
|
||||||
strbuf_init(msg, PATH_MAX * 2 + 300);
|
strbuf_init(msg, PATH_MAX * 2 + 300);
|
||||||
|
@ -4364,6 +4402,11 @@ static void fill_metainfo(struct strbuf *msg,
|
||||||
default:
|
default:
|
||||||
*must_show_header = 0;
|
*must_show_header = 0;
|
||||||
}
|
}
|
||||||
|
if ((more_headers = additional_headers(o, name))) {
|
||||||
|
add_formatted_headers(msg, more_headers,
|
||||||
|
line_prefix, set, reset);
|
||||||
|
*must_show_header = 1;
|
||||||
|
}
|
||||||
if (one && two && !oideq(&one->oid, &two->oid)) {
|
if (one && two && !oideq(&one->oid, &two->oid)) {
|
||||||
const unsigned hexsz = the_hash_algo->hexsz;
|
const unsigned hexsz = the_hash_algo->hexsz;
|
||||||
int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV;
|
int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV;
|
||||||
|
@ -5852,12 +5895,27 @@ int diff_unmodified_pair(struct diff_filepair *p)
|
||||||
|
|
||||||
static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
|
static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
|
||||||
{
|
{
|
||||||
if (diff_unmodified_pair(p))
|
int include_conflict_headers =
|
||||||
|
(additional_headers(o, p->one->path) &&
|
||||||
|
(!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we can return early without showing a diff. Note that
|
||||||
|
* diff_filepair only stores {oid, path, mode, is_valid}
|
||||||
|
* information for each path, and thus diff_unmodified_pair() only
|
||||||
|
* considers those bits of info. However, we do not want pairs
|
||||||
|
* created by create_filepairs_for_header_only_notifications()
|
||||||
|
* (which always look like unmodified pairs) to be ignored, so
|
||||||
|
* return early if both p is unmodified AND we don't want to
|
||||||
|
* include_conflict_headers.
|
||||||
|
*/
|
||||||
|
if (diff_unmodified_pair(p) && !include_conflict_headers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Actually, we can also return early to avoid showing tree diffs */
|
||||||
if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
|
if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
|
||||||
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
|
(DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
|
||||||
return; /* no tree diffs in patch format */
|
return;
|
||||||
|
|
||||||
run_diff(p, o);
|
run_diff(p, o);
|
||||||
}
|
}
|
||||||
|
@ -5888,10 +5946,17 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
|
||||||
run_checkdiff(p, o);
|
run_checkdiff(p, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
int diff_queue_is_empty(void)
|
int diff_queue_is_empty(struct diff_options *o)
|
||||||
{
|
{
|
||||||
struct diff_queue_struct *q = &diff_queued_diff;
|
struct diff_queue_struct *q = &diff_queued_diff;
|
||||||
int i;
|
int i;
|
||||||
|
int include_conflict_headers =
|
||||||
|
(o->additional_path_headers &&
|
||||||
|
(!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o)));
|
||||||
|
|
||||||
|
if (include_conflict_headers)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < q->nr; i++)
|
for (i = 0; i < q->nr; i++)
|
||||||
if (!diff_unmodified_pair(q->queue[i]))
|
if (!diff_unmodified_pair(q->queue[i]))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -6325,6 +6390,54 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
|
||||||
warning(_(rename_limit_advice), varname, needed);
|
warning(_(rename_limit_advice), varname, needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_filepairs_for_header_only_notifications(struct diff_options *o)
|
||||||
|
{
|
||||||
|
struct strset present;
|
||||||
|
struct diff_queue_struct *q = &diff_queued_diff;
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct strmap_entry *e;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
strset_init_with_options(&present, /*pool*/ NULL, /*strdup*/ 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find out which paths exist in diff_queued_diff, preferring
|
||||||
|
* one->path for any pair that has multiple paths.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < q->nr; i++) {
|
||||||
|
struct diff_filepair *p = q->queue[i];
|
||||||
|
char *path = p->one->path ? p->one->path : p->two->path;
|
||||||
|
|
||||||
|
if (strmap_contains(o->additional_path_headers, path))
|
||||||
|
strset_add(&present, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop over paths in additional_path_headers; for each NOT already
|
||||||
|
* in diff_queued_diff, create a synthetic filepair and insert that
|
||||||
|
* into diff_queued_diff.
|
||||||
|
*/
|
||||||
|
strmap_for_each_entry(o->additional_path_headers, &iter, e) {
|
||||||
|
if (!strset_contains(&present, e->key)) {
|
||||||
|
struct diff_filespec *one, *two;
|
||||||
|
struct diff_filepair *p;
|
||||||
|
|
||||||
|
one = alloc_filespec(e->key);
|
||||||
|
two = alloc_filespec(e->key);
|
||||||
|
fill_filespec(one, null_oid(), 0, 0);
|
||||||
|
fill_filespec(two, null_oid(), 0, 0);
|
||||||
|
p = diff_queue(q, one, two);
|
||||||
|
p->status = DIFF_STATUS_MODIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Re-sort the filepairs */
|
||||||
|
diffcore_fix_diff_index();
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
strset_clear(&present);
|
||||||
|
}
|
||||||
|
|
||||||
static void diff_flush_patch_all_file_pairs(struct diff_options *o)
|
static void diff_flush_patch_all_file_pairs(struct diff_options *o)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -6337,6 +6450,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
|
||||||
if (o->color_moved)
|
if (o->color_moved)
|
||||||
o->emitted_symbols = &esm;
|
o->emitted_symbols = &esm;
|
||||||
|
|
||||||
|
if (o->additional_path_headers)
|
||||||
|
create_filepairs_for_header_only_notifications(o);
|
||||||
|
|
||||||
for (i = 0; i < q->nr; i++) {
|
for (i = 0; i < q->nr; i++) {
|
||||||
struct diff_filepair *p = q->queue[i];
|
struct diff_filepair *p = q->queue[i];
|
||||||
if (check_pair_status(p))
|
if (check_pair_status(p))
|
||||||
|
@ -6413,7 +6529,7 @@ void diff_flush(struct diff_options *options)
|
||||||
* Order: raw, stat, summary, patch
|
* Order: raw, stat, summary, patch
|
||||||
* or: name/name-status/checkdiff (other bits clear)
|
* or: name/name-status/checkdiff (other bits clear)
|
||||||
*/
|
*/
|
||||||
if (!q->nr)
|
if (!q->nr && !options->additional_path_headers)
|
||||||
goto free_queue;
|
goto free_queue;
|
||||||
|
|
||||||
if (output_format & (DIFF_FORMAT_RAW |
|
if (output_format & (DIFF_FORMAT_RAW |
|
||||||
|
|
3
diff.h
3
diff.h
|
@ -395,6 +395,7 @@ struct diff_options {
|
||||||
|
|
||||||
struct repository *repo;
|
struct repository *repo;
|
||||||
struct option *parseopts;
|
struct option *parseopts;
|
||||||
|
struct strmap *additional_path_headers;
|
||||||
|
|
||||||
int no_free;
|
int no_free;
|
||||||
};
|
};
|
||||||
|
@ -593,7 +594,7 @@ void diffcore_fix_diff_index(void);
|
||||||
" show all files diff when -S is used and hit is found.\n" \
|
" show all files diff when -S is used and hit is found.\n" \
|
||||||
" -a --text treat all files as text.\n"
|
" -a --text treat all files as text.\n"
|
||||||
|
|
||||||
int diff_queue_is_empty(void);
|
int diff_queue_is_empty(struct diff_options *o);
|
||||||
void diff_flush(struct diff_options*);
|
void diff_flush(struct diff_options*);
|
||||||
void diff_free(struct diff_options*);
|
void diff_free(struct diff_options*);
|
||||||
void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
|
void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
|
||||||
|
|
|
@ -852,7 +852,7 @@ int log_tree_diff_flush(struct rev_info *opt)
|
||||||
opt->shown_dashes = 0;
|
opt->shown_dashes = 0;
|
||||||
diffcore_std(&opt->diffopt);
|
diffcore_std(&opt->diffopt);
|
||||||
|
|
||||||
if (diff_queue_is_empty()) {
|
if (diff_queue_is_empty(&opt->diffopt)) {
|
||||||
int saved_fmt = opt->diffopt.output_format;
|
int saved_fmt = opt->diffopt.output_format;
|
||||||
opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
|
opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||||
diff_flush(&opt->diffopt);
|
diff_flush(&opt->diffopt);
|
||||||
|
|
Loading…
Reference in New Issue