Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -674,10 +674,12 @@ @ @ %z(href("%R/tree?ci=%S",zUuid))files @ | %z(href("%R/fileage?name=%S",zUuid))file ages @ | %z(href("%R/tree?nofiles&type=tree&ci=%S",zUuid))folders @ | %z(href("%R/artifact/%S",zUuid))manifest + @ | %z(href("%R/vdiff?from=pbranch:%S&to=%S",zUuid,zUuid)) + @ branch diff if( g.perm.Write ){ @ | %z(href("%R/ci_edit?r=%S",zUuid))edit } @ @ Index: src/name.c ================================================================== --- src/name.c +++ src/name.c @@ -181,10 +181,19 @@ " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", &zTag[4], zType); return rid; } + + /* "pbranch:", as for parent branch. It returns the checkin of + the last checkin of the parent branch that has been merged in. */ + if( memcmp(zTag, "pbranch:", 8)==0 ){ + rid = symbolic_name_to_rid(&zTag[8], zType); + if( rid==0 ) return 0; /* TODO: Negative rid allowed here? */ + rid = get_parent_branch_rid(rid); + return rid; + } /* "tag:" + symbolic-name */ if( memcmp(zTag, "tag:", 4)==0 ){ rid = db_int(0, "SELECT event.objid, max(event.mtime)" @@ -1058,5 +1067,108 @@ */ void test_phatoms_cmd(void){ db_find_and_open_repository(0,0); describe_artifacts_to_stdout("IN (SELECT rid FROM blob WHERE size<0)", 0); } + +/* +** Returns the rid for the last checkin where the parent branch was merged. +*/ +int get_parent_branch_rid( + int branchRid /* The rid to find parent branch for. */ +){ + Stmt s; + char *branchName = 0; /* Name of the branch at rid */ + char *parentBranchName = 0; /* Name of the parent branch */ + int rid; + + /* Get the name of the current branch */ + branchName = db_text(0, + "SELECT value FROM tagxref" + " WHERE tagid=%d" + " AND tagxref.tagtype>0" + " AND rid=%d", + TAG_BRANCH, branchRid + ); + + if( !branchName ) + return 0; + + /* Find the name of the branch this was forked from */ + db_prepare(&s, + "SELECT pid, tagxref.value FROM plink JOIN tagxref" + " WHERE cid=:rid" + " AND isprim=1" + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0" + " AND tagxref.rid=pid", + TAG_BRANCH + ); + rid = branchRid; + while( rid>0 ){ + db_bind_int(&s, ":rid", rid); + if( db_step(&s)==SQLITE_ROW ){ + const char *zValue; /* Branch name of the pid */ + rid = db_column_int(&s, 0); + zValue = db_column_text(&s, 1); + if( !zValue ){ + rid = 0; + break; + } + if( fossil_strcmp(zValue,branchName) ){ + parentBranchName = fossil_strdup(zValue); + break; + } + }else{ + rid = 0; + break; + } + db_reset(&s); + } + db_finalize(&s); + + if( rid==0 ){ + fossil_free(branchName); + fossil_free(parentBranchName); + return 0; + } + + /* Find the last checkin coming from the parent branch */ + db_prepare(&s, + "SELECT pid, tagxref.value FROM plink JOIN tagxref" + " WHERE cid=:rid" + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0" + " AND tagxref.rid=pid ORDER BY isprim ASC", + TAG_BRANCH + ); + rid = branchRid; + while( rid>0 ){ + db_bind_int(&s, ":rid", rid); + int found = 0; + while( db_step(&s)==SQLITE_ROW ){ + const char *zValue; /* Branch name of the pid */ + found++; + rid = db_column_int(&s, 0); + zValue = db_column_text(&s, 1); + if( !zValue ){ + break; + } + if( fossil_strcmp(parentBranchName,zValue)==0 ){ + /* Found the last merge from the parent branch */ + db_finalize(&s); + fossil_free(branchName); + fossil_free(parentBranchName); + return rid; + } + } + if( found==0 ){ + break; + } + db_reset(&s); + } + db_finalize(&s); + + fossil_free(branchName); + fossil_free(parentBranchName); + return 0; +}