24
24
#include "dir.h"
25
25
#include "progress.h"
26
26
#include "blame.h"
27
+ #include "string-list.h"
27
28
28
29
static char blame_usage [] = N_ ("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>" );
29
30
@@ -323,6 +324,7 @@ static const char *format_time(timestamp_t time, const char *tz_str,
323
324
#define OUTPUT_SHOW_EMAIL 0400
324
325
#define OUTPUT_LINE_PORCELAIN 01000
325
326
#define OUTPUT_COLOR_LINE 02000
327
+ #define OUTPUT_SHOW_AGE_WITH_COLOR 04000
326
328
327
329
static void emit_porcelain_details (struct blame_origin * suspect , int repeat )
328
330
{
@@ -370,6 +372,63 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
370
372
putchar ('\n' );
371
373
}
372
374
375
+ static struct color_field {
376
+ timestamp_t hop ;
377
+ char col [COLOR_MAXLEN ];
378
+ } * colorfield ;
379
+ static int colorfield_nr , colorfield_alloc ;
380
+
381
+ static void parse_color_fields (const char * s )
382
+ {
383
+ struct string_list l = STRING_LIST_INIT_DUP ;
384
+ struct string_list_item * item ;
385
+ enum { EXPECT_DATE , EXPECT_COLOR } next = EXPECT_COLOR ;
386
+
387
+ colorfield_nr = 0 ;
388
+
389
+ /* Ideally this would be stripped and split at the same time? */
390
+ string_list_split (& l , s , ',' , -1 );
391
+ ALLOC_GROW (colorfield , colorfield_nr + 1 , colorfield_alloc );
392
+
393
+ for_each_string_list_item (item , & l ) {
394
+ switch (next ) {
395
+ case EXPECT_DATE :
396
+ colorfield [colorfield_nr ].hop = approxidate (item -> string );
397
+ next = EXPECT_COLOR ;
398
+ colorfield_nr ++ ;
399
+ ALLOC_GROW (colorfield , colorfield_nr + 1 , colorfield_alloc );
400
+ break ;
401
+ case EXPECT_COLOR :
402
+ if (color_parse (item -> string , colorfield [colorfield_nr ].col ))
403
+ die (_ ("expecting a color: %s" ), item -> string );
404
+ next = EXPECT_DATE ;
405
+ break ;
406
+ }
407
+ }
408
+
409
+ if (next == EXPECT_COLOR )
410
+ die (_ ("must end with a color" ));
411
+
412
+ colorfield [colorfield_nr ].hop = TIME_MAX ;
413
+ }
414
+
415
+ static void setup_default_color_by_age (void )
416
+ {
417
+ parse_color_fields ("blue,12 month ago,white,1 month ago,red" );
418
+ }
419
+
420
+ static void determine_line_heat (struct blame_entry * ent , const char * * dest_color )
421
+ {
422
+ int i = 0 ;
423
+ struct commit_info ci ;
424
+ get_commit_info (ent -> suspect -> commit , & ci , 1 );
425
+
426
+ while (i < colorfield_nr && ci .author_time > colorfield [i ].hop )
427
+ i ++ ;
428
+
429
+ * dest_color = colorfield [i ].col ;
430
+ }
431
+
373
432
static void emit_other (struct blame_scoreboard * sb , struct blame_entry * ent , int opt )
374
433
{
375
434
int cnt ;
@@ -378,12 +437,19 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
378
437
struct commit_info ci ;
379
438
char hex [GIT_MAX_HEXSZ + 1 ];
380
439
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP );
381
- const char * color = NULL , * reset = NULL ;
440
+ const char * default_color = NULL , * color = NULL , * reset = NULL ;
382
441
383
442
get_commit_info (suspect -> commit , & ci , 1 );
384
443
oid_to_hex_r (hex , & suspect -> commit -> object .oid );
385
444
386
445
cp = blame_nth_line (sb , ent -> lno );
446
+
447
+ if (opt & OUTPUT_SHOW_AGE_WITH_COLOR ) {
448
+ determine_line_heat (ent , & default_color );
449
+ color = default_color ;
450
+ reset = GIT_COLOR_RESET ;
451
+ }
452
+
387
453
for (cnt = 0 ; cnt < ent -> num_lines ; cnt ++ ) {
388
454
char ch ;
389
455
int length = (opt & OUTPUT_LONG_OBJECT_NAME ) ? GIT_SHA1_HEXSZ : abbrev ;
@@ -393,8 +459,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
393
459
color = repeated_meta_color ;
394
460
reset = GIT_COLOR_RESET ;
395
461
} else {
396
- color = NULL ;
397
- reset = NULL ;
462
+ color = default_color ? default_color : NULL ;
463
+ reset = default_color ? GIT_COLOR_RESET : NULL ;
398
464
}
399
465
}
400
466
if (color )
@@ -631,6 +697,10 @@ static int git_blame_config(const char *var, const char *value, void *cb)
631
697
value );
632
698
return 0 ;
633
699
}
700
+ if (!strcmp (var , "color.blame.highlightrecent" )) {
701
+ parse_color_fields (value );
702
+ return 0 ;
703
+ }
634
704
635
705
if (git_diff_heuristic_config (var , value , cb ) < 0 )
636
706
return -1 ;
@@ -706,6 +776,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
706
776
OPT_BIT ('e' , "show-email" , & output_option , N_ ("Show author email instead of name (Default: off)" ), OUTPUT_SHOW_EMAIL ),
707
777
OPT_BIT ('w' , NULL , & xdl_opts , N_ ("Ignore whitespace differences" ), XDF_IGNORE_WHITESPACE ),
708
778
OPT_BIT (0 , "color-lines" , & output_option , N_ ("color redundant metadata from previous line differently" ), OUTPUT_COLOR_LINE ),
779
+ OPT_BIT (0 , "color-by-age" , & output_option , N_ ("color lines by age" ), OUTPUT_SHOW_AGE_WITH_COLOR ),
709
780
710
781
/*
711
782
* The following two options are parsed by parse_revision_opt()
@@ -730,6 +801,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
730
801
unsigned int range_i ;
731
802
long anchor ;
732
803
804
+ setup_default_color_by_age ();
733
805
git_config (git_blame_config , & output_option );
734
806
init_revisions (& revs , NULL );
735
807
revs .date_mode = blame_date_mode ;
@@ -972,7 +1044,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
972
1044
strcpy (repeated_meta_color , GIT_COLOR_CYAN );
973
1045
}
974
1046
if (output_option & OUTPUT_ANNOTATE_COMPAT )
975
- output_option &= ~OUTPUT_COLOR_LINE ;
1047
+ output_option &= ~( OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR ) ;
976
1048
977
1049
output (& sb , output_option );
978
1050
free ((void * )sb .final_buf );
0 commit comments