Overview
SHA1 Hash: | b6e22e62cf26115617822c7940cec5d0dc687184 |
---|---|
Date: | 2009-01-20 16:51:19 |
User: | drh |
Comment: | Attempting to rationalize the tagging and branching logic. The "branch" command has been resurrected and appears to work now. The "tag branch" command has been removed. Special tags "newbranch" and "closed" used to manage branches. New changes are not well-tested - use with caution. You must "rebuild" when upgrading through this version. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/branch.c from [ff6e350782] to [85e459979d].
@@ -25,153 +25,170 @@ */ #include "config.h" #include "branch.h" #include <assert.h> +/* +** fossil branch new BRANCH-NAME ?ORIGIN-CHECK-IN? ?-bgcolor COLOR? +** argv0 argv1 argv2 argv3 argv4 +*/ void branch_new(void){ - int vid, nvid, noSign; - Stmt q; - char *zBranch, *zUuid, *zDate, *zComment; - const char *zColor; - Blob manifest; - Blob mcksum; /* Self-checksum on the manifest */ - Blob cksum1, cksum2; /* Before and after commit checksums */ - Blob cksum1b; /* Checksum recorded in the manifest */ + int rootid; /* RID of the root check-in - what we branch off of */ + int brid; /* RID of the branch check-in */ + int noSign; /* True if the branch is unsigned */ + int i; /* Loop counter */ + char *zUuid; /* Artifact ID of origin */ + Stmt q; /* Generic query */ + const char *zBranch; /* Name of the new branch */ + char *zDate; /* Date that branch was created */ + char *zComment; /* Check-in comment for the new branch */ + const char *zColor; /* Color of the new branch */ + Blob branch; /* manifest for the new branch */ + Blob parent; /* root check-in manifest */ + Manifest mParent; /* Parsed parent manifest */ + Blob mcksum; /* Self-checksum on the manifest */ noSign = find_option("nosign","",0)!=0; - db_must_be_within_tree(); - noSign = db_get_int("omitsign", 0)|noSign; - zColor = find_option("bgcolor","c",1); - + zColor = find_option("bgcolor","c",1); verify_all_options(); - - /* fossil branch new name */ if( g.argc<3 ){ - usage("branch new ?-bgcolor COLOR? BRANCH-NAME"); - } + usage("branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR?"); + } + db_find_and_open_repository(1); + noSign = db_get_int("omitsign", 0)|noSign; + + /* fossil branch new name */ zBranch = g.argv[3]; if( zBranch==0 || zBranch[0]==0 ){ fossil_panic("branch name cannot be empty"); } + if( db_exists( + "SELECT 1 FROM tagxref" + " WHERE tagtype>0" + " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')", + zBranch)!=0 ){ + fossil_fatal("branch \"%s\" already exists", zBranch); + } user_select(); db_begin_transaction(); - if( unsaved_changes() ){ - fossil_panic("there are uncommitted changes. please commit first"); + if( g.argc<5 ){ + if( unsaved_changes() ){ + fossil_fatal("there are uncommitted changes. please commit first"); + } + rootid = db_lget_int("checkout", 0); + }else{ + rootid = name_to_rid(g.argv[4]); + } + if( rootid==0 ){ + fossil_fatal("unable to locate check-in off of which to branch"); } - vid = db_lget_int("checkout", 0); - vfile_aggregate_checksum_disk(vid, &cksum1); - - /* Create our new manifest */ - blob_zero(&manifest); - zComment = mprintf("Branch created %s", zBranch); - blob_appendf(&manifest, "C %F\n", zComment); + /* Create a manifest for the new branch */ + blob_zero(&branch); + zComment = mprintf("Create new branch named \"%h\"", zBranch); + blob_appendf(&branch, "C %F\n", zComment); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; - blob_appendf(&manifest, "D %s\n", zDate); + blob_appendf(&branch, "D %s\n", zDate); - db_prepare(&q, - "SELECT pathname, uuid FROM vfile JOIN blob ON vfile.mrid=blob.rid" - " WHERE NOT deleted AND vfile.vid=%d" - " ORDER BY 1", vid); - while( db_step(&q)==SQLITE_ROW ){ - const char *zName = db_column_text(&q, 0); - const char *zUuid = db_column_text(&q, 1); - blob_appendf(&manifest, "F %F %s\n", zName, zUuid); + /* Copy all of the content from the parent into the branch */ + content_get(rootid, &parent); + manifest_parse(&mParent, &parent); + if( mParent.type!=CFTYPE_MANIFEST ){ + fossil_fatal("%s is not a valid check-in", g.argv[4]); + } + for(i=0; i<mParent.nFile; ++i){ + if( mParent.aFile[i].zPerm[0] ){ + blob_appendf(&branch, "F %F %s %s\n", + mParent.aFile[i].zName, + mParent.aFile[i].zUuid, + mParent.aFile[i].zPerm); + }else{ + blob_appendf(&branch, "F %F %s\n", + mParent.aFile[i].zName, + mParent.aFile[i].zUuid); + } } - db_finalize(&q); - - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); - blob_appendf(&manifest, "P %s\n", zUuid); - blob_appendf(&manifest, "R %b\n", &cksum1); + zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); + blob_appendf(&branch, "P %s\n", zUuid); + blob_appendf(&branch, "R %s\n", mParent.zRepoCksum); + manifest_clear(&mParent); + /* Add the symbolic branch name and the "newbranch" tag to identify + ** this as a new branch */ if( zColor!=0 ){ - blob_appendf(&manifest, "T *bgcolor * %F\n", zColor); - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); - }else{ - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); + blob_appendf(&branch, "T *bgcolor * %F\n", zColor); } + blob_appendf(&branch, "T *sym-%F *\n", zBranch); + blob_appendf(&branch, "T +newbranch *\n"); - /* Cancel any tags that propagate */ + /* Cancel all other symbolic tags */ db_prepare(&q, - "SELECT tagname" - " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid" - " WHERE rid=%d AND tagtype=2", vid); + "SELECT tagname FROM tagxref, tag" + " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" + " AND tagtype>0 AND tagname GLOB 'sym-*'" + " ORDER BY tagname", + rootid); while( db_step(&q)==SQLITE_ROW ){ - const char *zTagname = db_column_text(&q, 0); - blob_appendf(&manifest, "T -%s *\n", zTagname); + const char *zTag = db_column_text(&q, 0); + blob_appendf(&branch, "T -%s *\n", zTag); } db_finalize(&q); - blob_appendf(&manifest, "U %F\n", g.zLogin); - md5sum_blob(&manifest, &mcksum); - blob_appendf(&manifest, "Z %b\n", &mcksum); - if( !noSign && clearsign(&manifest, &manifest) ){ + blob_appendf(&branch, "U %F\n", g.zLogin); + md5sum_blob(&branch, &mcksum); + blob_appendf(&branch, "Z %b\n", &mcksum); + if( !noSign && clearsign(&branch, &branch) ){ Blob ans; blob_zero(&ans); prompt_user("unable to sign manifest. continue [y/N]? ", &ans); if( blob_str(&ans)[0]!='y' ){ db_end_transaction(1); exit(1); } } - /*blob_write_to_file(&manifest, "manifest.new");*/ - - nvid = content_put(&manifest, 0, 0); - if( nvid==0 ){ + brid = content_put(&branch, 0, 0); + if( brid==0 ){ fossil_panic("trouble committing manifest: %s", g.zErrMsg); } - db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); - manifest_crosslink(nvid, &manifest); - content_deltify(vid, nvid, 0); - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); - printf("Branch Version: %s\n", zUuid); - printf("\n"); - printf("Notice: working copy not updated to the new branch. If\n"); - printf(" you wish to work on the new branch, update to\n"); - printf(" that branch first:\n"); - printf("\n"); - printf(" fossil update %s\n", zBranch); - - /* Verify that the manifest checksum matches the expected checksum */ - vfile_aggregate_checksum_repository(nvid, &cksum2); - vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); - if( blob_compare(&cksum1, &cksum1b) ){ - fossil_panic("manifest checksum does not agree with manifest: " - "%b versus %b", &cksum1, &cksum1b); + db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); + if( manifest_crosslink(brid, &branch)==0 ){ + fossil_panic("unable to install new manifest"); + } + content_deltify(rootid, brid, 0); + zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); + printf("New branch: %s\n", zUuid); + if( g.argc==3 ){ + printf( + "\n" + "Note: the local check-out has not been updated to the new\n" + " branch. To begin working on the new branch, do this:\n" + "\n" + " %s update %s\n", + g.argv[0], zBranch + ); } - /* Verify that the commit did not modify any disk images. */ - vfile_aggregate_checksum_disk(vid, &cksum2); - if( blob_compare(&cksum1, &cksum2) ){ - fossil_panic("tree checksums before and after commit do not match"); - } - - /* Clear the undo/redo stack */ - undo_reset(); /* Commit */ db_end_transaction(0); /* Do an autosync push, if requested */ autosync(AUTOSYNC_PUSH); } /* -** NB: The "branch" command is disabled pending further discussion of its -** purpose and usefulness.... -** -** COM MAND: branch +** COMMAND: branch ** ** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE? ** ** Run various subcommands on the branches of the open repository or ** of the repository identified by the -R or --repository option. ** -** %fossil branch new ?-bgcolor COLOR? BRANCH-NAME +** %fossil branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR? ** ** Create a new branch BRANCH-NAME. You can optionally give ** a commit message and branch color. ** ** %fossil branch list
Modified src/checkin.c from [a3f1065edb] to [b983c3b208].
@@ -346,10 +346,29 @@ g.aCommitFile[ii-2] = 0; } } /* +** Return true if the check-in with RID=rid has one or more child +** check-ins which are not tagged with "newbranch". In other words, +** return true if the check-in is not a leaf. +*/ +int is_not_a_leaf(int rid){ + return db_exists( + "SELECT 1 FROM plink" + " WHERE pid=%d" + " AND NOT EXIST(" + "SELECT 1 FROM tagxref" + " WHERE tagxref.rid=plink.cid" + " AND tagxref.tagid=%d" + " AND tagxref.tagtype=1" + ")", + rid, TAG_NEWBRANCH + ); +} + +/* ** COMMAND: ci ** COMMAND: commit ** ** Usage: %fossil commit ?-m COMMENT? ?--nosign? ?FILE...? ** @@ -440,14 +459,14 @@ fossil_panic("file %s has not changed", blob_str(&unmodified)); } } vid = db_lget_int("checkout", 0); - if( db_exists("SELECT 1 FROM plink WHERE pid=%d", vid) ){ + if( is_not_a_leaf(vid) ){ wouldFork=1; if( forceFlag==0 ){ - fossil_fatal("would fork. use -f or --force"); + fossil_fatal("would fork. \"update\" first or use -f or --force."); } } vfile_aggregate_checksum_disk(vid, &cksum1); if( zComment ){ blob_zero(&comment);
Modified src/descendants.c from [a11699aaf4] to [a19ebf91b9].
@@ -30,46 +30,111 @@ /* ** Create a temporary table named "leaves" if it does not ** already exist. Load this table with the RID of all -** versions that are leaves which are decended from -** version iBase. +** check-ins that are leaves which are decended from +** check-in iBase. +** +** A "leaf" is a check-in that has no children. For the purpose +** of finding leaves, children marked with the "newbranch" tag are +** not counted as children. For example: +** +** +** A -> B -> C -> D +** `-> E +** +** D and E are clearly leaves since they have no children. If +** D has the "newbranch" tag, then C is also a leaf since its only +** child is marked as a newbranch. +** +** The closeMode flag determines behavior associated with the "closed" +** tag: +** +** closeMode==0 Show all leaves regardless of the "closed" tag. +** +** closeMode==1 Show only leaves without the "closed" tag. +** +** closeMode==2 Show only leaves with the "closed" tag. +** +** The default behavior is to ignore closed leaves (closeMode==0). To +** Show all leaves, use closeMode==1. To show only closed leaves, use +** closeMode==2. */ -void compute_leaves(int iBase){ +void compute_leaves(int iBase, int closeMode){ Bag seen; /* Descendants seen */ Bag pending; /* Unpropagated descendants */ + Stmt q; /* Query to find children of a check-in */ + Stmt isBr; /* Query to check to see if a check-in starts a new branch */ + Stmt ins; /* INSERT statement for a new record */ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS leaves(" " rid INTEGER PRIMARY KEY" ");" "DELETE FROM leaves;" ); bag_init(&seen); bag_init(&pending); + if( iBase<=0 ){ + iBase = db_int(0, "SELECT objid FROM event WHERE type='ci'" + " ORDER BY mtime LIMIT 1"); + } bag_insert(&pending, iBase); + db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid"); + db_prepare(&isBr, + "SELECT 1 FROM tagxref WHERE rid=:rid AND tagid=%d AND tagtype=1", + TAG_NEWBRANCH + ); + db_prepare(&ins, "INSERT OR IGNORE INTO leaves VALUES(:rid)"); while( bag_count(&pending) ){ int rid = bag_first(&pending); int cnt = 0; - Stmt q; bag_remove(&pending, rid); - db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid); + db_bind_int(&q, ":rid", rid); while( db_step(&q)==SQLITE_ROW ){ int cid = db_column_int(&q, 0); if( bag_insert(&seen, cid) ){ bag_insert(&pending, cid); } - cnt++; + db_bind_int(&isBr, ":rid", cid); + if( db_step(&isBr)==SQLITE_DONE ){ + cnt++; + } + db_reset(&isBr); } - db_finalize(&q); + db_reset(&q); if( cnt==0 ){ - db_multi_exec("INSERT INTO leaves VALUES(%d)", rid); + db_bind_int(&ins, ":rid", rid); + db_step(&ins); + db_reset(&ins); } } + db_finalize(&ins); + db_finalize(&isBr); + db_finalize(&q); bag_clear(&pending); bag_clear(&seen); + if( closeMode==1 ){ + db_multi_exec( + "DELETE FROM leaves WHERE rid IN" + " (SELECT leaves.rid FROM leaves, tagxref" + " WHERE tagxref.rid=leaves.rid " + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0)", + TAG_CLOSED + ); + }else if( closeMode==2 ){ + db_multi_exec( + "DELETE FROM leaves WHERE rid NOT IN" + " (SELECT leaves.rid FROM leaves, tagxref" + " WHERE tagxref.rid=leaves.rid " + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0)", + TAG_CLOSED + ); + } } /* ** Load the record ID rid and up to N-1 closest ancestors into ** the "ok" table. @@ -146,11 +211,11 @@ base = db_lget_int("checkout", 0); }else{ base = name_to_rid(g.argv[2]); } if( base==0 ) return; - compute_leaves(base); + compute_leaves(base, 0); db_prepare(&q, "%s" " AND event.objid IN (SELECT rid FROM leaves)" " ORDER BY event.mtime DESC", timeline_query_for_tty() @@ -160,22 +225,26 @@ } /* ** COMMAND: leaves ** -** Usage: %fossil leaves +** Usage: %fossil leaves ?--all? ?--closed? ** -** Find leaves of all branches. +** Find leaves of all branches. By default show only open leaves. +** The --all flag causes all leaves (closed and open) to be shown. +** The --closed flag shows only closed leaves. */ -void branches_cmd(void){ +void leaves_cmd(void){ Stmt q; + int showAll = find_option("all", 0, 0)!=0; + int showClosed = find_option("closed", 0, 0)!=0; db_must_be_within_tree(); + compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1); db_prepare(&q, "%s" - " AND blob.rid IN" - " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)" + " AND blob.rid IN leaves" " ORDER BY event.mtime DESC", timeline_query_for_tty() ); print_timeline(&q, 2000); db_finalize(&q); @@ -186,20 +255,38 @@ ** ** Find leaves of all branches. */ void leaves_page(void){ Stmt q; + int showAll = P("all")!=0; + int showClosed = P("closed")!=0; login_check_credentials(); if( !g.okRead ){ login_needed(); return; } + if( !showAll ){ + style_submenu_element("All", "All", "leaves?all"); + } + if( !showClosed ){ + style_submenu_element("Closed", "Closed", "leaves?closed"); + } + if( showClosed || showAll ){ + style_submenu_element("Open", "Open", "leaves"); + } style_header("Leaves"); login_anonymous_available(); + compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1); + if( showAll ){ + @ <h1>All leaves, both open and closed</h1> + }else if( showClosed ){ + @ <h1>Closed leaves only</h1> + }else{ + @ <h1>All open leaves</h1> + } db_prepare(&q, "%s" - " AND blob.rid IN" - " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)" + " AND blob.rid IN leaves" " ORDER BY event.mtime DESC", timeline_query_for_www() ); www_print_timeline(&q); db_finalize(&q);
Modified src/info.c from [594a3689d1] to [6aacb697cc].
@@ -442,11 +442,11 @@ }else{ @ %h(zName)</li> } } @ </ul> - compute_leaves(rid); + compute_leaves(rid, 0); showDescendants(rid, 2, "Descendants"); showLeaves(); showAncestors(rid, 2, "Ancestors"); style_footer(); }
Modified src/schema.c from [89e594d6c0] to [aef0575363].
@@ -280,10 +280,12 @@ @ INSERT INTO tag VALUES(2, 'comment'); -- TAG_COMMENT @ INSERT INTO tag VALUES(3, 'user'); -- TAG_USER @ INSERT INTO tag VALUES(4, 'hidden'); -- TAG_HIDDEN @ INSERT INTO tag VALUES(5, 'private'); -- TAG_PRIVATE @ INSERT INTO tag VALUES(6, 'cluster'); -- TAG_CLUSTER +@ INSERT INTO tag VALUES(7, 'newbranch'); -- TAG_NEWBRANCH +@ INSERT INTO tag VALUES(8, 'closed'); -- TAG_CLOSED @ @ -- Assignments of tags to baselines. Note that we allow tags to @ -- have values assigned to them. So we are not really dealing with @ -- tags here. These are really properties. But we are going to @ -- keep calling them tags because in many cases the value is ignored. @@ -331,13 +333,15 @@ # define TAG_COMMENT 2 /* The check-in comment */ # define TAG_USER 3 /* User who made a checking */ # define TAG_HIDDEN 4 /* Do not display or sync */ # define TAG_PRIVATE 5 /* Display but do not sync */ # define TAG_CLUSTER 6 /* A cluster */ +# define TAG_NEWBRANCH 7 /* First check-in of a new named branch */ +# define TAG_CLOSED 8 /* Do not display this check-in as a leaf */ #endif #if EXPORT_INTERFACE -# define MAX_INT_TAG 6 /* The largest pre-assigned tag id */ +# define MAX_INT_TAG 8 /* The largest pre-assigned tag id */ #endif /* ** The schema for the locate FOSSIL database file found at the root ** of very check-out. This database contains the complete state of
Modified src/tag.c from [56669f6f87] to [6980217297].
@@ -247,75 +247,19 @@ tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); db_end_transaction(0); } /* -** Prepare an artifact that describes a fork from a certain baseline. -** Furthermore a propagating symbolic tag will be inserted and -** all other propagating symbolic tags will be cancelled. -** -** The changes are appended at the Blob pCtrl. However the manifest -** is not complete at that stage. -*/ -static void tag_prepare_fork( - Blob *pCtrl, - const char *zTagname, - int rid, - int preflen /* Tag prefix length to adjust name if reqd */ -){ - Stmt q; - Manifest origin; - Blob originContent; - char *zDate; - int i; - - blob_appendf(pCtrl, "C Create\\snamed\\sfork\\s%s\n", zTagname+preflen); - content_get(rid, &originContent); - manifest_parse(&origin, &originContent); - zDate = db_text(0, "SELECT datetime('now')"); - zDate[10] = 'T'; - blob_appendf(pCtrl, "D %s\n", zDate); - for(i=0; i<origin.nFile; ++i){ - blob_appendf(pCtrl, "F %s %s %s\n", - origin.aFile[i].zName, - origin.aFile[i].zUuid, - origin.aFile[i].zPerm); - } - if( origin.nParent>0 ){ - blob_appendf(pCtrl, "P %s\n", origin.azParent[0]); - } - blob_appendf(pCtrl, "R %s\n", origin.zRepoCksum); - blob_appendf(pCtrl, "T *%F *", zTagname); - - /* Cancel any sym- tags that propagate */ - db_prepare(&q, - "SELECT tagname FROM tagxref, tag" - " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" - " AND tagtype>0 AND tagname LIKE 'sym-%%'" - " ORDER BY tagname", - rid); - while( db_step(&q)==SQLITE_ROW ){ - const char *zTag = db_column_text(&q, 0); - blob_appendf(pCtrl, "\nT -%s *", zTag); - } - db_finalize(&q); - - /* Cleanup */ - manifest_clear(&origin); -} - -/* ** Add a control record to the repository that either creates ** or cancels a tag. */ static void tag_add_artifact( + const char *zPrefix, /* Prefix to prepend to tag name */ const char *zTagname, /* The tag to add or cancel */ const char *zObjName, /* Name of object attached to */ const char *zValue, /* Value for the tag. Might be NULL */ - int tagtype, /* 0:cancel 1:singleton 2:propagated */ - int fork, /* Should a fork created from zObjName? */ - int preflen /* Tag prefix length to adjust name if reqd */ + int tagtype /* 0:cancel 1:singleton 2:propagated */ ){ int rid; int nrid; char *zDate; Blob uuid; @@ -331,26 +275,25 @@ return; } rid = name_to_rid(blob_str(&uuid)); blob_zero(&ctrl); +#if 0 if( validate16(zTagname, strlen(zTagname)) ){ fossil_fatal( "invalid tag name \"%s\" - might be confused with" " a hexadecimal artifact ID", zTagname ); } - if( fork ){ - tag_prepare_fork(&ctrl, zTagname, rid, preflen); - }else{ - zDate = db_text(0, "SELECT datetime('now')"); - zDate[10] = 'T'; - blob_appendf(&ctrl, "D %s\n", zDate); - blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid); - } - if( tagtype && zValue && zValue[0] ){ +#endif + zDate = db_text(0, "SELECT datetime('now')"); + zDate[10] = 'T'; + blob_appendf(&ctrl, "D %s\n", zDate); + blob_appendf(&ctrl, "T %c%s%F %b", + zTagtype[tagtype], zPrefix, zTagname, &uuid); + if( tagtype>0 && zValue && zValue[0] ){ blob_appendf(&ctrl, " %F\n", zValue); }else{ blob_appendf(&ctrl, "\n"); } blob_appendf(&ctrl, "U %F\n", g.zLogin); @@ -369,43 +312,30 @@ ** COMMAND: tag ** Usage: %fossil tag SUBCOMMAND ... ** ** Run various subcommands to control tags and properties ** -** %fossil tag add ?--raw? TAGNAME BASELINE ?VALUE? -** -** Add a new tag or property to BASELINE. The tag will -** be usable instead of a BASELINE in commands such as -** update and merge. -** -** %fossil tag branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE? -** -** A fork will be created so that the new checkin -** is a sibling of BASELINE and identical to it except -** for a generated comment. Then the new tag will -** be added to the new checkin and propagated to -** all direct children. Additionally all symbolic -** tags of that checkin inherited from BASELINE will -** be cancelled. -** -** However, if the option --nofork is given, no -** fork will be created and the tag/property will be -** added to BASELINE directly. No tags will be canceled. -** -** %fossil tag cancel ?--raw? TAGNAME BASELINE -** -** Remove the tag TAGNAME from BASELINE, and also remove +** %fossil tag add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE? +** +** Add a new tag or property to CHECK-IN. The tag will +** be usable instead of a CHECK-IN in commands such as +** update and merge. If the --propagate flag is present, +** the tag value propages to all descendants of CHECK-IN +** +** %fossil tag cancel ?--raw? TAGNAME CHECK-IN +** +** Remove the tag TAGNAME from CHECK-IN, and also remove ** the propagation of the tag to any descendants. ** ** %fossil tag find ?--raw? TAGNAME ** -** List all baselines that use TAGNAME +** List all check-ins that use TAGNAME ** -** %fossil tag list ?--raw? ?BASELINE? +** %fossil tag list ?--raw? ?CHECK-IN? ** -** List all tags, or if BASELINE is supplied, list -** all tags and their values for BASELINE. +** List all tags, or if CHECK-IN is supplied, list +** all tags and their values for CHECK-IN. ** ** The option --raw allows the manipulation of all types of tags ** used for various internal purposes in fossil. It also shows ** "cancel" tags for the "find" and "list" subcommands. You should ** not use this option to make changes unless you are sure what @@ -425,15 +355,13 @@ ** will assume that "decaf" is a tag/branch name. ** */ void tag_cmd(void){ int n; - int raw = find_option("raw","",0)!=0; - int fork = find_option("nofork","",0)==0; - const char *prefix = raw ? "" : "sym-"; - int preflen = strlen(prefix); - Blob tagname; + int fRaw = find_option("raw","",0)!=0; + int fPropagate = find_option("propagate","",0)!=0; + const char *zPrefix = fRaw ? "" : "sym-"; db_find_and_open_repository(1); if( g.argc<3 ){ goto tag_cmd_usage; } @@ -440,78 +368,83 @@ n = strlen(g.argv[2]); if( n==0 ){ goto tag_cmd_usage; } - blob_set(&tagname, prefix); - if( strncmp(g.argv[2],"add",n)==0 ){ char *zValue; if( g.argc!=5 && g.argc!=6 ){ - usage("add ?--raw? TAGNAME BASELINE ?VALUE?"); + usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?"); } - blob_append(&tagname, g.argv[3], strlen(g.argv[3])); zValue = g.argc==6 ? g.argv[5] : 0; - tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 1, 0, 0); + tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, 1+fPropagate); }else if( strncmp(g.argv[2],"branch",n)==0 ){ - char *zValue; - if( g.argc!=5 && g.argc!=6 ){ - usage("branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE?"); - } - blob_append(&tagname, g.argv[3], strlen(g.argv[3])); - zValue = g.argc==6 ? g.argv[5] : 0; - tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 2, fork!=0, - preflen); - if( fork ){ - const char *zUuid = db_text(0, "SELECT uuid, MAX(rowid) FROM blob"); - printf("New_Fork \"%s\": %s\n", g.argv[3], zUuid); - } + fossil_fatal("the \"fossil tag branch\" command is discontinued\n" + "Use the \"fossil branch new\" command instead."); }else if( strncmp(g.argv[2],"cancel",n)==0 ){ if( g.argc!=5 ){ - usage("cancel ?--raw? TAGNAME BASELINE"); + usage("cancel ?--raw? TAGNAME CHECK-IN"); } - blob_append(&tagname, g.argv[3], strlen(g.argv[3])); - tag_add_artifact(blob_str(&tagname), g.argv[4], 0, 0, 0, 0); + tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0); }else if( strncmp(g.argv[2],"find",n)==0 ){ Stmt q; if( g.argc!=4 ){ usage("find ?--raw? TAGNAME"); } - blob_append(&tagname, g.argv[3], strlen(g.argv[3])); - db_prepare(&q, - "SELECT blob.uuid FROM tagxref, blob" - " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)" - " AND tagxref.tagtype > %d" - " AND blob.rid=tagxref.rid", &tagname, raw ? -1 : 0 - ); - while( db_step(&q)==SQLITE_ROW ){ - printf("%s\n", db_column_text(&q, 0)); - } - db_finalize(&q); + if( fRaw ){ + db_prepare(&q, + "SELECT blob.uuid FROM tagxref, blob" + " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" + " AND tagxref.tagtype>0" + " AND blob.rid=tagxref.rid", + g.argv[3] + ); + while( db_step(&q)==SQLITE_ROW ){ + printf("%s\n", db_column_text(&q, 0)); + } + db_finalize(&q); + }else{ + int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", + g.argv[3]); + if( tagid>0 ){ + db_prepare(&q, + "%s" + " AND blob.rid IN (" + " SELECT rid FROM tagxref" + " WHERE tagtype>0 AND tagid=%d" + ")" + " ORDER BY event.mtime DESC", + timeline_query_for_tty(), tagid + ); + print_timeline(&q, 2000); + db_finalize(&q); + } + } }else if( strncmp(g.argv[2],"list",n)==0 ){ Stmt q; if( g.argc==3 ){ db_prepare(&q, "SELECT tagname FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" - " AND tagtype>%d)" - " ORDER BY tagname", - raw ? -1 : 0 + " AND tagtype>0)" + " ORDER BY tagname" ); while( db_step(&q)==SQLITE_ROW ){ - const char *name = db_column_text(&q, 0); - if( raw || strncmp(name, prefix, preflen)==0 ){ - printf("%s\n", name+preflen); + const char *zName = db_column_text(&q, 0); + if( fRaw ){ + printf("%s\n", zName); + }else if( strncmp(zName, "sym-", 4)==0 ){ + printf("%s\n", &zName[4]); } } db_finalize(&q); }else if( g.argc==4 ){ int rid = name_to_rid(g.argv[3]); @@ -519,36 +452,35 @@ "SELECT tagname, value FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype>%d" " ORDER BY tagname", rid, - raw ? -1 : 0 + fRaw ? -1 : 0 ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zValue = db_column_text(&q, 1); - if( zValue ){ - if( raw || strncmp(zName, prefix, preflen)==0 ){ - printf("%s=%s\n", zName+preflen, zValue); - } + if( fRaw==0 ){ + if( strncmp(zName, "sym-", 4)!=0 ) continue; + zName += 4; + } + if( zValue && zValue[0] ){ + printf("%s=%s\n", zName, zValue); }else{ - if( raw || strncmp(zName, prefix, preflen)==0 ){ - printf("%s\n", zName+preflen); - } + printf("%s\n", zName); } } db_finalize(&q); }else{ - usage("tag list ?BASELINE?"); + usage("tag list ?CHECK-IN?"); } }else { goto tag_cmd_usage; } /* Cleanup */ - blob_reset(&tagname); return; tag_cmd_usage: - usage("add|branch|cancel|find|list ..."); + usage("add|cancel|find|list ..."); }
Modified src/timeline.c from [fe363fe990] to [79dd0c4340].
@@ -75,10 +75,30 @@ } } } /* +** Count the number of non-branch children for the given check-in. +** A non-branch child is a child that omits the "newbranch" tag. +*/ +int count_nonbranch_children(int pid){ + int nNonBranch; + + nNonBranch = db_int(0, + "SELECT count(*) FROM plink" + " WHERE pid=%d" + " AND NOT EXISTS(SELECT 1 FROM tagxref" + " WHERE tagid=%d" + " AND rid=cid" + " AND tagtype>0" + " )", + pid, TAG_NEWBRANCH + ); + return nNonBranch; +} + +/* ** Output a timeline in the web format given a query. The query ** should return these columns: ** ** 0. rid ** 1. UUID @@ -145,11 +165,15 @@ hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid); if( nParent>1 ){ @ <b>Merge</b> } if( nPChild>1 ){ - @ <b>Fork</b> + if( count_nonbranch_children(rid)>1 ){ + @ <b>Fork</b> + }else{ + @ <b>Branch</b> + } } if( isLeaf ){ @ <b>Leaf</b> } }else{ @@ -547,10 +571,11 @@ int rid = db_lget_int("checkout", 0); zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){ + int rid = db_column_int(q, 0); const char *zId = db_column_text(q, 1); const char *zDate = db_column_text(q, 2); const char *zCom = db_column_text(q, 3); int nChild = db_column_int(q, 4); int nParent = db_column_int(q, 5); @@ -571,11 +596,17 @@ if( nParent>1 ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* "); n = strlen(zPrefix); } if( nChild>1 ){ - sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* "); + const char *zBrType; + if( count_nonbranch_children(rid)>1 ){ + zBrType = "*FORK* "; + }else{ + zBrType = "*BRANCH* "; + } + sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType); n = strlen(zPrefix); } if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* "); n += strlen(zPrefix);
Modified src/update.c from [6096612034] to [e7d09d9291].
@@ -90,11 +90,11 @@ */ autosync(AUTOSYNC_PULL); } if( tid==0 ){ - compute_leaves(vid); + compute_leaves(vid, 1); if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ db_prepare(&q, "%s " " AND event.objid IN leaves" " ORDER BY event.mtime DESC",