2828#include "help.h"
2929#include "promisor-remote.h"
3030#include "dir.h"
31+ #include "strmap.h"
3132
3233#ifdef NO_FAST_WORKING_DIRECTORY
3334#define FAST_WORKING_DIRECTORY 0
@@ -3353,6 +3354,31 @@ struct userdiff_driver *get_textconv(struct repository *r,
33533354 return userdiff_get_textconv (r , one -> driver );
33543355}
33553356
3357+ static struct strbuf * additional_headers (struct diff_options * o ,
3358+ const char * path )
3359+ {
3360+ if (!o -> additional_path_headers )
3361+ return NULL ;
3362+ return strmap_get (o -> additional_path_headers , path );
3363+ }
3364+
3365+ static void add_formatted_headers (struct strbuf * msg ,
3366+ struct strbuf * more_headers ,
3367+ const char * line_prefix ,
3368+ const char * meta ,
3369+ const char * reset )
3370+ {
3371+ char * next , * newline ;
3372+
3373+ for (next = more_headers -> buf ; * next ; next = newline ) {
3374+ newline = strchrnul (next , '\n' );
3375+ strbuf_addf (msg , "%s%s%.*s%s\n" , line_prefix , meta ,
3376+ (int )(newline - next ), next , reset );
3377+ if (* newline )
3378+ newline ++ ;
3379+ }
3380+ }
3381+
33563382static void builtin_diff (const char * name_a ,
33573383 const char * name_b ,
33583384 struct diff_filespec * one ,
@@ -3411,6 +3437,17 @@ static void builtin_diff(const char *name_a,
34113437 b_two = quote_two (b_prefix , name_b + (* name_b == '/' ));
34123438 lbl [0 ] = DIFF_FILE_VALID (one ) ? a_one : "/dev/null" ;
34133439 lbl [1 ] = DIFF_FILE_VALID (two ) ? b_two : "/dev/null" ;
3440+ if (!DIFF_FILE_VALID (one ) && !DIFF_FILE_VALID (two )) {
3441+ /*
3442+ * We should only reach this point for pairs from
3443+ * create_filepairs_for_header_only_notifications(). For
3444+ * these, we should avoid the "/dev/null" special casing
3445+ * above, meaning we avoid showing such pairs as either
3446+ * "new file" or "deleted file" below.
3447+ */
3448+ lbl [0 ] = a_one ;
3449+ lbl [1 ] = b_two ;
3450+ }
34143451 strbuf_addf (& header , "%s%sdiff --git %s %s%s\n" , line_prefix , meta , a_one , b_two , reset );
34153452 if (lbl [0 ][0 ] == '/' ) {
34163453 /* /dev/null */
@@ -4275,6 +4312,7 @@ static void fill_metainfo(struct strbuf *msg,
42754312 const char * set = diff_get_color (use_color , DIFF_METAINFO );
42764313 const char * reset = diff_get_color (use_color , DIFF_RESET );
42774314 const char * line_prefix = diff_line_prefix (o );
4315+ struct strbuf * more_headers = NULL ;
42784316
42794317 * must_show_header = 1 ;
42804318 strbuf_init (msg , PATH_MAX * 2 + 300 );
@@ -4311,6 +4349,11 @@ static void fill_metainfo(struct strbuf *msg,
43114349 default :
43124350 * must_show_header = 0 ;
43134351 }
4352+ if ((more_headers = additional_headers (o , name ))) {
4353+ add_formatted_headers (msg , more_headers ,
4354+ line_prefix , set , reset );
4355+ * must_show_header = 1 ;
4356+ }
43144357 if (one && two && !oideq (& one -> oid , & two -> oid )) {
43154358 const unsigned hexsz = the_hash_algo -> hexsz ;
43164359 int abbrev = o -> abbrev ? o -> abbrev : DEFAULT_ABBREV ;
@@ -5803,12 +5846,27 @@ int diff_unmodified_pair(struct diff_filepair *p)
58035846
58045847static void diff_flush_patch (struct diff_filepair * p , struct diff_options * o )
58055848{
5806- if (diff_unmodified_pair (p ))
5849+ int include_conflict_headers =
5850+ (additional_headers (o , p -> one -> path ) &&
5851+ (!o -> filter || filter_bit_tst (DIFF_STATUS_UNMERGED , o )));
5852+
5853+ /*
5854+ * Check if we can return early without showing a diff. Note that
5855+ * diff_filepair only stores {oid, path, mode, is_valid}
5856+ * information for each path, and thus diff_unmodified_pair() only
5857+ * considers those bits of info. However, we do not want pairs
5858+ * created by create_filepairs_for_header_only_notifications()
5859+ * (which always look like unmodified pairs) to be ignored, so
5860+ * return early if both p is unmodified AND we don't want to
5861+ * include_conflict_headers.
5862+ */
5863+ if (diff_unmodified_pair (p ) && !include_conflict_headers )
58075864 return ;
58085865
5866+ /* Actually, we can also return early to avoid showing tree diffs */
58095867 if ((DIFF_FILE_VALID (p -> one ) && S_ISDIR (p -> one -> mode )) ||
58105868 (DIFF_FILE_VALID (p -> two ) && S_ISDIR (p -> two -> mode )))
5811- return ; /* no tree diffs in patch format */
5869+ return ;
58125870
58135871 run_diff (p , o );
58145872}
@@ -5839,10 +5897,17 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
58395897 run_checkdiff (p , o );
58405898}
58415899
5842- int diff_queue_is_empty (void )
5900+ int diff_queue_is_empty (struct diff_options * o )
58435901{
58445902 struct diff_queue_struct * q = & diff_queued_diff ;
58455903 int i ;
5904+ int include_conflict_headers =
5905+ (o -> additional_path_headers &&
5906+ (!o -> filter || filter_bit_tst (DIFF_STATUS_UNMERGED , o )));
5907+
5908+ if (include_conflict_headers )
5909+ return 0 ;
5910+
58465911 for (i = 0 ; i < q -> nr ; i ++ )
58475912 if (!diff_unmodified_pair (q -> queue [i ]))
58485913 return 0 ;
@@ -6276,6 +6341,54 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
62766341 warning (_ (rename_limit_advice ), varname , needed );
62776342}
62786343
6344+ static void create_filepairs_for_header_only_notifications (struct diff_options * o )
6345+ {
6346+ struct strset present ;
6347+ struct diff_queue_struct * q = & diff_queued_diff ;
6348+ struct hashmap_iter iter ;
6349+ struct strmap_entry * e ;
6350+ int i ;
6351+
6352+ strset_init_with_options (& present , /*pool*/ NULL , /*strdup*/ 0 );
6353+
6354+ /*
6355+ * Find out which paths exist in diff_queued_diff, preferring
6356+ * one->path for any pair that has multiple paths.
6357+ */
6358+ for (i = 0 ; i < q -> nr ; i ++ ) {
6359+ struct diff_filepair * p = q -> queue [i ];
6360+ char * path = p -> one -> path ? p -> one -> path : p -> two -> path ;
6361+
6362+ if (strmap_contains (o -> additional_path_headers , path ))
6363+ strset_add (& present , path );
6364+ }
6365+
6366+ /*
6367+ * Loop over paths in additional_path_headers; for each NOT already
6368+ * in diff_queued_diff, create a synthetic filepair and insert that
6369+ * into diff_queued_diff.
6370+ */
6371+ strmap_for_each_entry (o -> additional_path_headers , & iter , e ) {
6372+ if (!strset_contains (& present , e -> key )) {
6373+ struct diff_filespec * one , * two ;
6374+ struct diff_filepair * p ;
6375+
6376+ one = alloc_filespec (e -> key );
6377+ two = alloc_filespec (e -> key );
6378+ fill_filespec (one , null_oid (), 0 , 0 );
6379+ fill_filespec (two , null_oid (), 0 , 0 );
6380+ p = diff_queue (q , one , two );
6381+ p -> status = DIFF_STATUS_MODIFIED ;
6382+ }
6383+ }
6384+
6385+ /* Re-sort the filepairs */
6386+ diffcore_fix_diff_index ();
6387+
6388+ /* Cleanup */
6389+ strset_clear (& present );
6390+ }
6391+
62796392static void diff_flush_patch_all_file_pairs (struct diff_options * o )
62806393{
62816394 int i ;
@@ -6288,6 +6401,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
62886401 if (o -> color_moved )
62896402 o -> emitted_symbols = & esm ;
62906403
6404+ if (o -> additional_path_headers )
6405+ create_filepairs_for_header_only_notifications (o );
6406+
62916407 for (i = 0 ; i < q -> nr ; i ++ ) {
62926408 struct diff_filepair * p = q -> queue [i ];
62936409 if (check_pair_status (p ))
@@ -6358,7 +6474,7 @@ void diff_flush(struct diff_options *options)
63586474 * Order: raw, stat, summary, patch
63596475 * or: name/name-status/checkdiff (other bits clear)
63606476 */
6361- if (!q -> nr )
6477+ if (!q -> nr && ! options -> additional_path_headers )
63626478 goto free_queue ;
63636479
63646480 if (output_format & (DIFF_FORMAT_RAW |
0 commit comments