28
28
#include "help.h"
29
29
#include "promisor-remote.h"
30
30
#include "dir.h"
31
+ #include "strmap.h"
31
32
32
33
#ifdef NO_FAST_WORKING_DIRECTORY
33
34
#define FAST_WORKING_DIRECTORY 0
@@ -3353,6 +3354,31 @@ struct userdiff_driver *get_textconv(struct repository *r,
3353
3354
return userdiff_get_textconv (r , one -> driver );
3354
3355
}
3355
3356
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
+
3356
3382
static void builtin_diff (const char * name_a ,
3357
3383
const char * name_b ,
3358
3384
struct diff_filespec * one ,
@@ -3411,6 +3437,17 @@ static void builtin_diff(const char *name_a,
3411
3437
b_two = quote_two (b_prefix , name_b + (* name_b == '/' ));
3412
3438
lbl [0 ] = DIFF_FILE_VALID (one ) ? a_one : "/dev/null" ;
3413
3439
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
+ }
3414
3451
strbuf_addf (& header , "%s%sdiff --git %s %s%s\n" , line_prefix , meta , a_one , b_two , reset );
3415
3452
if (lbl [0 ][0 ] == '/' ) {
3416
3453
/* /dev/null */
@@ -4275,6 +4312,7 @@ static void fill_metainfo(struct strbuf *msg,
4275
4312
const char * set = diff_get_color (use_color , DIFF_METAINFO );
4276
4313
const char * reset = diff_get_color (use_color , DIFF_RESET );
4277
4314
const char * line_prefix = diff_line_prefix (o );
4315
+ struct strbuf * more_headers = NULL ;
4278
4316
4279
4317
* must_show_header = 1 ;
4280
4318
strbuf_init (msg , PATH_MAX * 2 + 300 );
@@ -4311,6 +4349,11 @@ static void fill_metainfo(struct strbuf *msg,
4311
4349
default :
4312
4350
* must_show_header = 0 ;
4313
4351
}
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
+ }
4314
4357
if (one && two && !oideq (& one -> oid , & two -> oid )) {
4315
4358
const unsigned hexsz = the_hash_algo -> hexsz ;
4316
4359
int abbrev = o -> abbrev ? o -> abbrev : DEFAULT_ABBREV ;
@@ -5803,12 +5846,27 @@ int diff_unmodified_pair(struct diff_filepair *p)
5803
5846
5804
5847
static void diff_flush_patch (struct diff_filepair * p , struct diff_options * o )
5805
5848
{
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 )
5807
5864
return ;
5808
5865
5866
+ /* Actually, we can also return early to avoid showing tree diffs */
5809
5867
if ((DIFF_FILE_VALID (p -> one ) && S_ISDIR (p -> one -> mode )) ||
5810
5868
(DIFF_FILE_VALID (p -> two ) && S_ISDIR (p -> two -> mode )))
5811
- return ; /* no tree diffs in patch format */
5869
+ return ;
5812
5870
5813
5871
run_diff (p , o );
5814
5872
}
@@ -5839,10 +5897,17 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
5839
5897
run_checkdiff (p , o );
5840
5898
}
5841
5899
5842
- int diff_queue_is_empty (void )
5900
+ int diff_queue_is_empty (struct diff_options * o )
5843
5901
{
5844
5902
struct diff_queue_struct * q = & diff_queued_diff ;
5845
5903
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
+
5846
5911
for (i = 0 ; i < q -> nr ; i ++ )
5847
5912
if (!diff_unmodified_pair (q -> queue [i ]))
5848
5913
return 0 ;
@@ -6276,6 +6341,54 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
6276
6341
warning (_ (rename_limit_advice ), varname , needed );
6277
6342
}
6278
6343
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
+
6279
6392
static void diff_flush_patch_all_file_pairs (struct diff_options * o )
6280
6393
{
6281
6394
int i ;
@@ -6288,6 +6401,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
6288
6401
if (o -> color_moved )
6289
6402
o -> emitted_symbols = & esm ;
6290
6403
6404
+ if (o -> additional_path_headers )
6405
+ create_filepairs_for_header_only_notifications (o );
6406
+
6291
6407
for (i = 0 ; i < q -> nr ; i ++ ) {
6292
6408
struct diff_filepair * p = q -> queue [i ];
6293
6409
if (check_pair_status (p ))
@@ -6358,7 +6474,7 @@ void diff_flush(struct diff_options *options)
6358
6474
* Order: raw, stat, summary, patch
6359
6475
* or: name/name-status/checkdiff (other bits clear)
6360
6476
*/
6361
- if (!q -> nr )
6477
+ if (!q -> nr && ! options -> additional_path_headers )
6362
6478
goto free_queue ;
6363
6479
6364
6480
if (output_format & (DIFF_FORMAT_RAW |
0 commit comments