Fossil SCM

Completely rework the "fossil grep" command. Omit the -H option. Instead, print a header line that includes both the file hash and the check-in hash and the timestamp for every file that contains any match. Scan all files together, in reverse chronological order.

drh 2019-11-30 13:38 grep-enhancements
Commit 9c2080a360fc4a4e7496eadf0d49722bf7861bf515157bf632bab46c41ec58cb
1 file changed +67 -49
+67 -49
--- src/regexp.c
+++ src/regexp.c
@@ -738,15 +738,18 @@
738738
n = j - i;
739739
ln++;
740740
if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
741741
cnt++;
742742
if( flags & GREP_EXISTS ){
743
- if( (flags & GREP_QUIET)==0 ) fossil_print("%S\n", zName);
743
+ if( (flags & GREP_QUIET)==0 && zName ) fossil_print("%s\n", zName);
744744
break;
745745
}
746746
if( (flags & GREP_QUIET)==0 ){
747
- fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
747
+ if( cnt==1 && zName ){
748
+ fossil_print("== %s\n", zName);
749
+ }
750
+ fossil_print("%d:%.*s\n", ln, n, z+i);
748751
}
749752
}
750753
}
751754
return cnt;
752755
}
@@ -793,19 +796,22 @@
793796
** COMMAND: grep
794797
**
795798
** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
796799
**
797800
** Attempt to match the given POSIX extended regular expression PATTERN
798
-** over all historic versions of FILENAME. For details of the supported
799
-** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
801
+** historic versions of FILENAME. The search begins with the most recent
802
+** version of the file and moves backwards in time. Multiple FILENAMEs can
803
+** be specified, in which case all named files are searched in reverse
804
+** chronological order.
805
+**
806
+** For details of the supported regular expression dialect, see
807
+** https://fossil-scm.org/fossil/doc/trunk/www/grep.md
800808
**
801809
** Options:
802810
**
803811
** -c|--count Suppress normal output; instead print a count
804812
** of the number of matching files
805
-** -H|--checkin-hash Show the check-in hash rather than
806
-** file artifact hash for each match
807813
** -i|--ignore-case Ignore case
808814
** -l|--files-with-matches List only hash for each match
809815
** --once Stop searching after the first match
810816
** -s|--no-messages Suppress error messages about nonexistant
811817
** or unreadable files
@@ -818,23 +824,23 @@
818824
int bVerbose = 0;
819825
ReCompiled *pRe;
820826
const char *zErr;
821827
int ignoreCase = 0;
822828
Blob fullName;
823
- int ckinHash = 0;
824829
int ii;
825830
int nMatch = 0;
826831
int bNoMsg;
827832
int cntFlag;
828833
int bOnce;
829834
int bInvert;
830835
int nSearch = 0;
836
+ Stmt q;
837
+
831838
832839
if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
833840
if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
834841
if( find_option("verbose",0,0)!=0 ) bVerbose = 1;
835
- ckinHash = find_option("checkin-hash","H",0)!=0;
836842
if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS;
837843
bNoMsg = find_option("no-messages","s",0)!=0;
838844
bOnce = find_option("once",0,0)!=0;
839845
bInvert = find_option("invert-match","v",0)!=0;
840846
if( bInvert ){
@@ -851,16 +857,13 @@
851857
}
852858
zErr = re_compile(&pRe, g.argv[2], ignoreCase);
853859
if( zErr ) fossil_fatal("%s", zErr);
854860
855861
add_content_sql_commands(g.db);
862
+ db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
856863
for(ii=3; ii<g.argc; ii++){
857864
const char *zTarget = g.argv[ii];
858
- if( nMatch ){
859
- if( (flags & GREP_QUIET)!=0 ) break;
860
- if( bOnce ) break;
861
- }
862865
if( file_tree_name(zTarget, &fullName, 0, 1) ){
863866
int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
864867
blob_str(&fullName));
865868
if( !fnid ){
866869
if( bNoMsg ) continue;
@@ -867,50 +870,65 @@
867870
if( file_size(zTarget, ExtFILE)<0 ){
868871
fossil_fatal("no such file: %s", zTarget);
869872
}
870873
fossil_fatal("not a managed file: %s", zTarget);
871874
}else{
872
- Stmt q;
873
- db_prepare(&q,
874
- "SELECT content(ux), %w FROM ("
875
- " SELECT A.uuid AS ux, B.uuid AS ckin, min(event.mtime) AS mx"
876
- " FROM mlink, blob A, blob B, event"
877
- " WHERE mlink.mid=event.objid"
878
- " AND mlink.fid=A.rid"
879
- " AND mlink.mid=B.rid"
880
- " AND mlink.fnid=%d"
881
- " GROUP BY A.uuid"
882
- ") ORDER BY mx DESC;",
883
- ckinHash ? "ckin" : "ux",
884
- fnid
885
- );
886
- while( db_step(&q)==SQLITE_ROW ){
887
- const char *zHash = db_column_text(&q,1);
888
- const char *zContent = db_column_text(&q,0);
889
- if( bVerbose ) fossil_print("%S:\n", zHash);
890
- nSearch++;
891
- nMatch += grep_buffer(pRe, zHash, zContent, flags);
892
- if( bInvert && cntFlag==0 ){
893
- if( nMatch==0 ){
894
- fossil_print("%S\n", zHash);
895
- if( bOnce ) nMatch = 1;
896
- }else{
897
- nMatch = 0;
898
- }
899
- }
900
- if( nMatch ){
901
- if( (flags & GREP_QUIET)!=0 ) break;
902
- if( bOnce ) break;
903
- }
904
- }
905
- db_finalize(&q);
906
- }
907
- }
908
- }
875
+ db_multi_exec(
876
+ "INSERT INTO arglist(iname,fname,fnid) VALUES(%Q,%Q,%d)",
877
+ zTarget, blob_str(&fullName), fnid);
878
+ }
879
+ }
880
+ blob_reset(&fullName);
881
+ }
882
+ db_prepare(&q,
883
+ " SELECT"
884
+ " A.uuid," /* file hash */
885
+ " A.rid," /* file rid */
886
+ " B.uuid," /* check-in hash */
887
+ " datetime(min(event.mtime))," /* check-in time */
888
+ " arglist.iname" /* file name */
889
+ " FROM arglist, mlink, blob A, blob B, event"
890
+ " WHERE mlink.mid=event.objid"
891
+ " AND mlink.fid=A.rid"
892
+ " AND mlink.mid=B.rid"
893
+ " AND mlink.fnid=arglist.fnid"
894
+ " GROUP BY A.uuid"
895
+ " ORDER BY min(event.mtime) DESC;"
896
+ );
897
+ while( db_step(&q)==SQLITE_ROW ){
898
+ const char *zFileHash = db_column_text(&q,0);
899
+ int rid = db_column_int(&q,1);
900
+ const char *zCkinHash = db_column_text(&q,2);
901
+ const char *zDate = db_column_text(&q,3);
902
+ const char *zFN = db_column_text(&q,4);
903
+ char *zLabel;
904
+ Blob cx;
905
+ content_get(rid, &cx);
906
+ zLabel = mprintf("%.16s %s %S checkin %S", zDate, zFN,zFileHash,zCkinHash);
907
+ if( bVerbose ) fossil_print("Scanning: %s\n", zLabel);
908
+ nSearch++;
909
+ nMatch += grep_buffer(pRe, zLabel, blob_str(&cx), flags);
910
+ blob_reset(&cx);
911
+ if( bInvert && cntFlag==0 ){
912
+ if( nMatch==0 ){
913
+ fossil_print("== %s\n", zLabel);
914
+ if( bOnce ) nMatch = 1;
915
+ }else{
916
+ nMatch = 0;
917
+ }
918
+ }
919
+ fossil_free(zLabel);
920
+ if( nMatch ){
921
+ if( (flags & GREP_QUIET)!=0 ) break;
922
+ if( bOnce ) break;
923
+ }
924
+ }
925
+ db_finalize(&q);
926
+ re_free(pRe);
909927
if( cntFlag ){
910928
if( bInvert ){
911929
fossil_print("%d\n", nSearch-nMatch);
912930
}else{
913931
fossil_print("%d\n", nMatch);
914932
}
915933
}
916934
}
917935
--- src/regexp.c
+++ src/regexp.c
@@ -738,15 +738,18 @@
738 n = j - i;
739 ln++;
740 if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
741 cnt++;
742 if( flags & GREP_EXISTS ){
743 if( (flags & GREP_QUIET)==0 ) fossil_print("%S\n", zName);
744 break;
745 }
746 if( (flags & GREP_QUIET)==0 ){
747 fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
 
 
 
748 }
749 }
750 }
751 return cnt;
752 }
@@ -793,19 +796,22 @@
793 ** COMMAND: grep
794 **
795 ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
796 **
797 ** Attempt to match the given POSIX extended regular expression PATTERN
798 ** over all historic versions of FILENAME. For details of the supported
799 ** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
 
 
 
 
 
