Fossil SCM

Extend the timeline command to allow customized "pretty" formats.

danield 2020-12-18 15:05 trunk
Commit 5d3239bf588f7f9b7212530c6226bb960f0037a90e085eb6a36d74eba034dff5
--- src/descendants.c
+++ src/descendants.c
@@ -382,11 +382,11 @@
382382
"%s"
383383
" AND event.objid IN (SELECT rid FROM leaves)"
384384
" ORDER BY event.mtime DESC",
385385
timeline_query_for_tty()
386386
);
387
- print_timeline(&q, 0, width, 0);
387
+ print_timeline(&q, 0, width, 0, 0);
388388
db_finalize(&q);
389389
}
390390
391391
/*
392392
** COMMAND: leaves*
393393
--- src/descendants.c
+++ src/descendants.c
@@ -382,11 +382,11 @@
382 "%s"
383 " AND event.objid IN (SELECT rid FROM leaves)"
384 " ORDER BY event.mtime DESC",
385 timeline_query_for_tty()
386 );
387 print_timeline(&q, 0, width, 0);
388 db_finalize(&q);
389 }
390
391 /*
392 ** COMMAND: leaves*
393
--- src/descendants.c
+++ src/descendants.c
@@ -382,11 +382,11 @@
382 "%s"
383 " AND event.objid IN (SELECT rid FROM leaves)"
384 " ORDER BY event.mtime DESC",
385 timeline_query_for_tty()
386 );
387 print_timeline(&q, 0, width, 0, 0);
388 db_finalize(&q);
389 }
390
391 /*
392 ** COMMAND: leaves*
393
+1 -1
--- src/search.c
+++ src/search.c
@@ -643,11 +643,11 @@
643643
blob_append_sql(&sql,"AND x>%d ", iBest/3);
644644
}
645645
blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
646646
db_prepare(&q, "%s", blob_sql_text(&sql));
647647
blob_reset(&sql);
648
- print_timeline(&q, nLimit, width, 0);
648
+ print_timeline(&q, nLimit, width, 0, 0);
649649
db_finalize(&q);
650650
}
651651
652652
#if INTERFACE
653653
/* What to search for */
654654
--- src/search.c
+++ src/search.c
@@ -643,11 +643,11 @@
643 blob_append_sql(&sql,"AND x>%d ", iBest/3);
644 }
645 blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
646 db_prepare(&q, "%s", blob_sql_text(&sql));
647 blob_reset(&sql);
648 print_timeline(&q, nLimit, width, 0);
649 db_finalize(&q);
650 }
651
652 #if INTERFACE
653 /* What to search for */
654
--- src/search.c
+++ src/search.c
@@ -643,11 +643,11 @@
643 blob_append_sql(&sql,"AND x>%d ", iBest/3);
644 }
645 blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
646 db_prepare(&q, "%s", blob_sql_text(&sql));
647 blob_reset(&sql);
648 print_timeline(&q, nLimit, width, 0, 0);
649 db_finalize(&q);
650 }
651
652 #if INTERFACE
653 /* What to search for */
654
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -541,11 +541,11 @@
541541
" ORDER BY event.mtime DESC /*sort*/",
542542
timeline_query_for_tty(), zType, tagid
543543
);
544544
db_prepare(&q, "%s", blob_sql_text(&sql));
545545
blob_reset(&sql);
546
- print_timeline(&q, nFindLimit, 79, 0);
546
+ print_timeline(&q, nFindLimit, 79, 0, 0);
547547
db_finalize(&q);
548548
}
549549
}
550550
}else
551551
552552
--- src/tag.c
+++ src/tag.c
@@ -541,11 +541,11 @@
541 " ORDER BY event.mtime DESC /*sort*/",
542 timeline_query_for_tty(), zType, tagid
543 );
544 db_prepare(&q, "%s", blob_sql_text(&sql));
545 blob_reset(&sql);
546 print_timeline(&q, nFindLimit, 79, 0);
547 db_finalize(&q);
548 }
549 }
550 }else
551
552
--- src/tag.c
+++ src/tag.c
@@ -541,11 +541,11 @@
541 " ORDER BY event.mtime DESC /*sort*/",
542 timeline_query_for_tty(), zType, tagid
543 );
544 db_prepare(&q, "%s", blob_sql_text(&sql));
545 blob_reset(&sql);
546 print_timeline(&q, nFindLimit, 79, 0, 0);
547 db_finalize(&q);
548 }
549 }
550 }else
551
552
+162 -7
--- src/timeline.c
+++ src/timeline.c
@@ -2727,10 +2727,106 @@
27272727
@ &nbsp;&darr;</a>
27282728
}
27292729
document_emit_js(/*handles pikchrs rendered above*/);
27302730
style_finish_page("timeline");
27312731
}
2732
+
2733
+/*
2734
+** Translate a timeline entry into the printable format by
2735
+** converting every %-substitutions as follows:
2736
+**
2737
+** %n newline
2738
+** %% a raw %
2739
+** %H commit hash
2740
+** %h abbreviated commit hash
2741
+** %a author name
2742
+** %d date
2743
+** %c comment (\n, \t replaced by space, \r deleted)
2744
+** %b branch
2745
+** %t tags
2746
+** %p prefix (zero or more of: *CURRENT*, *MERGE*, *FORK*,
2747
+** *UNPUBLISHED*, *LEAF*, *BRANCH*)
2748
+**
2749
+** The returned string is obtained from fossil_malloc() and should
2750
+** be freed by the caller.
2751
+*/
2752
+static char *timeline_entry_subst(
2753
+ const char *zFormat,
2754
+ int *nLine,
2755
+ const char *zId,
2756
+ const char *zDate,
2757
+ const char *zUser,
2758
+ const char *zCom,
2759
+ const char *zBranch,
2760
+ const char *zTags,
2761
+ const char *zPrefix
2762
+){
2763
+ Blob co;
2764
+ int j;
2765
+ blob_init(&co, 0, 0);
2766
+ *nLine = 1;
2767
+ /* Replace LF and tab with space, delete CR */
2768
+ while( zCom[0] ){
2769
+ for(j=0; zCom[j] && zCom[j]!='\r' && zCom[j]!='\n' && zCom[j]!='\t'; j++){}
2770
+ blob_append(&co, zCom, j);
2771
+ if( zCom[j]==0 ) break;
2772
+ if( zCom[j]!='\r')
2773
+ blob_append(&co, " ", 1);
2774
+ zCom += j+1;
2775
+ }
2776
+ blob_str(&co);
2777
+
2778
+ Blob r;
2779
+ int i;
2780
+ blob_init(&r, 0, 0);
2781
+ while( zFormat[0] ){
2782
+ for(i=0; zFormat[i] && zFormat[i]!='%'; i++){}
2783
+ blob_append(&r, zFormat, i);
2784
+ if( zFormat[i]==0 ) break;
2785
+ if( zFormat[i+1]=='%' ){
2786
+ blob_append(&r, "%", 1);
2787
+ zFormat += i+2;
2788
+ }else if( zFormat[i+1]=='n' ){
2789
+ blob_append(&r, "\n", 1);
2790
+ *nLine += 1;
2791
+ zFormat += i+2;
2792
+ }else if( zFormat[i+1]=='H' ){
2793
+ blob_append(&r, zId, -1);
2794
+ zFormat += i+2;
2795
+ }else if( zFormat[i+1]=='h' ){
2796
+ char *zFree = 0;
2797
+ zFree = mprintf("%S", zId);
2798
+ blob_append(&r, zFree, -1);
2799
+ fossil_free(zFree);
2800
+ zFormat += i+2;
2801
+ }else if( zFormat[i+1]=='d' ){
2802
+ blob_append(&r, zDate, -1);
2803
+ zFormat += i+2;
2804
+ }else if( zFormat[i+1]=='a' ){
2805
+ blob_append(&r, zUser, -1);
2806
+ zFormat += i+2;
2807
+ }else if( zFormat[i+1]=='c' ){
2808
+ blob_append(&r, co.aData, -1);
2809
+ zFormat += i+2;
2810
+ }else if( zFormat[i+1]=='b' ){
2811
+ if( zBranch ) blob_append(&r, zBranch, -1);
2812
+ zFormat += i+2;
2813
+ }else if( zFormat[i+1]=='t' ){
2814
+ blob_append(&r, zTags, -1);
2815
+ zFormat += i+2;
2816
+ }else if( zFormat[i+1]=='p' ){
2817
+ blob_append(&r, zPrefix, -1);
2818
+ zFormat += i+2;
2819
+ }else{
2820
+ blob_append(&r, zFormat+i, 1);
2821
+ zFormat += i+1;
2822
+ }
2823
+ }
2824
+ fossil_free(co.aData);
2825
+ blob_str(&r);
2826
+ return r.aData;
2827
+}
27322828
27332829
/*
27342830
** The input query q selects various records. Print a human-readable
27352831
** summary of those records.
27362832
**
@@ -2745,18 +2841,21 @@
27452841
** The query should return these columns:
27462842
**
27472843
** 0. rid
27482844
** 1. uuid
27492845
** 2. Date/Time
2750
-** 3. Comment string and user
2846
+** 3. Comment string, user, and tags
27512847
** 4. Number of non-merge children
27522848
** 5. Number of parents
27532849
** 6. mtime
27542850
** 7. branch
27552851
** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
2852
+** 9. comment
2853
+** 10. user
2854
+** 11. tags
27562855
*/
2757
-void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
2856
+void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat, int verboseFlag){
27582857
int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
27592858
int nLine = 0;
27602859
int nEntry = 0;
27612860
char zPrevDate[20];
27622861
const char *zCurrentUuid = 0;
@@ -2775,11 +2874,15 @@
27752874
const char *zId = db_column_text(q, 1);
27762875
const char *zDate = db_column_text(q, 2);
27772876
const char *zCom = db_column_text(q, 3);
27782877
int nChild = db_column_int(q, 4);
27792878
int nParent = db_column_int(q, 5);
2879
+ const char *zBranch = db_column_text(q, 7);
27802880
const char *zType = db_column_text(q, 8);
2881
+ const char *zComShort = db_column_text(q, 9);
2882
+ const char *zUserShort = db_column_text(q, 10);
2883
+ const char *zTags = db_column_text(q, 11);
27812884
char *zFree = 0;
27822885
int n = 0;
27832886
char zPrefix[80];
27842887
27852888
if( nAbsLimit!=0 ){
@@ -2789,17 +2892,18 @@
27892892
}else if( nEntry>=nAbsLimit ){
27902893
fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
27912894
break; /* entry count limit hit, stop. */
27922895
}
27932896
}
2794
- if( fossil_strnicmp(zDate, zPrevDate, 10) ){
2897
+ if( zFormat == 0 && fossil_strnicmp(zDate, zPrevDate, 10) ){
27952898
fossil_print("=== %.10s ===\n", zDate);
27962899
memcpy(zPrevDate, zDate, 10);
27972900
nLine++; /* record another line */
27982901
}
27992902
if( zCom==0 ) zCom = "";
2800
- fossil_print("%.8s ", &zDate[11]);
2903
+ if( zFormat == 0 )
2904
+ fossil_print("%.8s ", &zDate[11]);
28012905
zPrefix[0] = 0;
28022906
if( nParent>1 ){
28032907
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
28042908
n = strlen(zPrefix);
28052909
}
@@ -2831,12 +2935,27 @@
28312935
zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
28322936
}
28332937
}else{
28342938
zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
28352939
}
2836
- /* record another X lines */
2837
- nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
2940
+
2941
+ if( zFormat ){
2942
+ if( nChild==0 ){
2943
+ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
2944
+ }
2945
+ char *zEntry;
2946
+ int nEntryLine = 0;
2947
+ zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate, zUserShort,
2948
+ zComShort, zBranch, zTags, zPrefix);
2949
+ nLine += nEntryLine;
2950
+ fossil_print("%s\n", zEntry);
2951
+ fossil_free(zEntry);
2952
+ }
2953
+ else{
2954
+ /* record another X lines */
2955
+ nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
2956
+ }
28382957
fossil_free(zFree);
28392958
28402959
if(verboseFlag){
28412960
if( !fchngQueryInit ){
28422961
db_prepare(&fchngQuery,
@@ -2865,10 +2984,13 @@
28652984
}
28662985
nLine++; /* record another line */
28672986
}
28682987
db_reset(&fchngQuery);
28692988
}
2989
+ /* Except for "oneline", separate formatted entries by one empty line */
2990
+ if( zFormat && fossil_strcmp(zFormat, "%h %c")!=0 )
2991
+ fossil_print("\n");
28702992
nEntry++; /* record another complete entry */
28712993
}
28722994
if( rc==SQLITE_DONE ){
28732995
/* Did the underlying query actually have all entries? */
28742996
if( nAbsLimit==0 ){
@@ -2902,10 +3024,17 @@
29023024
@ AS primPlinkCount,
29033025
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
29043026
@ event.mtime AS mtime,
29053027
@ tagxref.value AS branch,
29063028
@ event.type
3029
+ @ , coalesce(ecomment,comment) AS comment0
3030
+ @ , coalesce(euser,user,'?') AS user0
3031
+ @ , (SELECT case when length(x)>0 then x else '' end
3032
+ @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
3033
+ @ FROM tag, tagxref
3034
+ @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
3035
+ @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags
29073036
@ FROM tag CROSS JOIN event CROSS JOIN blob
29083037
@ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
29093038
@ AND tagxref.tagtype>0
29103039
@ AND tagxref.rid=blob.rid
29113040
@ WHERE blob.rid=event.objid
@@ -2931,10 +3060,11 @@
29313060
*/
29323061
static int fossil_is_julianday(const char *zDate){
29333062
return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd"
29343063
" WHERE jd IS NOT NULL)", zDate);
29353064
}
3065
+
29363066
29373067
/*
29383068
** COMMAND: timeline
29393069
**
29403070
** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS?
@@ -2977,10 +3107,27 @@
29773107
** etc.) after the check-in comment.
29783108
** -W|--width N Width of lines (default is to auto-detect). N must be
29793109
** either greater than 20 or it ust be zero 0 to
29803110
** indicate no limit, resulting in a single line per
29813111
** entry.
3112
+** -F|--format Entry format. Values "oneline", "medium", and "full"
3113
+** get mapped to the full options below. Otherwise a
3114
+** string which can contain these placeholders:
3115
+** %n newline
3116
+** %% a raw %
3117
+** %H commit hash
3118
+** %h abbreviated commit hash
3119
+** %a author name
3120
+** %d date
3121
+** %c comment (NL, TAB replaced by space, LF deleted)
3122
+** %b branch
3123
+** %t tags
3124
+** %p zero or more of: *CURRENT*, *MERGE*, *FORK*,
3125
+** *UNPUBLISHED*, *LEAF*, *BRANCH*
3126
+** --oneline Show only short hash and comment for each entry
3127
+** --medium Medium-verbose entry formatting
3128
+** --full Extra verbose entry formatting
29823129
** -R REPO_FILE Specifies the repository db to use. Default is
29833130
** the current checkout's repository.
29843131
*/
29853132
void timeline_cmd(void){
29863133
Stmt q;
@@ -2996,10 +3143,11 @@
29963143
Blob uuid;
29973144
int mode = TIMELINE_MODE_NONE;
29983145
int verboseFlag = 0 ;
29993146
int iOffset;
30003147
const char *zFilePattern = 0;
3148
+ const char *zFormat = 0;
30013149
Blob treeName;
30023150
int showSql = 0;
30033151
30043152
verboseFlag = find_option("verbose","v", 0)!=0;
30053153
if( !verboseFlag){
@@ -3008,10 +3156,17 @@
30083156
db_find_and_open_repository(0, 0);
30093157
zLimit = find_option("limit","n",1);
30103158
zWidth = find_option("width","W",1);
30113159
zType = find_option("type","t",1);
30123160
zFilePattern = find_option("path","p",1);
3161
+ zFormat = find_option("format","F",1);
3162
+ if( find_option("oneline",0,0)!= 0 || fossil_strcmp(zFormat,"oneline")==0 )
3163
+ zFormat = "%h %c";
3164
+ if( find_option("medium",0,0)!= 0 || fossil_strcmp(zFormat,"medium")==0 )
3165
+ zFormat = "Commit: %h%nDate: %d%nAuthor: %a%nComment: %c";
3166
+ if( find_option("full",0,0)!= 0 || fossil_strcmp(zFormat,"full")==0 )
3167
+ zFormat = "Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%nBranch: %b%nTags: %t%nPrefix: %p";
30133168
showSql = find_option("sql",0,0)!=0;
30143169
30153170
if( !zLimit ){
30163171
zLimit = find_option("count",0,1);
30173172
}
@@ -3157,11 +3312,11 @@
31573312
if( showSql ){
31583313
fossil_print("%s\n", blob_str(&sql));
31593314
}
31603315
db_prepare_blob(&q, &sql);
31613316
blob_reset(&sql);
3162
- print_timeline(&q, n, width, verboseFlag);
3317
+ print_timeline(&q, n, width, zFormat, verboseFlag);
31633318
db_finalize(&q);
31643319
}
31653320
31663321
/*
31673322
** WEBPAGE: thisdayinhistory
31683323
--- src/timeline.c
+++ src/timeline.c
@@ -2727,10 +2727,106 @@
2727 @ &nbsp;&darr;</a>
2728 }
2729 document_emit_js(/*handles pikchrs rendered above*/);
2730 style_finish_page("timeline");
2731 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2732
2733 /*
2734 ** The input query q selects various records. Print a human-readable
2735 ** summary of those records.
2736 **
@@ -2745,18 +2841,21 @@
2745 ** The query should return these columns:
2746 **
2747 ** 0. rid
2748 ** 1. uuid
2749 ** 2. Date/Time
2750 ** 3. Comment string and user
2751 ** 4. Number of non-merge children
2752 ** 5. Number of parents
2753 ** 6. mtime
2754 ** 7. branch
2755 ** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
 
 
 
2756 */
2757 void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
2758 int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
2759 int nLine = 0;
2760 int nEntry = 0;
2761 char zPrevDate[20];
2762 const char *zCurrentUuid = 0;
@@ -2775,11 +2874,15 @@
2775 const char *zId = db_column_text(q, 1);
2776 const char *zDate = db_column_text(q, 2);
2777 const char *zCom = db_column_text(q, 3);
2778 int nChild = db_column_int(q, 4);
2779 int nParent = db_column_int(q, 5);
 
2780 const char *zType = db_column_text(q, 8);
 
 
 
2781 char *zFree = 0;
2782 int n = 0;
2783 char zPrefix[80];
2784
2785 if( nAbsLimit!=0 ){
@@ -2789,17 +2892,18 @@
2789 }else if( nEntry>=nAbsLimit ){
2790 fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
2791 break; /* entry count limit hit, stop. */
2792 }
2793 }
2794 if( fossil_strnicmp(zDate, zPrevDate, 10) ){
2795 fossil_print("=== %.10s ===\n", zDate);
2796 memcpy(zPrevDate, zDate, 10);
2797 nLine++; /* record another line */
2798 }
2799 if( zCom==0 ) zCom = "";
2800 fossil_print("%.8s ", &zDate[11]);
 
2801 zPrefix[0] = 0;
2802 if( nParent>1 ){
2803 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
2804 n = strlen(zPrefix);
2805 }
@@ -2831,12 +2935,27 @@
2831 zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
2832 }
2833 }else{
2834 zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
2835 }
2836 /* record another X lines */
2837 nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2838 fossil_free(zFree);
2839
2840 if(verboseFlag){
2841 if( !fchngQueryInit ){
2842 db_prepare(&fchngQuery,
@@ -2865,10 +2984,13 @@
2865 }
2866 nLine++; /* record another line */
2867 }
2868 db_reset(&fchngQuery);
2869 }
 
 
 
