27
27
#include "help.h"
28
28
#include "promisor-remote.h"
29
29
#include "dir.h"
30
+ #include "strmap.h"
30
31
31
32
#ifdef NO_FAST_WORKING_DIRECTORY
32
33
#define FAST_WORKING_DIRECTORY 0
@@ -3406,6 +3407,31 @@ struct userdiff_driver *get_textconv(struct repository *r,
3406
3407
return userdiff_get_textconv (r , one -> driver );
3407
3408
}
3408
3409
3410
+ static struct strbuf * additional_headers (struct diff_options * o ,
3411
+ const char * path )
3412
+ {
3413
+ if (!o -> additional_path_headers )
3414
+ return NULL ;
3415
+ return strmap_get (o -> additional_path_headers , path );
3416
+ }
3417
+
3418
+ static void add_formatted_headers (struct strbuf * msg ,
3419
+ struct strbuf * more_headers ,
3420
+ const char * line_prefix ,
3421
+ const char * meta ,
3422
+ const char * reset )
3423
+ {
3424
+ char * next , * newline ;
3425
+
3426
+ for (next = more_headers -> buf ; * next ; next = newline ) {
3427
+ newline = strchrnul (next , '\n' );
3428
+ strbuf_addf (msg , "%s%s%.*s%s\n" , line_prefix , meta ,
3429
+ (int )(newline - next ), next , reset );
3430
+ if (* newline )
3431
+ newline ++ ;
3432
+ }
3433
+ }
3434
+
3409
3435
static void builtin_diff (const char * name_a ,
3410
3436
const char * name_b ,
3411
3437
struct diff_filespec * one ,
@@ -3464,6 +3490,17 @@ static void builtin_diff(const char *name_a,
3464
3490
b_two = quote_two (b_prefix , name_b + (* name_b == '/' ));
3465
3491
lbl [0 ] = DIFF_FILE_VALID (one ) ? a_one : "/dev/null" ;
3466
3492
lbl [1 ] = DIFF_FILE_VALID (two ) ? b_two : "/dev/null" ;
3493
+ if (!DIFF_FILE_VALID (one ) && !DIFF_FILE_VALID (two )) {
3494
+ /*
3495
+ * We should only reach this point for pairs from
3496
+ * create_filepairs_for_header_only_notifications(). For
3497
+ * these, we should avoid the "/dev/null" special casing
3498
+ * above, meaning we avoid showing such pairs as either
3499
+ * "new file" or "deleted file" below.
3500
+ */
3501
+ lbl [0 ] = a_one ;
3502
+ lbl [1 ] = b_two ;
3503
+ }
3467
3504
strbuf_addf (& header , "%s%sdiff --git %s %s%s\n" , line_prefix , meta , a_one , b_two , reset );
3468
3505
if (lbl [0 ][0 ] == '/' ) {
3469
3506
/* /dev/null */
@@ -4328,6 +4365,7 @@ static void fill_metainfo(struct strbuf *msg,
4328
4365
const char * set = diff_get_color (use_color , DIFF_METAINFO );
4329
4366
const char * reset = diff_get_color (use_color , DIFF_RESET );
4330
4367
const char * line_prefix = diff_line_prefix (o );
4368
+ struct strbuf * more_headers = NULL ;
4331
4369
4332
4370
* must_show_header = 1 ;
4333
4371
strbuf_init (msg , PATH_MAX * 2 + 300 );
@@ -4364,6 +4402,11 @@ static void fill_metainfo(struct strbuf *msg,
4364
4402
default :
4365
4403
* must_show_header = 0 ;
4366
4404
}
4405
+ if ((more_headers = additional_headers (o , name ))) {
4406
+ add_formatted_headers (msg , more_headers ,
4407
+ line_prefix , set , reset );
4408
+ * must_show_header = 1 ;
4409
+ }
4367
4410
if (one && two && !oideq (& one -> oid , & two -> oid )) {
4368
4411
const unsigned hexsz = the_hash_algo -> hexsz ;
4369
4412
int abbrev = o -> abbrev ? o -> abbrev : DEFAULT_ABBREV ;
@@ -5852,12 +5895,27 @@ int diff_unmodified_pair(struct diff_filepair *p)
5852
5895
5853
5896
static void diff_flush_patch (struct diff_filepair * p , struct diff_options * o )
5854
5897
{
5855
- if (diff_unmodified_pair (p ))
5898
+ int include_conflict_headers =
5899
+ (additional_headers (o , p -> one -> path ) &&
5900
+ (!o -> filter || filter_bit_tst (DIFF_STATUS_UNMERGED , o )));
5901
+
5902
+ /*
5903
+ * Check if we can return early without showing a diff. Note that
5904
+ * diff_filepair only stores {oid, path, mode, is_valid}
5905
+ * information for each path, and thus diff_unmodified_pair() only
5906
+ * considers those bits of info. However, we do not want pairs
5907
+ * created by create_filepairs_for_header_only_notifications()
5908
+ * (which always look like unmodified pairs) to be ignored, so
5909
+ * return early if both p is unmodified AND we don't want to
5910
+ * include_conflict_headers.
5911
+ */
5912
+ if (diff_unmodified_pair (p ) && !include_conflict_headers )
5856
5913
return ;
5857
5914
5915
+ /* Actually, we can also return early to avoid showing tree diffs */
5858
5916
if ((DIFF_FILE_VALID (p -> one ) && S_ISDIR (p -> one -> mode )) ||
5859
5917
(DIFF_FILE_VALID (p -> two ) && S_ISDIR (p -> two -> mode )))
5860
- return ; /* no tree diffs in patch format */
5918
+ return ;
5861
5919
5862
5920
run_diff (p , o );
5863
5921
}
@@ -5888,10 +5946,17 @@ static void diff_flush_checkdiff(struct diff_filepair *p,
5888
5946
run_checkdiff (p , o );
5889
5947
}
5890
5948
5891
- int diff_queue_is_empty (void )
5949
+ int diff_queue_is_empty (struct diff_options * o )
5892
5950
{
5893
5951
struct diff_queue_struct * q = & diff_queued_diff ;
5894
5952
int i ;
5953
+ int include_conflict_headers =
5954
+ (o -> additional_path_headers &&
5955
+ (!o -> filter || filter_bit_tst (DIFF_STATUS_UNMERGED , o )));
5956
+
5957
+ if (include_conflict_headers )
5958
+ return 0 ;
5959
+
5895
5960
for (i = 0 ; i < q -> nr ; i ++ )
5896
5961
if (!diff_unmodified_pair (q -> queue [i ]))
5897
5962
return 0 ;
@@ -6325,6 +6390,54 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
6325
6390
warning (_ (rename_limit_advice ), varname , needed );
6326
6391
}
6327
6392
6393
+ static void create_filepairs_for_header_only_notifications (struct diff_options * o )
6394
+ {
6395
+ struct strset present ;
6396
+ struct diff_queue_struct * q = & diff_queued_diff ;
6397
+ struct hashmap_iter iter ;
6398
+ struct strmap_entry * e ;
6399
+ int i ;
6400
+
6401
+ strset_init_with_options (& present , /*pool*/ NULL , /*strdup*/ 0 );
6402
+
6403
+ /*
6404
+ * Find out which paths exist in diff_queued_diff, preferring
6405
+ * one->path for any pair that has multiple paths.
6406
+ */
6407
+ for (i = 0 ; i < q -> nr ; i ++ ) {
6408
+ struct diff_filepair * p = q -> queue [i ];
6409
+ char * path = p -> one -> path ? p -> one -> path : p -> two -> path ;
6410
+
6411
+ if (strmap_contains (o -> additional_path_headers , path ))
6412
+ strset_add (& present , path );
6413
+ }
6414
+
6415
+ /*
6416
+ * Loop over paths in additional_path_headers; for each NOT already
6417
+ * in diff_queued_diff, create a synthetic filepair and insert that
6418
+ * into diff_queued_diff.
6419
+ */
6420
+ strmap_for_each_entry (o -> additional_path_headers , & iter , e ) {
6421
+ if (!strset_contains (& present , e -> key )) {
6422
+ struct diff_filespec * one , * two ;
6423
+ struct diff_filepair * p ;
6424
+
6425
+ one = alloc_filespec (e -> key );
6426
+ two = alloc_filespec (e -> key );
6427
+ fill_filespec (one , null_oid (), 0 , 0 );
6428
+ fill_filespec (two , null_oid (), 0 , 0 );
6429
+ p = diff_queue (q , one , two );
6430
+ p -> status = DIFF_STATUS_MODIFIED ;
6431
+ }
6432
+ }
6433
+
6434
+ /* Re-sort the filepairs */
6435
+ diffcore_fix_diff_index ();
6436
+
6437
+ /* Cleanup */
6438
+ strset_clear (& present );
6439
+ }
6440
+
6328
6441
static void diff_flush_patch_all_file_pairs (struct diff_options * o )
6329
6442
{
6330
6443
int i ;
@@ -6337,6 +6450,9 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
6337
6450
if (o -> color_moved )
6338
6451
o -> emitted_symbols = & esm ;
6339
6452
6453
+ if (o -> additional_path_headers )
6454
+ create_filepairs_for_header_only_notifications (o );
6455
+
6340
6456
for (i = 0 ; i < q -> nr ; i ++ ) {
6341
6457
struct diff_filepair * p = q -> queue [i ];
6342
6458
if (check_pair_status (p ))
@@ -6413,7 +6529,7 @@ void diff_flush(struct diff_options *options)
6413
6529
* Order: raw, stat, summary, patch
6414
6530
* or: name/name-status/checkdiff (other bits clear)
6415
6531
*/
6416
- if (!q -> nr )
6532
+ if (!q -> nr && ! options -> additional_path_headers )
6417
6533
goto free_queue ;
6418
6534
6419
6535
if (output_format & (DIFF_FORMAT_RAW |
0 commit comments