Fossil SCM
Add the "fossil test-missing" command. Make test-missing and test-orphans available to "fossil all".
Commit
eb949991434d3e1a33abd8d15df867c548454ae3
Parent
aab9e66b8b2901e…
2 files changed
+5
+138
+5
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -148,10 +148,15 @@ | ||
| 148 | 148 | collect_argument(&extra, "stat"); |
| 149 | 149 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 150 | 150 | zCmd = "sync -autourl -R"; |
| 151 | 151 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 152 | 152 | zCmd = "test-integrity"; |
| 153 | + }else if( strncmp(zCmd, "test-orphans", n)==0 ){ | |
| 154 | + zCmd = "test-orphans -R"; | |
| 155 | + }else if( strncmp(zCmd, "test-missing", n)==0 ){ | |
| 156 | + zCmd = "test-missing -q -R"; | |
| 157 | + collect_argument(&extra, "notshunned"); | |
| 153 | 158 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 154 | 159 | zCmd = "changes --quiet --header --chdir"; |
| 155 | 160 | useCheckouts = 1; |
| 156 | 161 | stopOnError = 0; |
| 157 | 162 | quiet = 1; |
| 158 | 163 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -148,10 +148,15 @@ | |
| 148 | collect_argument(&extra, "stat"); |
| 149 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 150 | zCmd = "sync -autourl -R"; |
| 151 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 152 | zCmd = "test-integrity"; |
| 153 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 154 | zCmd = "changes --quiet --header --chdir"; |
| 155 | useCheckouts = 1; |
| 156 | stopOnError = 0; |
| 157 | quiet = 1; |
| 158 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -148,10 +148,15 @@ | |
| 148 | collect_argument(&extra, "stat"); |
| 149 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 150 | zCmd = "sync -autourl -R"; |
| 151 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 152 | zCmd = "test-integrity"; |
| 153 | }else if( strncmp(zCmd, "test-orphans", n)==0 ){ |
| 154 | zCmd = "test-orphans -R"; |
| 155 | }else if( strncmp(zCmd, "test-missing", n)==0 ){ |
| 156 | zCmd = "test-missing -q -R"; |
| 157 | collect_argument(&extra, "notshunned"); |
| 158 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 159 | zCmd = "changes --quiet --header --chdir"; |
| 160 | useCheckouts = 1; |
| 161 | stopOnError = 0; |
| 162 | quiet = 1; |
| 163 |
+138
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -926,5 +926,143 @@ | ||
| 926 | 926 | cnt++; |
| 927 | 927 | } |
| 928 | 928 | db_finalize(&q); |
| 929 | 929 | fossil_print("%d orphans\n", cnt); |
| 930 | 930 | } |
| 931 | + | |
| 932 | +/* Allowed flags for check_exists */ | |
| 933 | +#define MISSING_SHUNNED 0x0001 /* Do not report shunned artifacts */ | |
| 934 | + | |
| 935 | +/* This is a helper routine for test-artifacts. | |
| 936 | +** | |
| 937 | +** Check to see that artifact zUuid exists in the repository. If it does, | |
| 938 | +** return 0. If it does not, generate an error message and return 1. | |
| 939 | +*/ | |
| 940 | +static int check_exists( | |
| 941 | + const char *zUuid, /* The artifact we are checking for */ | |
| 942 | + unsigned flags, /* Flags */ | |
| 943 | + Manifest *p, /* The control artifact that references zUuid */ | |
| 944 | + const char *zRole, /* Role of zUuid in p */ | |
| 945 | + const char *zDetail /* Additional information, such as a filename */ | |
| 946 | +){ | |
| 947 | + static Stmt q; | |
| 948 | + int rc = 0; | |
| 949 | + | |
| 950 | + db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); | |
| 951 | + if( zUuid==0 || zUuid[0]==0 ) return 0; | |
| 952 | + db_bind_text(&q, ":uuid", zUuid); | |
| 953 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 954 | + int size = db_column_int(&q, 0); | |
| 955 | + if( size<0 ) rc = 2; | |
| 956 | + }else{ | |
| 957 | + rc = 1; | |
| 958 | + } | |
| 959 | + db_reset(&q); | |
| 960 | + if( rc ){ | |
| 961 | + const char *zCFType = "control artifact"; | |
| 962 | + char *zSrc; | |
| 963 | + char *zDate; | |
| 964 | + char *zErrType = "MISSING"; | |
| 965 | + if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ | |
| 966 | + if( flags & MISSING_SHUNNED ) return 0; | |
| 967 | + zErrType = "SHUNNED"; | |
| 968 | + } | |
| 969 | + switch( p->type ){ | |
| 970 | + case CFTYPE_MANIFEST: zCFType = "check-in"; break; | |
| 971 | + case CFTYPE_CLUSTER: zCFType = "cluster"; break; | |
| 972 | + case CFTYPE_CONTROL: zCFType = "tag"; break; | |
| 973 | + case CFTYPE_WIKI: zCFType = "wiki"; break; | |
| 974 | + case CFTYPE_TICKET: zCFType = "ticket"; break; | |
| 975 | + case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; | |
| 976 | + case CFTYPE_EVENT: zCFType = "event"; break; | |
| 977 | + } | |
| 978 | + zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); | |
| 979 | + if( p->rDate>0.0 ){ | |
| 980 | + zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); | |
| 981 | + }else{ | |
| 982 | + zDate = db_text(0, | |
| 983 | + "SELECT datetime(rcvfrom.mtime)" | |
| 984 | + " FROM blob, rcvfrom" | |
| 985 | + " WHERE blob.rcvid=rcvfrom.rcvid" | |
| 986 | + " AND blob.rid=%d", p->rid); | |
| 987 | + } | |
| 988 | + fossil_print("%s: %s\n %s %s %S (%d) %s\n", | |
| 989 | + zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); | |
| 990 | + if( zDetail && zDetail[0] ){ | |
| 991 | + fossil_print(" %s\n", zDetail); | |
| 992 | + } | |
| 993 | + fossil_free(zSrc); | |
| 994 | + fossil_free(zDate); | |
| 995 | + rc = 1; | |
| 996 | + } | |
| 997 | + return rc; | |
| 998 | +} | |
| 999 | + | |
| 1000 | +/* | |
| 1001 | +** COMMAND: test-missing | |
| 1002 | +** | |
| 1003 | +** Usage: %fossil test-missing | |
| 1004 | +** | |
| 1005 | +** Look at every artifact in the repository and verify that | |
| 1006 | +** all references are satisfied. Report any referenced artifacts | |
| 1007 | +** that are missing or shunned. | |
| 1008 | +** | |
| 1009 | +** Options: | |
| 1010 | +** | |
| 1011 | +** --notshunned Do not report shunned artifacts | |
| 1012 | +** --quiet Only show output if there are errors | |
| 1013 | +*/ | |
| 1014 | +void test_missing(void){ | |
| 1015 | + Stmt q; | |
| 1016 | + Blob content; | |
| 1017 | + int nErr = 0; | |
| 1018 | + int nArtifact = 0; | |
| 1019 | + int i; | |
| 1020 | + Manifest *p; | |
| 1021 | + unsigned flags = 0; | |
| 1022 | + int quietFlag; | |
| 1023 | + | |
| 1024 | + if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; | |
| 1025 | + quietFlag = find_option("quiet","q",0)!=0; | |
| 1026 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1027 | + db_prepare(&q, | |
| 1028 | + "SELECT mid FROM mlink UNION " | |
| 1029 | + "SELECT srcid FROM tagxref WHERE srcid>0 UNION " | |
| 1030 | + "SELECT rid FROM tagxref UNION " | |
| 1031 | + "SELECT rid FROM attachment JOIN blob ON src=uuid UNION " | |
| 1032 | + "SELECT objid FROM event"); | |
| 1033 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1034 | + int rid = db_column_int(&q, 0); | |
| 1035 | + content_get(rid, &content); | |
| 1036 | + p = manifest_parse(&content, rid, 0); | |
| 1037 | + if( p ){ | |
| 1038 | + nArtifact++; | |
| 1039 | + nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); | |
| 1040 | + nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); | |
| 1041 | + for(i=0; i<p->nFile; i++){ | |
| 1042 | + nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", | |
| 1043 | + p->aFile[i].zName); | |
| 1044 | + } | |
| 1045 | + for(i=0; i<p->nParent; i++){ | |
| 1046 | + nErr += check_exists(p->azParent[i], flags, p, "parent of", 0); | |
| 1047 | + } | |
| 1048 | + for(i=0; i<p->nCherrypick; i++){ | |
| 1049 | + nErr += check_exists(p->aCherrypick[i].zCPTarget+1, flags, p, | |
| 1050 | + "cherry-pick target of", 0); | |
| 1051 | + nErr += check_exists(p->aCherrypick[i].zCPBase, flags, p, | |
| 1052 | + "cherry-pick baseline of", 0); | |
| 1053 | + } | |
| 1054 | + for(i=0; i<p->nCChild; i++){ | |
| 1055 | + nErr += check_exists(p->azCChild[i], flags, p, "in", 0); | |
| 1056 | + } | |
| 1057 | + for(i=0; i<p->nTag; i++){ | |
| 1058 | + nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0); | |
| 1059 | + } | |
| 1060 | + manifest_destroy(p); | |
| 1061 | + } | |
| 1062 | + } | |
| 1063 | + db_finalize(&q); | |
| 1064 | + if( nErr>0 || quietFlag==0 ){ | |
| 1065 | + fossil_print("%d missing or shunned references in %d control artifacts\n", | |
| 1066 | + nErr, nArtifact); | |
| 1067 | + } | |
| 1068 | +} | |
| 931 | 1069 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -926,5 +926,143 @@ | |
| 926 | cnt++; |
| 927 | } |
| 928 | db_finalize(&q); |
| 929 | fossil_print("%d orphans\n", cnt); |
| 930 | } |
| 931 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -926,5 +926,143 @@ | |
| 926 | cnt++; |
| 927 | } |
| 928 | db_finalize(&q); |
| 929 | fossil_print("%d orphans\n", cnt); |
| 930 | } |
| 931 | |
| 932 | /* Allowed flags for check_exists */ |
| 933 | #define MISSING_SHUNNED 0x0001 /* Do not report shunned artifacts */ |
| 934 | |
| 935 | /* This is a helper routine for test-artifacts. |
| 936 | ** |
| 937 | ** Check to see that artifact zUuid exists in the repository. If it does, |
| 938 | ** return 0. If it does not, generate an error message and return 1. |
| 939 | */ |
| 940 | static int check_exists( |
| 941 | const char *zUuid, /* The artifact we are checking for */ |
| 942 | unsigned flags, /* Flags */ |
| 943 | Manifest *p, /* The control artifact that references zUuid */ |
| 944 | const char *zRole, /* Role of zUuid in p */ |
| 945 | const char *zDetail /* Additional information, such as a filename */ |
| 946 | ){ |
| 947 | static Stmt q; |
| 948 | int rc = 0; |
| 949 | |
| 950 | db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); |
| 951 | if( zUuid==0 || zUuid[0]==0 ) return 0; |
| 952 | db_bind_text(&q, ":uuid", zUuid); |
| 953 | if( db_step(&q)==SQLITE_ROW ){ |
| 954 | int size = db_column_int(&q, 0); |
| 955 | if( size<0 ) rc = 2; |
| 956 | }else{ |
| 957 | rc = 1; |
| 958 | } |
| 959 | db_reset(&q); |
| 960 | if( rc ){ |
| 961 | const char *zCFType = "control artifact"; |
| 962 | char *zSrc; |
| 963 | char *zDate; |
| 964 | char *zErrType = "MISSING"; |
| 965 | if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ |
| 966 | if( flags & MISSING_SHUNNED ) return 0; |
| 967 | zErrType = "SHUNNED"; |
| 968 | } |
| 969 | switch( p->type ){ |
| 970 | case CFTYPE_MANIFEST: zCFType = "check-in"; break; |
| 971 | case CFTYPE_CLUSTER: zCFType = "cluster"; break; |
| 972 | case CFTYPE_CONTROL: zCFType = "tag"; break; |
| 973 | case CFTYPE_WIKI: zCFType = "wiki"; break; |
| 974 | case CFTYPE_TICKET: zCFType = "ticket"; break; |
| 975 | case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; |
| 976 | case CFTYPE_EVENT: zCFType = "event"; break; |
| 977 | } |
| 978 | zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); |
| 979 | if( p->rDate>0.0 ){ |
| 980 | zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); |
| 981 | }else{ |
| 982 | zDate = db_text(0, |
| 983 | "SELECT datetime(rcvfrom.mtime)" |
| 984 | " FROM blob, rcvfrom" |
| 985 | " WHERE blob.rcvid=rcvfrom.rcvid" |
| 986 | " AND blob.rid=%d", p->rid); |
| 987 | } |
| 988 | fossil_print("%s: %s\n %s %s %S (%d) %s\n", |
| 989 | zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); |
| 990 | if( zDetail && zDetail[0] ){ |
| 991 | fossil_print(" %s\n", zDetail); |
| 992 | } |
| 993 | fossil_free(zSrc); |
| 994 | fossil_free(zDate); |
| 995 | rc = 1; |
| 996 | } |
| 997 | return rc; |
| 998 | } |
| 999 | |
| 1000 | /* |
| 1001 | ** COMMAND: test-missing |
| 1002 | ** |
| 1003 | ** Usage: %fossil test-missing |
| 1004 | ** |
| 1005 | ** Look at every artifact in the repository and verify that |
| 1006 | ** all references are satisfied. Report any referenced artifacts |
| 1007 | ** that are missing or shunned. |
| 1008 | ** |
| 1009 | ** Options: |
| 1010 | ** |
| 1011 | ** --notshunned Do not report shunned artifacts |
| 1012 | ** --quiet Only show output if there are errors |
| 1013 | */ |
| 1014 | void test_missing(void){ |
| 1015 | Stmt q; |
| 1016 | Blob content; |
| 1017 | int nErr = 0; |
| 1018 | int nArtifact = 0; |
| 1019 | int i; |
| 1020 | Manifest *p; |
| 1021 | unsigned flags = 0; |
| 1022 | int quietFlag; |
| 1023 | |
| 1024 | if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; |
| 1025 | quietFlag = find_option("quiet","q",0)!=0; |
| 1026 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1027 | db_prepare(&q, |
| 1028 | "SELECT mid FROM mlink UNION " |
| 1029 | "SELECT srcid FROM tagxref WHERE srcid>0 UNION " |
| 1030 | "SELECT rid FROM tagxref UNION " |
| 1031 | "SELECT rid FROM attachment JOIN blob ON src=uuid UNION " |
| 1032 | "SELECT objid FROM event"); |
| 1033 | while( db_step(&q)==SQLITE_ROW ){ |
| 1034 | int rid = db_column_int(&q, 0); |
| 1035 | content_get(rid, &content); |
| 1036 | p = manifest_parse(&content, rid, 0); |
| 1037 | if( p ){ |
| 1038 | nArtifact++; |
| 1039 | nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); |
| 1040 | nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); |
| 1041 | for(i=0; i<p->nFile; i++){ |
| 1042 | nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", |
| 1043 | p->aFile[i].zName); |
| 1044 | } |
| 1045 | for(i=0; i<p->nParent; i++){ |
| 1046 | nErr += check_exists(p->azParent[i], flags, p, "parent of", 0); |
| 1047 | } |
| 1048 | for(i=0; i<p->nCherrypick; i++){ |
| 1049 | nErr += check_exists(p->aCherrypick[i].zCPTarget+1, flags, p, |
| 1050 | "cherry-pick target of", 0); |
| 1051 | nErr += check_exists(p->aCherrypick[i].zCPBase, flags, p, |
| 1052 | "cherry-pick baseline of", 0); |
| 1053 | } |
| 1054 | for(i=0; i<p->nCChild; i++){ |
| 1055 | nErr += check_exists(p->azCChild[i], flags, p, "in", 0); |
| 1056 | } |
| 1057 | for(i=0; i<p->nTag; i++){ |
| 1058 | nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0); |
| 1059 | } |
| 1060 | manifest_destroy(p); |
| 1061 | } |
| 1062 | } |
| 1063 | db_finalize(&q); |
| 1064 | if( nErr>0 || quietFlag==0 ){ |
| 1065 | fossil_print("%d missing or shunned references in %d control artifacts\n", |
| 1066 | nErr, nArtifact); |
| 1067 | } |
| 1068 | } |
| 1069 |