800 **
801 ** Options:
802 **
803 ** -c|--count Suppress normal output; instead print a count
804 ** of the number of matching files
805 ** -H|--checkin-hash Show the check-in hash rather than
806 ** file artifact hash for each match
807 ** -i|--ignore-case Ignore case
808 ** -l|--files-with-matches List only hash for each match
809 ** --once Stop searching after the first match
810 ** -s|--no-messages Suppress error messages about nonexistant
811 ** or unreadable files
@@ -818,23 +824,23 @@
818 int bVerbose = 0;
819 ReCompiled *pRe;
820 const char *zErr;
821 int ignoreCase = 0;
822 Blob fullName;
823 int ckinHash = 0;
824 int ii;
825 int nMatch = 0;
826 int bNoMsg;
827 int cntFlag;
828 int bOnce;
829 int bInvert;
830 int nSearch = 0;
 
 
831
832 if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
833 if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
834 if( find_option("verbose",0,0)!=0 ) bVerbose = 1;
835 ckinHash = find_option("checkin-hash","H",0)!=0;
836 if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS;
837 bNoMsg = find_option("no-messages","s",0)!=0;
838 bOnce = find_option("once",0,0)!=0;
839 bInvert = find_option("invert-match","v",0)!=0;
840 if( bInvert ){
@@ -851,16 +857,13 @@
851 }
852 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
853 if( zErr ) fossil_fatal("%s", zErr);
854
855 add_content_sql_commands(g.db);
 