2870 nEntry++; /* record another complete entry */
2871 }
2872 if( rc==SQLITE_DONE ){
2873 /* Did the underlying query actually have all entries? */
2874 if( nAbsLimit==0 ){
@@ -2902,10 +3024,17 @@
2902 @ AS primPlinkCount,
2903 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
2904 @ event.mtime AS mtime,
2905 @ tagxref.value AS branch,
2906 @ event.type
 
 
 
 
 
 
 
2907 @ FROM tag CROSS JOIN event CROSS JOIN blob
2908 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
2909 @ AND tagxref.tagtype>0
2910 @ AND tagxref.rid=blob.rid
2911 @ WHERE blob.rid=event.objid
@@ -2931,10 +3060,11 @@
2931 */
2932 static int fossil_is_julianday(const char *zDate){
2933 return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd"
2934 " WHERE jd IS NOT NULL)", zDate);
2935 }
 
2936
2937 /*
2938 ** COMMAND: timeline
2939 **
2940 ** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS?
@@ -2977,10 +3107,27 @@
2977 ** etc.) after the check-in comment.
2978 ** -W|--width N Width of lines (default is to auto-detect). N must be
2979 ** either greater than 20 or it ust be zero 0 to
2980 ** indicate no limit, resulting in a single line per
2981 ** entry.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2982 ** -R REPO_FILE Specifies the repository db to use. Default is
2983 ** the current checkout's repository.
2984 */
2985 void timeline_cmd(void){
2986 Stmt q;
@@ -2996,10 +3143,11 @@
2996 Blob uuid;
2997 int mode = TIMELINE_MODE_NONE;
2998 int verboseFlag = 0 ;
2999 int iOffset;
3000 const char *zFilePattern = 0;
 
3001 Blob treeName;
3002 int showSql = 0;
3003
3004 verboseFlag = find_option("verbose","v", 0)!=0;
3005 if( !verboseFlag){
@@ -3008,10 +3156,17 @@
3008 db_find_and_open_repository(0, 0);
3009 zLimit = find_option("limit","n",1);
3010 zWidth = find_option("width","W",1);
3011 zType = find_option("type","t",1);
3012 zFilePattern = find_option("path","p",1);
 
 
 
 
 
 
 
3013 showSql = find_option("sql",0,0)!=0;
3014
3015 if( !zLimit ){
3016 zLimit = find_option("count",0,1);
3017 }
@@ -3157,11 +3312,11 @@
3157 if( showSql ){
3158 fossil_print("%s\n", blob_str(&sql));
3159 }
3160 db_prepare_blob(&q, &sql);
3161 blob_reset(&sql);
3162 print_timeline(&q, n, width, verboseFlag);
3163 db_finalize(&q);
3164 }
3165
3166 /*
3167 ** WEBPAGE: thisdayinhistory
3168
--- src/timeline.c
+++ src/timeline.c
@@ -2727,10 +2727,106 @@
2727 @ &nbsp;&darr;</a>
2728 }
2729 document_emit_js(/*handles pikchrs rendered above*/);
2730 style_finish_page("timeline");
2731 }
2732
2733 /*
2734 ** Translate a timeline entry into the printable format by
2735 ** converting every %-substitutions as follows:
2736 **
2737 ** %n newline
2738 ** %% a raw %
2739 ** %H commit hash
2740 ** %h abbreviated commit hash
2741 ** %a author name
2742 ** %d date
2743 ** %c comment (\n, \t replaced by space, \r deleted)
2744 ** %b branch
2745 ** %t tags
2746 ** %p prefix (zero or more of: *CURRENT*, *MERGE*, *FORK*,
2747 ** *UNPUBLISHED*, *LEAF*, *BRANCH*)
2748 **
2749 ** The returned string is obtained from fossil_malloc() and should
2750 ** be freed by the caller.
2751 */
2752 static char *timeline_entry_subst(
2753 const char *zFormat,
2754 int *nLine,
2755 const char *zId,
2756 const char *zDate,
2757 const char *zUser,
2758 const char *zCom,
2759 const char *zBranch,
2760 const char *zTags,
2761 const char *zPrefix
2762 ){
2763 Blob co;
2764 int j;
2765 blob_init(&co, 0, 0);
2766 *nLine = 1;
2767 /* Replace LF and tab with space, delete CR */
2768 while( zCom[0] ){
2769 for(j=0; zCom[j] && zCom[j]!='\r' && zCom[j]!='\n' && zCom[j]!='\t'; j++){}
2770 blob_append(&co, zCom, j);
2771 if( zCom[j]==0 ) break;
2772 if( zCom[j]!='\r')
2773 blob_append(&co, " ", 1);
2774 zCom += j+1;
2775 }
2776 blob_str(&co);
2777
2778 Blob r;
2779 int i;
2780 blob_init(&r, 0, 0);
2781 while( zFormat[0] ){
2782 for(i=0; zFormat[i] && zFormat[i]!='%'; i++){}
2783 blob_append(&r, zFormat, i);
2784 if( zFormat[i]==0 ) break;
2785 if( zFormat[i+1]=='%' ){
2786 blob_append(&r, "%", 1);
2787 zFormat += i+2;
2788 }else if( zFormat[i+1]=='n' ){
2789 blob_append(&r, "\n", 1);
2790 *nLine += 1;
2791 zFormat += i+2;
2792 }else if( zFormat[i+1]=='H' ){
2793 blob_append(&r, zId, -1);
2794 zFormat += i+2;
2795 }else if( zFormat[i+1]=='h' ){
2796 char *zFree = 0;
2797 zFree = mprintf("%S", zId);
2798 blob_append(&r, zFree, -1);
2799 fossil_free(zFree);
2800 zFormat += i+2;
2801 }else if( zFormat[i+1]=='d' ){
2802 blob_append(&r, zDate, -1);
2803 zFormat += i+2;
2804 }else if( zFormat[i+1]=='a' ){
2805 blob_append(&r, zUser, -1);
2806 zFormat += i+2;
2807 }else if( zFormat[i+1]=='c' ){
2808 blob_append(&r, co.aData, -1);
2809 zFormat += i+2;
2810 }else if( zFormat[i+1]=='b' ){
2811 if( zBranch ) blob_append(&r, zBranch, -1);
2812 zFormat += i+2;
2813 }else if( zFormat[i+1]=='t' ){
2814 blob_append(&r, zTags, -1);
2815 zFormat += i+2;
2816 }else if( zFormat[i+1]=='p' ){
2817 blob_append(&r, zPrefix, -1);
2818 zFormat += i+2;
2819 }else{
2820 blob_append(&r, zFormat+i, 1);
2821 zFormat += i+1;
2822 }
2823 }
2824 fossil_free(co.aData);
2825 blob_str(&r);
2826 return r.aData;
2827 }
2828
2829 /*
2830 ** The input query q selects various records. Print a human-readable
2831 ** summary of those records.
2832 **
@@ -2745,18 +2841,21 @@
2841 ** The query should return these columns:
2842 **
2843 ** 0. rid
2844 ** 1. uuid
2845 ** 2. Date/Time
2846 ** 3. Comment string, user, and tags
2847 ** 4. Number of non-merge children
2848 ** 5. Number of parents
2849 ** 6. mtime
2850 ** 7. branch
2851 ** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
2852 ** 9. comment
2853 ** 10. user
2854 ** 11. tags
2855 */
2856 void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat, int verboseFlag){
2857 int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
2858 int nLine = 0;
2859 int nEntry = 0;
2860 char zPrevDate[20];
2861 const char *zCurrentUuid = 0;
@@ -2775,11 +2874,15 @@
2874 const char *zId = db_column_text(q, 1);
2875 const char *zDate = db_column_text(q, 2);
2876 const char *zCom = db_column_text(q, 3);
2877 int nChild = db_column_int(q, 4);
2878 int nParent = db_column_int(q, 5);
2879 const char *zBranch = db_column_text(q, 7);
2880 const char *zType = db_column_text(q, 8);
2881 const char *zComShort = db_column_text(q, 9);
2882 const char *zUserShort = db_column_text(q, 10);
2883 const char *zTags = db_column_text(q, 11);
2884 char *zFree = 0;
2885 int n = 0;
2886 char zPrefix[80];
2887
2888 if( nAbsLimit!=0 ){
@@ -2789,17 +2892,18 @@
2892 }else if( nEntry>=nAbsLimit ){
2893 fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
2894 break; /* entry count limit hit, stop. */
2895 }
2896 }
2897 if( zFormat == 0 && fossil_strnicmp(zDate, zPrevDate, 10) ){
2898 fossil_print("=== %.10s ===\n", zDate);
2899 memcpy(zPrevDate, zDate, 10);
2900 nLine++; /* record another line */
2901 }
2902 if( zCom==0 ) zCom = "";
2903 if( zFormat == 0 )
2904 fossil_print("%.8s ", &zDate[11]);
2905 zPrefix[0] = 0;
2906 if( nParent>1 ){
2907 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
2908 n = strlen(zPrefix);
2909 }
@@ -2831,12 +2935,27 @@
2935 zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
2936 }
2937 }else{
2938 zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
2939 }
2940
2941 if( zFormat ){
2942 if( nChild==0 ){
2943 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
2944 }
2945 char *zEntry;
2946 int nEntryLine = 0;
2947 zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate, zUserShort,
2948 zComShort, zBranch, zTags, zPrefix);
2949 nLine += nEntryLine;
2950 fossil_print("%s\n", zEntry);
2951 fossil_free(zEntry);
2952 }
2953 else{
2954 /* record another X lines */
2955 nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
2956 }
2957 fossil_free(zFree);
2958
2959 if(verboseFlag){
2960 if( !fchngQueryInit ){
2961 db_prepare(&fchngQuery,
@@ -2865,10 +2984,13 @@
2984 }
2985 nLine++; /* record another line */
2986 }
2987 db_reset(&fchngQuery);
2988 }
2989 /* Except for "oneline", separate formatted entries by one empty line */
2990 if( zFormat && fossil_strcmp(zFormat, "%h %c")!=0 )
2991 fossil_print("\n");
2992 nEntry++; /* record another complete entry */
2993 }
2994 if( rc==SQLITE_DONE ){
2995 /* Did the underlying query actually have all entries? */
2996 if( nAbsLimit==0 ){
@@ -2902,10 +3024,17 @@
3024 @ AS primPlinkCount,
3025 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
3026 @ event.mtime AS mtime,
3027 @ tagxref.value AS branch,
3028 @ event.type
3029 @ , coalesce(ecomment,comment) AS comment0
3030 @ , coalesce(euser,user,'?') AS user0
3031 @ , (SELECT case when length(x)>0 then x else '' end
3032 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
3033 @ FROM tag, tagxref
3034 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
3035 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags
3036 @ FROM tag CROSS JOIN event CROSS JOIN blob
3037 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
3038 @ AND tagxref.tagtype>0
3039 @ AND tagxref.rid=blob.rid
3040 @ WHERE blob.rid=event.objid
@@ -2931,10 +3060,11 @@
3060 */
3061 static int fossil_is_julianday(const char *zDate){
3062 return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd"
3063 " WHERE jd IS NOT NULL)", zDate);
3064 }
3065
3066
3067 /*
3068 ** COMMAND: timeline
3069 **
3070 ** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS?
@@ -2977,10 +3107,27 @@
3107 ** etc.) after the check-in comment.
3108 ** -W|--width N Width of lines (default is to auto-detect). N must be
3109 ** either greater than 20 or it ust be zero 0 to
3110 ** indicate no limit, resulting in a single line per
3111 ** entry.
3112 ** -F|--format Entry format. Values "oneline", "medium", and "full"
3113 ** get mapped to the full options below. Otherwise a
3114 ** string which can contain these placeholders:
3115 ** %n newline
3116 ** %% a raw %
3117 ** %H commit hash
3118 ** %h abbreviated commit hash
3119 ** %a author name
3120 ** %d date
3121 ** %c comment (NL, TAB replaced by space, LF deleted)
3122 ** %b branch
3123 ** %t tags
3124 ** %p zero or more of: *CURRENT*, *MERGE*, *FORK*,
3125 ** *UNPUBLISHED*, *LEAF*, *BRANCH*
3126 ** --oneline Show only short hash and comment for each entry
3127 ** --medium Medium-verbose entry formatting
3128 ** --full Extra verbose entry formatting
3129 ** -R REPO_FILE Specifies the repository db to use. Default is
3130 ** the current checkout's repository.
3131 */
3132 void timeline_cmd(void){
3133 Stmt q;
@@ -2996,10 +3143,11 @@
3143 Blob uuid;
3144 int mode = TIMELINE_MODE_NONE;
3145 int verboseFlag = 0 ;
3146 int iOffset;
3147 const char *zFilePattern = 0;
3148 const char *zFormat = 0;
3149 Blob treeName;
3150 int showSql = 0;
3151
3152 verboseFlag = find_option("verbose","v", 0)!=0;
3153 if( !verboseFlag){
@@ -3008,10 +3156,17 @@
3156 db_find_and_open_repository(0, 0);
3157 zLimit = find_option("limit","n",1);
3158 zWidth = find_option("width","W",1);
3159 zType = find_option("type","t",1);
3160 zFilePattern = find_option("path","p",1);
3161 zFormat = find_option("format","F",1);
3162 if( find_option("oneline",0,0)!= 0 || fossil_strcmp(zFormat,"oneline")==0 )
3163 zFormat = "%h %c";
3164 if( find_option("medium",0,0)!= 0 || fossil_strcmp(zFormat,"medium")==0 )
3165 zFormat = "Commit: %h%nDate: %d%nAuthor: %a%nComment: %c";
3166 if( find_option("full",0,0)!= 0 || fossil_strcmp(zFormat,"full")==0 )
3167 zFormat = "Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%nBranch: %b%nTags: %t%nPrefix: %p";
3168 showSql = find_option("sql",0,0)!=0;
3169
3170 if( !zLimit ){
3171 zLimit = find_option("count",0,1);
3172 }
@@ -3157,11 +3312,11 @@
3312 if( showSql ){
3313 fossil_print("%s\n", blob_str(&sql));
3314 }
3315 db_prepare_blob(&q, &sql);
3316 blob_reset(&sql);
3317 print_timeline(&q, n, width, zFormat, verboseFlag);
3318 db_finalize(&q);
3319 }
3320
3321 /*
3322 ** WEBPAGE: thisdayinhistory
3323
+1 -1
--- src/update.c
+++ src/update.c
@@ -221,11 +221,11 @@
221221
"%s "
222222
" AND event.objid IN leaves"
223223
" ORDER BY event.mtime DESC",
224224
timeline_query_for_tty()
225225
);
226
- print_timeline(&q, -100, width, 0);
226
+ print_timeline(&q, -100, width, 0, 0);
227227
db_finalize(&q);
228228
fossil_fatal("Multiple descendants");
229229
}
230230
}
231231
tid = db_int(0, "SELECT rid FROM leaves, event"
232232
--- src/update.c
+++ src/update.c
@@ -221,11 +221,11 @@
221 "%s "
222 " AND event.objid IN leaves"
223 " ORDER BY event.mtime DESC",
224 timeline_query_for_tty()
225 );
226 print_timeline(&q, -100, width, 0);
227 db_finalize(&q);
228 fossil_fatal("Multiple descendants");
229 }
230 }
231 tid = db_int(0, "SELECT rid FROM leaves, event"
232
--- src/update.c
+++ src/update.c
@@ -221,11 +221,11 @@
221 "%s "
222 " AND event.objid IN leaves"
223 " ORDER BY event.mtime DESC",
224 timeline_query_for_tty()
225 );
226 print_timeline(&q, -100, width, 0, 0);
227 db_finalize(&q);
228 fossil_fatal("Multiple descendants");
229 }
230 }
231 tid = db_int(0, "SELECT rid FROM leaves, event"
232

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button