Index: src/branch.c ================================================================== --- src/branch.c +++ src/branch.c @@ -18,10 +18,37 @@ ** This file contains code used to create new branches within a repository. */ #include "config.h" #include "branch.h" #include + +/* +** If RID refers to a check-in, return the name of the branch for that +** check-in. +** +** Space to hold the returned value is obtained from fossil_malloc() +** and should be freed by the caller. +*/ +char *branch_of_rid(int rid){ + char *zBr = 0; + static Stmt q; + db_static_prepare(&q, + "SELECT value FROM tagxref" + " WHERE rid=$rid AND tagid=%d" + " AND tagtype>0", TAG_BRANCH); + db_bind_int(&q, "$rid", rid); + if( db_step(&q)==SQLITE_ROW ){ + zBr = fossil_strdup(db_column_text(&q,0)); + } + db_reset(&q); + if( zBr==0 ){ + static char *zMain = 0; + if( zMain==0 ) zMain = db_get("main-branch","trunk"); + zBr = fossil_strdup(zMain); + } + return zBr; +} /* ** fossil branch new NAME BASIS ?OPTIONS? ** argv0 argv1 argv2 argv3 argv4 */ Index: src/descendants.c ================================================================== --- src/descendants.c +++ src/descendants.c @@ -183,10 +183,35 @@ "INSERT INTO ok" " SELECT rid FROM ancestor;", rid, rid, directOnly ? "AND plink.isPrim" : "", N ); } + +/* +** Compute the youngest ancestor of record ID rid that is a member of +** branch zBranch. +*/ +int compute_youngest_ancestor_in_branch(int rid, const char *zBranch){ + return db_int(0, + "WITH RECURSIVE " + " ancestor(rid, mtime) AS (" + " SELECT %d, mtime FROM event WHERE objid=%d " + " UNION " + " SELECT plink.pid, event.mtime" + " FROM ancestor, plink, event" + " WHERE plink.cid=ancestor.rid" + " AND event.objid=plink.pid" + " ORDER BY mtime DESC" + " )" + " SELECT ancestor.rid FROM ancestor" + " WHERE EXISTS(SELECT 1 FROM tagxref" + " WHERE tagid=%d AND tagxref.rid=ancestor.rid" + " AND value=%Q AND tagtype>0)" + " LIMIT 1", + rid, rid, TAG_BRANCH, zBranch + ); +} /* ** Compute all direct ancestors (merge ancestors do not count) ** for the check-in rid and put them in a table named "ancestor". ** Label each generation with consecutive integers going backwards Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -1206,10 +1206,11 @@ const char *zFrom; const char *zTo; const char *zRe; const char *zW; const char *zGlob; + char *zQuery; ReCompiled *pRe = 0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } login_anonymous_available(); load_control(); @@ -1217,13 +1218,17 @@ diffType = atoi(PD("diff","2")); cookie_render(); zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); zBranch = P("branch"); - if( zBranch && zBranch[0] ){ + if( zBranch && zBranch[0]==0 ) zBranch = 0; + if( zBranch ){ + zQuery = mprintf("branch=%T", zBranch); cgi_replace_parameter("from", mprintf("root:%s", zBranch)); cgi_replace_parameter("to", zBranch); + }else{ + zQuery = mprintf("from=%T&to=%T",PD("from",""),PD("to","")); } pTo = vdiff_parse_manifest("to", &ridTo); if( pTo==0 ) return; pFrom = vdiff_parse_manifest("from", &ridFrom); if( pFrom==0 ) return; @@ -1233,34 +1238,38 @@ if(zGlob && !*zGlob){ zGlob = NULL; } diffFlags = construct_diff_flags(diffType); zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; - style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); + if( zBranch==0 ){ + style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); + } if( diffType!=0 ){ - style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&diff=0%s%T%s", - zFrom, zTo, + style_submenu_element("Hide Diff", "%R/vdiff?%s&diff=0%s%T%s", + zQuery, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } if( diffType!=2 ){ style_submenu_element("Side-by-Side Diff", - "%R/vdiff?from=%T&to=%T&diff=2%s%T%s", - zFrom, zTo, + "%R/vdiff?%s&diff=2%s%T%s", + zQuery, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } if( diffType!=1 ) { style_submenu_element("Unified Diff", - "%R/vdiff?from=%T&to=%T&diff=1%s%T%s", - zFrom, zTo, + "%R/vdiff?%s&diff=1%s%T%s", + zQuery, + zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); + } + if( zBranch==0 ){ + style_submenu_element("Invert", + "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } - style_submenu_element("Invert", - "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, - zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); if( zGlob ){ style_submenu_element("Clear glob", - "%R/vdiff?from=%T&to=%T&%s", zFrom, zTo, zW); + "%R/vdiff?%s&%s", zQuery, zW); }else{ style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); } if( diffType!=0 ){ style_submenu_checkbox("w", "Ignore Whitespace", 0, 0); @@ -1279,10 +1288,11 @@ if( zGlob ){ @

