Index: src/diff.c
==================================================================
--- src/diff.c
+++ src/diff.c
@@ -40,10 +40,11 @@
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
#define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
#define DIFF_STRIP_EOLCR (((u64)0x10)<<32) /* Strip trailing CR */
+#define DIFF_NO_ERRMSG (((u64)0x20)<<32) /* Do not generate error messages */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
@@ -1804,10 +1805,12 @@
/*
** Append the error message to pOut.
*/
void diff_errmsg(Blob *pOut, const char *msg, int diffFlags){
+ if( pOut==0 ) return;
+ if( diffFlags & DIFF_NO_ERRMSG ) return;
if( diffFlags & DIFF_HTML ){
blob_appendf(pOut, "
%s
", msg);
}else{
blob_append(pOut, msg, -1);
}
@@ -1858,23 +1861,21 @@
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, diffFlags);
if( c.aFrom==0 || c.aTo==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
- if( pOut ){
- diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
- }
+ diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
return 0;
}
/* Compute the difference */
diff_all(&c);
if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
- if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
+ diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
return 0;
}
if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
int i, m, n;
int *a = c.aEdit;
@@ -1882,11 +1883,11 @@
for(i=m=n=0; i10000 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
- if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
+ diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
return 0;
}
}
if( (diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
Index: src/diffcmd.c
==================================================================
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -927,5 +927,82 @@
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
cgi_set_content_type("text/plain");
diff_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE, 0);
}
+
+/*
+** Compute a string (into pOut) that is a diff of check-in "rid" and its
+** primary parent. The diff has no context lines and is designed to support
+** full-text search.
+*/
+void diff_for_search(int rid, Blob *pOut){
+ Manifest *pFrom, *pTo;
+ ManifestFile *pFromFile, *pToFile;
+ const u64 diffFlags = DIFF_NO_ERRMSG | DIFF_IGNORE_ALLWS | DIFF_CONTEXT_EX;
+
+ pTo = manifest_get(rid, CFTYPE_MANIFEST, 0);
+ if( pTo==0 ) return;
+ if( pTo->nParent==0 ){
+ manifest_destroy(pTo);
+ return;
+ }
+ pFrom = manifest_get_by_name(pTo->azParent[0], 0);
+ manifest_file_rewind(pFrom);
+ manifest_file_rewind(pTo);
+ pFromFile = manifest_file_next(pFrom,0);
+ pToFile = manifest_file_next(pTo,0);
+
+ while( pFromFile || pToFile ){
+ int cmp;
+ if( pFromFile==0 ){
+ cmp = +1;
+ }else if( pToFile==0 ){
+ cmp = -1;
+ }else{
+ cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
+ }
+ if( cmp<0 ){
+ blob_appendf(pOut, "DELETED %s\n", pFromFile->zName);
+ pFromFile = manifest_file_next(pFrom,0);
+ }else if( cmp>0 ){
+ blob_appendf(pOut, "ADDED %s\n", pToFile->zName);
+ pToFile = manifest_file_next(pTo,0);
+ }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
+ /* No changes */
+ pFromFile = manifest_file_next(pFrom,0);
+ pToFile = manifest_file_next(pTo,0);
+ }else{
+ Blob fileA, fileB;
+ blob_appendf(pOut, "CHANGED %s\n", pToFile->zName);
+ rid = uuid_to_rid(pFromFile->zUuid, 0);
+ content_get(rid, &fileA);
+ rid = uuid_to_rid(pToFile->zUuid, 0);
+ content_get(rid, &fileB);
+ text_diff(&fileA, &fileB, pOut, 0, diffFlags);
+ blob_reset(&fileA);
+ blob_reset(&fileB);
+ pFromFile = manifest_file_next(pFrom,0);
+ pToFile = manifest_file_next(pTo,0);
+ }
+ }
+ manifest_destroy(pFrom);
+ manifest_destroy(pTo);
+}
+
+/*
+** Implement the diff(RID) SQL function that returns a text diff for
+** check-in RID.
+*/
+void diff_sqlfunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int rid;
+ Blob out;
+ rid = sqlite3_value_int(argv[0]);
+ blob_init(&out, 0, 0);
+ diff_for_search(rid, &out);
+ sqlite3_result_text(context, blob_str(&out), blob_size(&out), SQLITE_TRANSIENT);
+ blob_reset(&out);
+}
Index: src/search.c
==================================================================
--- src/search.c
+++ src/search.c
@@ -510,10 +510,12 @@
search_title_sqlfunc, 0, 0);
sqlite3_create_function(db, "body", 3, SQLITE_UTF8, 0,
search_body_sqlfunc, 0, 0);
sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
search_urlencode_sqlfunc, 0, 0);
+ sqlite3_create_function(db, "diff", 1, SQLITE_UTF8, 0,
+ diff_sqlfunc, 0, 0);
}
/*
** Testing the search function.
**
@@ -600,11 +602,12 @@
/* What to search for */
#define SRCH_CKIN 0x0001 /* Search over check-in comments */
#define SRCH_DOC 0x0002 /* Search over embedded documents */
#define SRCH_TKT 0x0004 /* Search over tickets */
#define SRCH_WIKI 0x0008 /* Search over wiki */
-#define SRCH_ALL 0x000f /* Search over everything */
+#define SRCH_DIFF 0x0010 /* Search check-in diffs */
+#define SRCH_ALL 0x001f /* Search over everything */
#endif
/*
** Remove bits from srchFlags which are disallowed by either the
** current server configuration or by user permissions.
@@ -615,13 +618,14 @@
static const struct { unsigned m; const char *zKey; } aSetng[] = {
{ SRCH_CKIN, "search-ci" },
{ SRCH_DOC, "search-doc" },
{ SRCH_TKT, "search-tkt" },
{ SRCH_WIKI, "search-wiki" },
+ { SRCH_DIFF, "search-diff" },
};
int i;
- if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC);
+ if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_DIFF);
if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT);
if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
for(i=0; i
@@ -1063,10 +1070,11 @@
** y=TYPE What to search.
** c -> check-ins
** d -> documentation
** t -> tickets
** w -> wiki
+** x -> diff
** all -> everything
*/
void search_page(void){
login_check_credentials();
style_header("Search");
@@ -1169,10 +1177,11 @@
**
** cType: d Embedded documentation
** w Wiki page
** c Check-in comment
** t Ticket text
+** x Check-in diffs
**
** rid The RID of an artifact that defines the object
** being searched.
**
** zName Name of the object being searched.
@@ -1182,10 +1191,11 @@
int rid, /* BLOB.RID or TAG.TAGID value for document */
const char *zName, /* Auxiliary information */
Blob *pOut /* OUT: Initialize to the search text */
){
blob_init(pOut, 0, 0);
+ /* printf("stext(%c%d)\n", cType, rid); fflush(stdout); */
switch( cType ){
case 'd': { /* Documents */
Blob doc;
content_get(rid, &doc);
blob_to_utf8_no_bom(&doc, 0);
@@ -1201,10 +1211,14 @@
get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype),
pOut);
blob_reset(&wiki);
manifest_destroy(pWiki);
break;
+ }
+ case 'x': { /* Check-in diff */
+ diff_for_search(rid, pOut);
+ break;
}
case 'c': { /* Check-in Comments */
static Stmt q;
static int isPlainText = -1;
db_static_prepare(&q,
@@ -1411,10 +1425,14 @@
search_sql_setup(g.db);
db_multi_exec(
"INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
" SELECT 'c', objid, 0 FROM event WHERE type='ci';"
);
+ db_multi_exec(
+ "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
+ " SELECT 'x', objid, 0 FROM event WHERE type='ci';"
+ );
db_multi_exec(
"WITH latest_wiki(rid,name,mtime) AS ("
" SELECT tagxref.rid, substr(tag.tagname,6), max(tagxref.mtime)"
" FROM tag, tagxref"
" WHERE tag.tagname GLOB 'wiki-*'"
@@ -1545,10 +1563,32 @@
" WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed"
" AND event.objid=ftsdocs.rid"
" AND blob.rid=ftsdocs.rid"
);
}
+
+/*
+** Deal with all of the unindexed 'x' terms in FTSDOCS
+*/
+static void search_update_diff_index(void){
+ db_multi_exec(
+ "INSERT INTO ftsidx(docid,title,body)"
+ " SELECT rowid, '', body('x',rid,NULL) FROM ftsdocs"
+ " WHERE type='x' AND NOT idxed;"
+ );
+ db_multi_exec(
+ "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
+ " SELECT ftsdocs.rowid, 1, 'x', ftsdocs.rid, NULL,"
+ " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime)),"
+ " printf('/info/%%.20s',blob.uuid),"
+ " event.mtime"
+ " FROM ftsdocs, event, blob"
+ " WHERE ftsdocs.type='x' AND NOT ftsdocs.idxed"
+ " AND event.objid=ftsdocs.rid"
+ " AND blob.rid=ftsdocs.rid"
+ );
+}
/*
** Deal with all of the unindexed 't' terms in FTSDOCS
*/
static void search_update_ticket_index(void){
@@ -1604,10 +1644,13 @@
search_sql_setup(g.db);
if( srchFlags & (SRCH_CKIN|SRCH_DOC) ){
search_update_doc_index();
search_update_checkin_index();
}
+ if( srchFlags & SRCH_DIFF ){
+ search_update_diff_index();
+ }
if( srchFlags & SRCH_TKT ){
search_update_ticket_index();
}
if( srchFlags & SRCH_WIKI ){
search_update_wiki_index();
@@ -1637,14 +1680,14 @@
** reindex Rebuild the search index. This is a no-op if
** index search is disabled
**
** index (on|off) Turn the search index on or off
**
-** enable cdtw Enable various kinds of search. c=Check-ins,
-** d=Documents, t=Tickets, w=Wiki.
+** enable cdtwx Enable various kinds of search. c=Check-ins,
+** d=Documents, t=Tickets, w=Wiki, x=Diffs.
**
-** disable cdtw Disable various kinds of search
+** disable cdtwx Disable various kinds of search
**
** stemmer (on|off) Turn the Porter stemmer on or off for indexed
** search. (Unindexed search is never stemmed.)
**
** The current search settings are displayed after any changes are applied.
@@ -1657,14 +1700,15 @@
{ 3, "disable" },
{ 4, "enable" },
{ 5, "stemmer" },
};
static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = {
- { "search-ckin", "check-in search:", "c" },
- { "search-doc", "document search:", "d" },
- { "search-tkt", "ticket search:", "t" },
- { "search-wiki", "wiki search:", "w" },
+ { "search-ckin", "check-in comment search:", "c" },
+ { "search-diff", "check-in diff search:", "x" },
+ { "search-doc", "document search:", "d" },
+ { "search-tkt", "ticket search:", "t" },
+ { "search-wiki", "wiki search:", "w" },
};
char *zSubCmd = 0;
int i, j, n;
int iCmd = 0;
int iAction = 0;
@@ -1683,10 +1727,12 @@
zSubCmd, blob_str(&all));
return;
}
iCmd = aCmd[i].iCmd;
}
+ login_set_capabilities("s",0);
+ g.zTop = "";
if( iCmd==1 ){
if( search_index_exists() ) iAction = 2;
}
if( iCmd==2 ){
if( g.argc<3 ) usage("index (on|off)");
@@ -1719,19 +1765,19 @@
search_rebuild_index();
}
/* Always show the status before ending */
for(i=0; iWhen searching documents, use the versions of the files found at the
@ type of the "Document Branch" branch. Recommended value: "trunk".
@ Document search is disabled if blank.
@
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
+ @
+ onoff_attribute("Search Check-in Diffs","search-diff", "sx", 0, 0);
@
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
@
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
@