856 for(ii=3; ii<g.argc; ii++){
857 const char *zTarget = g.argv[ii];
858 if( nMatch ){
859 if( (flags & GREP_QUIET)!=0 ) break;
860 if( bOnce ) break;
861 }
862 if( file_tree_name(zTarget, &fullName, 0, 1) ){
863 int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
864 blob_str(&fullName));
865 if( !fnid ){
866 if( bNoMsg ) continue;
@@ -867,50 +870,65 @@
867 if( file_size(zTarget, ExtFILE)<0 ){
868 fossil_fatal("no such file: %s", zTarget);
869 }
870 fossil_fatal("not a managed file: %s", zTarget);
871 }else{
872 Stmt q;
873 db_prepare(&q,
874 "SELECT content(ux), %w FROM ("
875 " SELECT A.uuid AS ux, B.uuid AS ckin, min(event.mtime) AS mx"
876 " FROM mlink, blob A, blob B, event"
877 " WHERE mlink.mid=event.objid"
878 " AND mlink.fid=A.rid"
879 " AND mlink.mid=B.rid"
880 " AND mlink.fnid=%d"
881 " GROUP BY A.uuid"
882 ") ORDER BY mx DESC;",
883 ckinHash ? "ckin" : "ux",
884 fnid
885 );
886 while( db_step(&q)==SQLITE_ROW ){
887 const char *zHash = db_column_text(&q,1);
888 const char *zContent = db_column_text(&q,0);
889 if( bVerbose ) fossil_print("%S:\n", zHash);
890 nSearch++;
891 nMatch += grep_buffer(pRe, zHash, zContent, flags);
892 if( bInvert && cntFlag==0 ){
893 if( nMatch==0 ){
894 fossil_print("%S\n", zHash);
895 if( bOnce ) nMatch = 1;
896 }else{
897 nMatch = 0;
898 }
899 }
900 if( nMatch ){
901 if( (flags & GREP_QUIET)!=0 ) break;
902 if( bOnce ) break;
903 }
904 }
905 db_finalize(&q);
906 }
907 }
908 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909 if( cntFlag ){
910 if( bInvert ){
911 fossil_print("%d\n", nSearch-nMatch);
912 }else{
913 fossil_print("%d\n", nMatch);
914 }
915 }
916 }
917
--- src/regexp.c
+++ src/regexp.c
@@ -738,15 +738,18 @@
738 n = j - i;
739 ln++;
740 if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
741 cnt++;
742 if( flags & GREP_EXISTS ){
743 if( (flags & GREP_QUIET)==0 && zName ) fossil_print("%s\n", zName);
744 break;
745 }
746 if( (flags & GREP_QUIET)==0 ){
747 if( cnt==1 && zName ){
748 fossil_print("== %s\n", zName);
749 }
750 fossil_print("%d:%.*s\n", ln, n, z+i);
751 }
752 }
753 }
754 return cnt;
755 }
@@ -793,19 +796,22 @@
796 ** COMMAND: grep
797 **
798 ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
799 **
800 ** Attempt to match the given POSIX extended regular expression PATTERN
801 ** historic versions of FILENAME. The search begins with the most recent
802 ** version of the file and moves backwards in time. Multiple FILENAMEs can
803 ** be specified, in which case all named files are searched in reverse
804 ** chronological order.
805 **
806 ** For details of the supported regular expression dialect, see
807 ** https://fossil-scm.org/fossil/doc/trunk/www/grep.md
808 **
809 ** Options:
810 **
811 ** -c|--count Suppress normal output; instead print a count
812 ** of the number of matching files
 
 
813 ** -i|--ignore-case Ignore case
814 ** -l|--files-with-matches List only hash for each match
815 ** --once Stop searching after the first match
816 ** -s|--no-messages Suppress error messages about nonexistant
817 ** or unreadable files
@@ -818,23 +824,23 @@
824 int bVerbose = 0;
825 ReCompiled *pRe;
826 const char *zErr;
827 int ignoreCase = 0;
828 Blob fullName;
 