Only files matching the glob "%h(zGlob)" are shown.

} @

} + fossil_free(zQuery); manifest_file_rewind(pFrom); pFileFrom = manifest_file_next(pFrom, 0); manifest_file_rewind(pTo); pFileTo = manifest_file_next(pTo, 0); Index: src/name.c ================================================================== --- src/name.c +++ src/name.c @@ -108,21 +108,25 @@ return zEDate; } /* ** Return the RID that is the "root" of the branch that contains -** check-in "rid" if inBranch==0 or the first check-in in the branch -** if inBranch==1. +** check-in "rid". Details depending on eType: +** +** eType==0 The check-in of the parent branch off of which +** the branch containing RID originally diverged. +** +** eType==1 The first check-in of the branch that contains RID. +** +** eType==2 The youngest ancestor of RID that is on the branch +** from which the branch containing RID diverged. */ -int start_of_branch(int rid, int inBranch){ +int start_of_branch(int rid, int eType){ Stmt q; int rc; - char *zBr; - zBr = db_text("trunk","SELECT value FROM tagxref" - " WHERE rid=%d AND tagid=%d" - " AND tagtype>0", - rid, TAG_BRANCH); + int ans = rid; + char *zBr = branch_of_rid(rid); db_prepare(&q, "SELECT pid, EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid)" " FROM plink" @@ -130,18 +134,23 @@ TAG_BRANCH, zBr ); fossil_free(zBr); do{ db_reset(&q); - db_bind_int(&q, ":cid", rid); + db_bind_int(&q, ":cid", ans); rc = db_step(&q); if( rc!=SQLITE_ROW ) break; - if( inBranch && db_column_int(&q,1)==0 ) break; - rid = db_column_int(&q, 0); - }while( db_column_int(&q, 1)==1 && rid>0 ); + if( eType==1 && db_column_int(&q,1)==0 ) break; + ans = db_column_int(&q, 0); + }while( db_column_int(&q, 1)==1 && ans>0 ); db_finalize(&q); - return rid; + if( eType==2 && ans>0 ){ + zBr = branch_of_rid(ans); + ans = compute_youngest_ancestor_in_branch(rid, zBr); + fossil_free(zBr); + } + return ans; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** @@ -165,10 +174,11 @@ ** ** The zType parameter specifies the type of artifact: ci, t, w, e, g, f. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. +** ** zType is "ci" in most use cases since we are usually searching for ** a check-in. ** ** Note that the input zTag for types "t" and "e" is the artifact hash of ** the ticket-change or technote-change artifact, not the randomly generated @@ -270,14 +280,19 @@ ); if( startOfBranch ) rid = start_of_branch(rid,1); return rid; } - /* root:TAG -> The origin of the branch */ - if( memcmp(zTag, "root:", 5)==0 ){ + /* root:BR -> The origin of the branch named BR */ + if( strncmp(zTag, "root:", 5)==0 ){ rid = symbolic_name_to_rid(zTag+5, zType); return start_of_branch(rid, 0); + } + /* rootx:BR -> Most recent merge-in for the branch name BR */ + if( strncmp(zTag, "merge-in:", 9)==0 ){ + rid = symbolic_name_to_rid(zTag+9, zType); + return start_of_branch(rid, 2); } /* symbolic-name ":" date-time */ nTag = strlen(zTag); for(i=0; i ", g.argv[i]); - if( name_to_uuid(&name, 1, "*") ){ - fossil_print("ERROR: %s\n", g.zErrMsg); - fossil_error_reset(); - }else{ - fossil_print("%s\n", blob_buffer(&name)); - } - blob_reset(&name); + if( strcmp(g.argv[i],"--count")==0 && i+1 ", g.argv[i]); + if( name_to_uuid(&name, 1, "*") ){ + fossil_print("ERROR: %s\n", g.zErrMsg); + fossil_error_reset(); + }else{ + fossil_print("%s\n", blob_buffer(&name)); + } + blob_reset(&name); + }while( n-- > 0 ); } } /* ** Convert a name to a rid. If the name can be any of the various forms