Fossil SCM

Enhance the "fossil timeline" command so that one can specify a file or directory as a command-line argument and the timeline only shows check-ins that involve that particular file or any of the files in the named directory.

drh 2014-10-18 19:18 trunk merge
Commit e05a74f1f471272261a0ffde7f463f5aa39e0878
1 file changed +74 -39
+74 -39
--- src/timeline.c
+++ src/timeline.c
@@ -1655,11 +1655,10 @@
16551655
/*
16561656
** Return a pointer to a static string that forms the basis for
16571657
** a timeline query for display on a TTY.
16581658
*/
16591659
const char *timeline_query_for_tty(void){
1660
- static const char *zBase = 0;
16611660
static const char zBaseSql[] =
16621661
@ SELECT
16631662
@ blob.rid AS rid,
16641663
@ uuid,
16651664
@ datetime(event.mtime%s) AS mDateTime,
@@ -1675,20 +1674,17 @@
16751674
@ AS primPlinkCount,
16761675
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
16771676
@ event.mtime AS mtime,
16781677
@ tagxref.value AS branch
16791678
@ FROM tag CROSS JOIN event CROSS JOIN blob
1680
- @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
1679
+ @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
16811680
@ AND tagxref.tagtype>0
16821681
@ AND tagxref.rid=blob.rid
16831682
@ WHERE blob.rid=event.objid
16841683
@ AND tag.tagname='branch'
16851684
;
1686
- if( zBase==0 ){
1687
- zBase = mprintf(zBaseSql, timeline_utc());
1688
- }
1689
- return zBase;
1685
+ return mprintf(zBaseSql, timeline_utc());
16901686
}
16911687
16921688
/*
16931689
** Return true if the input string is a date in the ISO 8601 format:
16941690
** YYYY-MM-DD.
@@ -1739,11 +1735,11 @@
17391735
** -R REPO_FILE Specifies the repository db to use. Default is
17401736
** the current checkout's repository.
17411737
*/
17421738
void timeline_cmd(void){
17431739
Stmt q;
1744
- int n, k, width;
1740
+ int n, k, width, i;
17451741
const char *zLimit;
17461742
const char *zWidth;
17471743
const char *zOffset;
17481744
const char *zType;
17491745
char *zOrigin;
@@ -1752,10 +1748,12 @@
17521748
int objid = 0;
17531749
Blob uuid;
17541750
int mode = 0 ; /* 0:none 1: before 2:after 3:children 4:parents */
17551751
int verboseFlag = 0 ;
17561752
int iOffset;
1753
+ const char *zFilePattern = 0;
1754
+ Blob treeName;
17571755
17581756
verboseFlag = find_option("verbose","v", 0)!=0;
17591757
if( !verboseFlag){
17601758
verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
17611759
}
@@ -1783,37 +1781,39 @@
17831781
iOffset = zOffset ? atoi(zOffset) : 0;
17841782
17851783
/* We should be done with options.. */
17861784
verify_all_options();
17871785
1788
- if( g.argc>=4 ){
1789
- k = strlen(g.argv[2]);
1790
- if( strncmp(g.argv[2],"before",k)==0 ){
1791
- mode = 1;
1792
- }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
1793
- mode = 2;
1794
- }else if( strncmp(g.argv[2],"descendants",k)==0 ){
1795
- mode = 3;
1796
- }else if( strncmp(g.argv[2],"children",k)==0 ){
1797
- mode = 3;
1798
- }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
1799
- mode = 4;
1800
- }else if( strncmp(g.argv[2],"parents",k)==0 ){
1801
- mode = 4;
1802
- }else if(!zType && !zLimit){
1803
- usage("?WHEN? ?BASELINE|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
1804
- "?-W|--width WIDTH?");
1805
- }
1806
- if( '-' != *g.argv[3] ){
1807
- zOrigin = g.argv[3];
1808
- }else{
1809
- zOrigin = "now";
1810
- }
1811
- }else if( g.argc==3 ){
1812
- zOrigin = g.argv[2];
1813
- }else{
1814
- zOrigin = "now";
1786
+ zOrigin = "now";
1787
+ zFilePattern = 0;
1788
+ for(i=2; i<g.argc; i++){
1789
+ char *zArg = g.argv[i];
1790
+ k = strlen(zArg);
1791
+ if( mode==0 ){
1792
+ if( strncmp(zArg,"before",k)==0 ){
1793
+ mode = 1;
1794
+ }else if( strncmp(zArg,"after",k)==0 && k>1 ){
1795
+ mode = 2;
1796
+ }else if( strncmp(zArg,"descendants",k)==0 ){
1797
+ mode = 3;
1798
+ }else if( strncmp(zArg,"children",k)==0 ){
1799
+ mode = 3;
1800
+ }else if( strncmp(zArg,"ancestors",k)==0 && k>1 ){
1801
+ mode = 4;
1802
+ }else if( strncmp(zArg,"parents",k)==0 ){
1803
+ mode = 4;
1804
+ }
1805
+ if( mode ){
1806
+ if( i<g.argc-1 ) zOrigin = g.argv[++i];
1807
+ continue;
1808
+ }
1809
+ }
1810
+ if( zFilePattern==0 ){
1811
+ zFilePattern = zArg;
1812
+ }else{
1813
+ usage("?WHEN? ?CHECKIN|DATETIME? ?FILE? ?OPTIONS?");
1814
+ }
18151815
}
18161816
k = strlen(zOrigin);
18171817
blob_zero(&uuid);
18181818
blob_append(&uuid, zOrigin, -1);
18191819
if( fossil_strcmp(zOrigin, "now")==0 ){
@@ -1838,10 +1838,25 @@
18381838
if( mode==0 ){
18391839
if( isIsoDate(zOrigin) ) zShift = ",'+1 day'";
18401840
}
18411841
zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift);
18421842
}
1843
+
1844
+ if( zFilePattern ){
1845
+ if( zType==0 ){
1846
+ /* When zFilePattern is specified and type is not specified, only show
1847
+ * file checkins */
1848
+ zType="ci";
1849
+ }
1850
+ file_tree_name(zFilePattern, &treeName, 1);
1851
+ if( fossil_strcmp(blob_str(&treeName), ".")==0 ){
1852
+ /* When zTreeName refers to g.zLocalRoot, it's like not specifying
1853
+ * zFilePattern. */
1854
+ zFilePattern = 0;
1855
+ }
1856
+ }
1857
+
18431858
if( mode==0 ) mode = 1;
18441859
blob_zero(&sql);
18451860
blob_append(&sql, timeline_query_for_tty(), -1);
18461861
blob_appendf(&sql, " AND event.mtime %s %s",
18471862
(mode==1 || mode==4) ? "<=" : ">=",
@@ -1853,22 +1868,42 @@
18531868
if( mode==3 ){
18541869
compute_descendants(objid, n);
18551870
}else{
18561871
compute_ancestors(objid, n, 0);
18571872
}
1858
- blob_appendf(&sql, " AND blob.rid IN ok");
1873
+ blob_appendf(&sql, "\n AND blob.rid IN ok");
18591874
}
18601875
if( zType && (zType[0]!='a') ){
1861
- blob_appendf(&sql, " AND event.type=%Q ", zType);
1876
+ blob_appendf(&sql, "\n AND event.type=%Q ", zType);
1877
+ }
1878
+ if( zFilePattern ){
1879
+ blob_append(&sql,
1880
+ "\n AND EXISTS(SELECT 1 FROM mlink\n"
1881
+ " WHERE mlink.mid=event.objid\n"
1882
+ " AND mlink.fnid IN ", -1);
1883
+ if( filenames_are_case_sensitive() ){
1884
+ blob_appendf(&sql,
1885
+ "(SELECT fnid FROM filename"
1886
+ " WHERE name=%Q"
1887
+ " OR name GLOB '%q/*')",
1888
+ blob_str(&treeName), blob_str(&treeName));
1889
+ }else{
1890
+ blob_appendf(&sql,
1891
+ "(SELECT fnid FROM filename"
1892
+ " WHERE name=%Q COLLATE nocase"
1893
+ " OR lower(name) GLOB lower('%q/*'))",
1894
+ blob_str(&treeName), blob_str(&treeName));
1895
+ }
1896
+ blob_append(&sql, ")", -1);
18621897
}
1863
- blob_appendf(&sql, " ORDER BY event.mtime DESC");
1898
+ blob_appendf(&sql, "\nORDER BY event.mtime DESC");
18641899
if( iOffset>0 ){
18651900
/* Don't handle LIMIT here, otherwise print_timeline()
18661901
* will not determine the end-marker correctly! */
1867
- blob_appendf(&sql, " LIMIT -1 OFFSET %d", iOffset);
1902
+ blob_appendf(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
18681903
}
1869
- db_prepare(&q, blob_str(&sql));
1904
+ db_prepare(&q, "%s", blob_str(&sql));
18701905
blob_reset(&sql);
18711906
print_timeline(&q, n, width, verboseFlag);
18721907
db_finalize(&q);
18731908
}
18741909
18751910
--- src/timeline.c
+++ src/timeline.c
@@ -1655,11 +1655,10 @@
1655 /*
1656 ** Return a pointer to a static string that forms the basis for
1657 ** a timeline query for display on a TTY.
1658 */
1659 const char *timeline_query_for_tty(void){
1660 static const char *zBase = 0;
1661 static const char zBaseSql[] =
1662 @ SELECT
1663 @ blob.rid AS rid,
1664 @ uuid,
1665 @ datetime(event.mtime%s) AS mDateTime,
@@ -1675,20 +1674,17 @@
1675 @ AS primPlinkCount,
1676 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1677 @ event.mtime AS mtime,
1678 @ tagxref.value AS branch
1679 @ FROM tag CROSS JOIN event CROSS JOIN blob
1680 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
1681 @ AND tagxref.tagtype>0
1682 @ AND tagxref.rid=blob.rid
1683 @ WHERE blob.rid=event.objid
1684 @ AND tag.tagname='branch'
1685 ;
1686 if( zBase==0 ){
1687 zBase = mprintf(zBaseSql, timeline_utc());
1688 }
1689 return zBase;
1690 }
1691
1692 /*
1693 ** Return true if the input string is a date in the ISO 8601 format:
1694 ** YYYY-MM-DD.
@@ -1739,11 +1735,11 @@
1739 ** -R REPO_FILE Specifies the repository db to use. Default is
1740 ** the current checkout's repository.
1741 */
1742 void timeline_cmd(void){
1743 Stmt q;
1744 int n, k, width;
1745 const char *zLimit;
1746 const char *zWidth;
1747 const char *zOffset;
1748 const char *zType;
1749 char *zOrigin;
@@ -1752,10 +1748,12 @@
1752 int objid = 0;
1753 Blob uuid;
1754 int mode = 0 ; /* 0:none 1: before 2:after 3:children 4:parents */
1755 int verboseFlag = 0 ;
1756 int iOffset;
 
 
1757
1758 verboseFlag = find_option("verbose","v", 0)!=0;
1759 if( !verboseFlag){
1760 verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
1761 }
@@ -1783,37 +1781,39 @@
1783 iOffset = zOffset ? atoi(zOffset) : 0;
1784
1785 /* We should be done with options.. */
1786 verify_all_options();
1787
1788 if( g.argc>=4 ){
1789 k = strlen(g.argv[2]);
1790 if( strncmp(g.argv[2],"before",k)==0 ){
1791 mode = 1;
1792 }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
1793 mode = 2;
1794 }else if( strncmp(g.argv[2],"descendants",k)==0 ){
1795 mode = 3;
1796 }else if( strncmp(g.argv[2],"children",k)==0 ){
1797 mode = 3;
1798 }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
1799 mode = 4;
1800 }else if( strncmp(g.argv[2],"parents",k)==0 ){
1801 mode = 4;
1802 }else if(!zType && !zLimit){
1803 usage("?WHEN? ?BASELINE|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
1804 "?-W|--width WIDTH?");
1805 }
1806 if( '-' != *g.argv[3] ){
1807 zOrigin = g.argv[3];
1808 }else{
1809 zOrigin = "now";
1810 }
1811 }else if( g.argc==3 ){
1812 zOrigin = g.argv[2];
1813 }else{
1814 zOrigin = "now";
 
 
1815 }
1816 k = strlen(zOrigin);
1817 blob_zero(&uuid);
1818 blob_append(&uuid, zOrigin, -1);
1819 if( fossil_strcmp(zOrigin, "now")==0 ){
@@ -1838,10 +1838,25 @@
1838 if( mode==0 ){
1839 if( isIsoDate(zOrigin) ) zShift = ",'+1 day'";
1840 }
1841 zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift);
1842 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1843 if( mode==0 ) mode = 1;
1844 blob_zero(&sql);
1845 blob_append(&sql, timeline_query_for_tty(), -1);
1846 blob_appendf(&sql, " AND event.mtime %s %s",
1847 (mode==1 || mode==4) ? "<=" : ">=",
@@ -1853,22 +1868,42 @@
1853 if( mode==3 ){
1854 compute_descendants(objid, n);
1855 }else{
1856 compute_ancestors(objid, n, 0);
1857 }
1858 blob_appendf(&sql, " AND blob.rid IN ok");
1859 }
1860 if( zType && (zType[0]!='a') ){
1861 blob_appendf(&sql, " AND event.type=%Q ", zType);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1862 }
1863 blob_appendf(&sql, " ORDER BY event.mtime DESC");
1864 if( iOffset>0 ){
1865 /* Don't handle LIMIT here, otherwise print_timeline()
1866 * will not determine the end-marker correctly! */
1867 blob_appendf(&sql, " LIMIT -1 OFFSET %d", iOffset);
1868 }
1869 db_prepare(&q, blob_str(&sql));
1870 blob_reset(&sql);
1871 print_timeline(&q, n, width, verboseFlag);
1872 db_finalize(&q);
1873 }
1874
1875
--- src/timeline.c
+++ src/timeline.c
@@ -1655,11 +1655,10 @@
1655 /*
1656 ** Return a pointer to a static string that forms the basis for
1657 ** a timeline query for display on a TTY.
1658 */
1659 const char *timeline_query_for_tty(void){
 
1660 static const char zBaseSql[] =
1661 @ SELECT
1662 @ blob.rid AS rid,
1663 @ uuid,
1664 @ datetime(event.mtime%s) AS mDateTime,
@@ -1675,20 +1674,17 @@
1674 @ AS primPlinkCount,
1675 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1676 @ event.mtime AS mtime,
1677 @ tagxref.value AS branch
1678 @ FROM tag CROSS JOIN event CROSS JOIN blob
1679 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
1680 @ AND tagxref.tagtype>0
1681 @ AND tagxref.rid=blob.rid
1682 @ WHERE blob.rid=event.objid
1683 @ AND tag.tagname='branch'
1684 ;
1685 return mprintf(zBaseSql, timeline_utc());
 
 
 
1686 }
1687
1688 /*
1689 ** Return true if the input string is a date in the ISO 8601 format:
1690 ** YYYY-MM-DD.
@@ -1739,11 +1735,11 @@
1735 ** -R REPO_FILE Specifies the repository db to use. Default is
1736 ** the current checkout's repository.
1737 */
1738 void timeline_cmd(void){
1739 Stmt q;
1740 int n, k, width, i;
1741 const char *zLimit;
1742 const char *zWidth;
1743 const char *zOffset;
1744 const char *zType;
1745 char *zOrigin;
@@ -1752,10 +1748,12 @@
1748 int objid = 0;
1749 Blob uuid;
1750 int mode = 0 ; /* 0:none 1: before 2:after 3:children 4:parents */
1751 int verboseFlag = 0 ;
1752 int iOffset;
1753 const char *zFilePattern = 0;
1754 Blob treeName;
1755
1756 verboseFlag = find_option("verbose","v", 0)!=0;
1757 if( !verboseFlag){
1758 verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
1759 }
@@ -1783,37 +1781,39 @@
1781 iOffset = zOffset ? atoi(zOffset) : 0;
1782
1783 /* We should be done with options.. */
1784 verify_all_options();
1785
1786 zOrigin = "now";
1787 zFilePattern = 0;
1788 for(i=2; i<g.argc; i++){
1789 char *zArg = g.argv[i];
1790 k = strlen(zArg);
1791 if( mode==0 ){
1792 if( strncmp(zArg,"before",k)==0 ){
1793 mode = 1;
1794 }else if( strncmp(zArg,"after",k)==0 && k>1 ){
1795 mode = 2;
1796 }else if( strncmp(zArg,"descendants",k)==0 ){
1797 mode = 3;
1798 }else if( strncmp(zArg,"children",k)==0 ){
1799 mode = 3;
1800 }else if( strncmp(zArg,"ancestors",k)==0 && k>1 ){
1801 mode = 4;
1802 }else if( strncmp(zArg,"parents",k)==0 ){
1803 mode = 4;
1804 }
1805 if( mode ){
1806 if( i<g.argc-1 ) zOrigin = g.argv[++i];
1807 continue;
1808 }
1809 }
1810 if( zFilePattern==0 ){
1811 zFilePattern = zArg;
1812 }else{
1813 usage("?WHEN? ?CHECKIN|DATETIME? ?FILE? ?OPTIONS?");
1814 }
1815 }
1816 k = strlen(zOrigin);
1817 blob_zero(&uuid);
1818 blob_append(&uuid, zOrigin, -1);
1819 if( fossil_strcmp(zOrigin, "now")==0 ){
@@ -1838,10 +1838,25 @@
1838 if( mode==0 ){
1839 if( isIsoDate(zOrigin) ) zShift = ",'+1 day'";
1840 }
1841 zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift);
1842 }
1843
1844 if( zFilePattern ){
1845 if( zType==0 ){
1846 /* When zFilePattern is specified and type is not specified, only show
1847 * file checkins */
1848 zType="ci";
1849 }
1850 file_tree_name(zFilePattern, &treeName, 1);
1851 if( fossil_strcmp(blob_str(&treeName), ".")==0 ){
1852 /* When zTreeName refers to g.zLocalRoot, it's like not specifying
1853 * zFilePattern. */
1854 zFilePattern = 0;
1855 }
1856 }
1857
1858 if( mode==0 ) mode = 1;
1859 blob_zero(&sql);
1860 blob_append(&sql, timeline_query_for_tty(), -1);
1861 blob_appendf(&sql, " AND event.mtime %s %s",
1862 (mode==1 || mode==4) ? "<=" : ">=",
@@ -1853,22 +1868,42 @@
1868 if( mode==3 ){
1869 compute_descendants(objid, n);
1870 }else{
1871 compute_ancestors(objid, n, 0);
1872 }
1873 blob_appendf(&sql, "\n AND blob.rid IN ok");
1874 }
1875 if( zType && (zType[0]!='a') ){
1876 blob_appendf(&sql, "\n AND event.type=%Q ", zType);
1877 }
1878 if( zFilePattern ){
1879 blob_append(&sql,
1880 "\n AND EXISTS(SELECT 1 FROM mlink\n"
1881 " WHERE mlink.mid=event.objid\n"
1882 " AND mlink.fnid IN ", -1);
1883 if( filenames_are_case_sensitive() ){
1884 blob_appendf(&sql,
1885 "(SELECT fnid FROM filename"
1886 " WHERE name=%Q"
1887 " OR name GLOB '%q/*')",
1888 blob_str(&treeName), blob_str(&treeName));
1889 }else{
1890 blob_appendf(&sql,
1891 "(SELECT fnid FROM filename"
1892 " WHERE name=%Q COLLATE nocase"
1893 " OR lower(name) GLOB lower('%q/*'))",
1894 blob_str(&treeName), blob_str(&treeName));
1895 }
1896 blob_append(&sql, ")", -1);
1897 }
1898 blob_appendf(&sql, "\nORDER BY event.mtime DESC");
1899 if( iOffset>0 ){
1900 /* Don't handle LIMIT here, otherwise print_timeline()
1901 * will not determine the end-marker correctly! */
1902 blob_appendf(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
1903 }
1904 db_prepare(&q, "%s", blob_str(&sql));
1905 blob_reset(&sql);
1906 print_timeline(&q, n, width, verboseFlag);
1907 db_finalize(&q);
1908 }
1909
1910

Keyboard Shortcuts

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