829 int ii;
830 int nMatch = 0;
831 int bNoMsg;
832 int cntFlag;
833 int bOnce;
834 int bInvert;
835 int nSearch = 0;
836 Stmt q;
837
838
839 if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
840 if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
841 if( find_option("verbose",0,0)!=0 ) bVerbose = 1;
 
842 if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS;
843 bNoMsg = find_option("no-messages","s",0)!=0;
844 bOnce = find_option("once",0,0)!=0;
845 bInvert = find_option("invert-match","v",0)!=0;
846 if( bInvert ){
@@ -851,16 +857,13 @@
857 }
858 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
859 if( zErr ) fossil_fatal("%s", zErr);
860
861 add_content_sql_commands(g.db);
862 db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
863 for(ii=3; ii<g.argc; ii++){
864 const char *zTarget = g.argv[ii];
 
 
 
 
865 if( file_tree_name(zTarget, &fullName, 0, 1) ){
866 int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
867 blob_str(&fullName));
868 if( !fnid ){
869 if( bNoMsg ) continue;
@@ -867,50 +870,65 @@
870 if( file_size(zTarget, ExtFILE)<0 ){
871 fossil_fatal("no such file: %s", zTarget);
872 }
873 fossil_fatal("not a managed file: %s", zTarget);
874 }else{
875 db_multi_exec(
876 "INSERT INTO arglist(iname,fname,fnid) VALUES(%Q,%Q,%d)",
877 zTarget, blob_str(&fullName), fnid);
878 }
879 }
880 blob_reset(&fullName);
881 }
882 db_prepare(&q,
883 " SELECT"
884 " A.uuid," /* file hash */
885 " A.rid," /* file rid */
886 " B.uuid," /* check-in hash */
887 " datetime(min(event.mtime))," /* check-in time */
888 " arglist.iname" /* file name */
889 " FROM arglist, mlink, blob A, blob B, event"
890 " WHERE mlink.mid=event.objid"
891 " AND mlink.fid=A.rid"
892 " AND mlink.mid=B.rid"
893 " AND mlink.fnid=arglist.fnid"
894 " GROUP BY A.uuid"
895 " ORDER BY min(event.mtime) DESC;"
896 );
897 while( db_step(&q)==SQLITE_ROW ){
898 const char *zFileHash = db_column_text(&q,0);
899 int rid = db_column_int(&q,1);
900 const char *zCkinHash = db_column_text(&q,2);
901 const char *zDate = db_column_text(&q,3);
902 const char *zFN = db_column_text(&q,4);
903 char *zLabel;
904 Blob cx;
905 content_get(rid, &cx);
906 zLabel = mprintf("%.16s %s %S checkin %S", zDate, zFN,zFileHash,zCkinHash);
907 if( bVerbose ) fossil_print("Scanning: %s\n", zLabel);
908 nSearch++;
909 nMatch += grep_buffer(pRe, zLabel, blob_str(&cx), flags);
910 blob_reset(&cx);
911 if( bInvert && cntFlag==0 ){
912 if( nMatch==0 ){
913 fossil_print("== %s\n", zLabel);
914 if( bOnce ) nMatch = 1;
915 }else{
916 nMatch = 0;
917 }
918 }
919 fossil_free(zLabel);
920 if( nMatch ){
921 if( (flags & GREP_QUIET)!=0 ) break;
922 if( bOnce ) break;
923 }
924 }
925 db_finalize(&q);
926 re_free(pRe);
927 if( cntFlag ){
928 if( bInvert ){
929 fossil_print("%d\n", nSearch-nMatch);
930 }else{
931 fossil_print("%d\n", nMatch);
932 }
933 }
934 }
935

Keyboard Shortcuts

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