3
3
#include "commit.h"
4
4
#include "pkt-line.h"
5
5
#include "utf8.h"
6
+ #include "interpolate.h"
6
7
7
8
int save_commit_buffer = 1 ;
8
9
@@ -36,8 +37,11 @@ struct cmt_fmt_map {
36
37
{ "full" , 5 , CMIT_FMT_FULL },
37
38
{ "fuller" , 5 , CMIT_FMT_FULLER },
38
39
{ "oneline" , 1 , CMIT_FMT_ONELINE },
40
+ { "format:" , 7 , CMIT_FMT_USERFORMAT },
39
41
};
40
42
43
+ static char * user_format ;
44
+
41
45
enum cmit_fmt get_commit_format (const char * arg )
42
46
{
43
47
int i ;
@@ -46,6 +50,12 @@ enum cmit_fmt get_commit_format(const char *arg)
46
50
return CMIT_FMT_DEFAULT ;
47
51
if (* arg == '=' )
48
52
arg ++ ;
53
+ if (!prefixcmp (arg , "format:" )) {
54
+ if (user_format )
55
+ free (user_format );
56
+ user_format = xstrdup (arg + 7 );
57
+ return CMIT_FMT_USERFORMAT ;
58
+ }
49
59
for (i = 0 ; i < ARRAY_SIZE (cmt_fmts ); i ++ ) {
50
60
if (!strncmp (arg , cmt_fmts [i ].n , cmt_fmts [i ].cmp_len ) &&
51
61
!strncmp (arg , cmt_fmts [i ].n , strlen (arg )))
@@ -710,6 +720,188 @@ static char *logmsg_reencode(const struct commit *commit,
710
720
return out ;
711
721
}
712
722
723
+ static char * xstrndup (const char * text , int len )
724
+ {
725
+ char * result = xmalloc (len + 1 );
726
+ memcpy (result , text , len );
727
+ result [len ] = '\0' ;
728
+ return result ;
729
+ }
730
+
731
+ static void fill_person (struct interp * table , const char * msg , int len )
732
+ {
733
+ int start , end , tz = 0 ;
734
+ unsigned long date ;
735
+ char * ep ;
736
+
737
+ /* parse name */
738
+ for (end = 0 ; end < len && msg [end ] != '<' ; end ++ )
739
+ ; /* do nothing */
740
+ start = end + 1 ;
741
+ while (end > 0 && isspace (msg [end - 1 ]))
742
+ end -- ;
743
+ table [0 ].value = xstrndup (msg , end );
744
+
745
+ if (start >= len )
746
+ return ;
747
+
748
+ /* parse email */
749
+ for (end = start + 1 ; end < len && msg [end ] != '>' ; end ++ )
750
+ ; /* do nothing */
751
+
752
+ if (end >= len )
753
+ return ;
754
+
755
+ table [1 ].value = xstrndup (msg + start , end - start );
756
+
757
+ /* parse date */
758
+ for (start = end + 1 ; start < len && isspace (msg [start ]); start ++ )
759
+ ; /* do nothing */
760
+ if (start >= len )
761
+ return ;
762
+ date = strtoul (msg + start , & ep , 10 );
763
+ if (msg + start == ep )
764
+ return ;
765
+
766
+ table [5 ].value = xstrndup (msg + start , ep - msg + start );
767
+
768
+ /* parse tz */
769
+ for (start = ep - msg + 1 ; start < len && isspace (msg [start ]); start ++ )
770
+ ; /* do nothing */
771
+ if (start + 1 < len ) {
772
+ tz = strtoul (msg + start + 1 , NULL , 10 );
773
+ if (msg [start ] == '-' )
774
+ tz = - tz ;
775
+ }
776
+
777
+ interp_set_entry (table , 2 , show_date (date , tz , 0 ));
778
+ interp_set_entry (table , 3 , show_rfc2822_date (date , tz ));
779
+ interp_set_entry (table , 4 , show_date (date , tz , 1 ));
780
+ }
781
+
782
+ static long format_commit_message (const struct commit * commit ,
783
+ const char * msg , char * buf , unsigned long space )
784
+ {
785
+ struct interp table [] = {
786
+ { "%H" }, /* commit hash */
787
+ { "%h" }, /* abbreviated commit hash */
788
+ { "%T" }, /* tree hash */
789
+ { "%t" }, /* abbreviated tree hash */
790
+ { "%P" }, /* parent hashes */
791
+ { "%p" }, /* abbreviated parent hashes */
792
+ { "%an" }, /* author name */
793
+ { "%ae" }, /* author email */
794
+ { "%ad" }, /* author date */
795
+ { "%aD" }, /* author date, RFC2822 style */
796
+ { "%ar" }, /* author date, relative */
797
+ { "%at" }, /* author date, UNIX timestamp */
798
+ { "%cn" }, /* committer name */
799
+ { "%ce" }, /* committer email */
800
+ { "%cd" }, /* committer date */
801
+ { "%cD" }, /* committer date, RFC2822 style */
802
+ { "%cr" }, /* committer date, relative */
803
+ { "%ct" }, /* committer date, UNIX timestamp */
804
+ { "%e" }, /* encoding */
805
+ { "%s" }, /* subject */
806
+ { "%b" }, /* body */
807
+ { "%Cred" }, /* red */
808
+ { "%Cgreen" }, /* green */
809
+ { "%Cblue" }, /* blue */
810
+ { "%Creset" }, /* reset color */
811
+ { "%n" } /* newline */
812
+ };
813
+ enum interp_index {
814
+ IHASH = 0 , IHASH_ABBREV ,
815
+ ITREE , ITREE_ABBREV ,
816
+ IPARENTS , IPARENTS_ABBREV ,
817
+ IAUTHOR_NAME , IAUTHOR_EMAIL ,
818
+ IAUTHOR_DATE , IAUTHOR_DATE_RFC2822 , IAUTHOR_DATE_RELATIVE ,
819
+ IAUTHOR_TIMESTAMP ,
820
+ ICOMMITTER_NAME , ICOMMITTER_EMAIL ,
821
+ ICOMMITTER_DATE , ICOMMITTER_DATE_RFC2822 ,
822
+ ICOMMITTER_DATE_RELATIVE , ICOMMITTER_TIMESTAMP ,
823
+ IENCODING ,
824
+ ISUBJECT ,
825
+ IBODY ,
826
+ IRED , IGREEN , IBLUE , IRESET_COLOR ,
827
+ INEWLINE
828
+ };
829
+ struct commit_list * p ;
830
+ char parents [1024 ];
831
+ int i ;
832
+ enum { HEADER , SUBJECT , BODY } state ;
833
+
834
+ if (INEWLINE + 1 != ARRAY_SIZE (table ))
835
+ die ("invalid interp table!" );
836
+
837
+ /* these are independent of the commit */
838
+ interp_set_entry (table , IRED , "\033[31m" );
839
+ interp_set_entry (table , IGREEN , "\033[32m" );
840
+ interp_set_entry (table , IBLUE , "\033[34m" );
841
+ interp_set_entry (table , IRESET_COLOR , "\033[m" );
842
+ interp_set_entry (table , INEWLINE , "\n" );
843
+
844
+ /* these depend on the commit */
845
+ if (!commit -> object .parsed )
846
+ parse_object (commit -> object .sha1 );
847
+ interp_set_entry (table , IHASH , sha1_to_hex (commit -> object .sha1 ));
848
+ interp_set_entry (table , IHASH_ABBREV ,
849
+ find_unique_abbrev (commit -> object .sha1 ,
850
+ DEFAULT_ABBREV ));
851
+ interp_set_entry (table , ITREE , sha1_to_hex (commit -> tree -> object .sha1 ));
852
+ interp_set_entry (table , ITREE_ABBREV ,
853
+ find_unique_abbrev (commit -> tree -> object .sha1 ,
854
+ DEFAULT_ABBREV ));
855
+ for (i = 0 , p = commit -> parents ;
856
+ p && i < sizeof (parents ) - 1 ;
857
+ p = p -> next )
858
+ i += snprintf (parents + i , sizeof (parents ) - i - 1 , "%s " ,
859
+ sha1_to_hex (p -> item -> object .sha1 ));
860
+ interp_set_entry (table , IPARENTS , parents );
861
+ for (i = 0 , p = commit -> parents ;
862
+ p && i < sizeof (parents ) - 1 ;
863
+ p = p -> next )
864
+ i += snprintf (parents + i , sizeof (parents ) - i - 1 , "%s " ,
865
+ find_unique_abbrev (p -> item -> object .sha1 ,
866
+ DEFAULT_ABBREV ));
867
+ interp_set_entry (table , IPARENTS_ABBREV , parents );
868
+
869
+ for (i = 0 , state = HEADER ; msg [i ] && state < BODY ; i ++ ) {
870
+ int eol ;
871
+ for (eol = i ; msg [eol ] && msg [eol ] != '\n' ; eol ++ )
872
+ ; /* do nothing */
873
+
874
+ if (state == SUBJECT ) {
875
+ table [ISUBJECT ].value = xstrndup (msg + i , eol - i );
876
+ i = eol ;
877
+ }
878
+ if (i == eol ) {
879
+ state ++ ;
880
+ /* strip empty lines */
881
+ while (msg [eol + 1 ] == '\n' )
882
+ eol ++ ;
883
+ } else if (!prefixcmp (msg + i , "author " ))
884
+ fill_person (table + IAUTHOR_NAME ,
885
+ msg + i + 7 , eol - i - 7 );
886
+ else if (!prefixcmp (msg + i , "committer " ))
887
+ fill_person (table + ICOMMITTER_NAME ,
888
+ msg + i + 10 , eol - i - 10 );
889
+ else if (!prefixcmp (msg + i , "encoding " ))
890
+ table [IENCODING ].value = xstrndup (msg + i , eol - i );
891
+ i = eol ;
892
+ }
893
+ if (msg [i ])
894
+ table [IBODY ].value = xstrdup (msg + i );
895
+ for (i = 0 ; i < ARRAY_SIZE (table ); i ++ )
896
+ if (!table [i ].value )
897
+ interp_set_entry (table , i , "<unknown>" );
898
+
899
+ interpolate (buf , space , user_format , table , ARRAY_SIZE (table ));
900
+ interp_clear_table (table , ARRAY_SIZE (table ));
901
+
902
+ return strlen (buf );
903
+ }
904
+
713
905
unsigned long pretty_print_commit (enum cmit_fmt fmt ,
714
906
const struct commit * commit ,
715
907
unsigned long len ,
@@ -727,6 +919,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
727
919
char * reencoded ;
728
920
char * encoding ;
729
921
922
+ if (fmt == CMIT_FMT_USERFORMAT )
923
+ return format_commit_message (commit , msg , buf , space );
924
+
730
925
encoding = (git_log_output_encoding
731
926
? git_log_output_encoding
732
927
: git_commit_encoding );
0 commit comments