b8a8959ec5 2007-09-23 jnc: /* b8a8959ec5 2007-09-23 jnc: ** Copyright (c) 2007 D. Richard Hipp b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** This program is free software; you can redistribute it and/or b8a8959ec5 2007-09-23 jnc: ** modify it under the terms of the GNU General Public b8a8959ec5 2007-09-23 jnc: ** License version 2 as published by the Free Software Foundation. b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** This program is distributed in the hope that it will be useful, b8a8959ec5 2007-09-23 jnc: ** but WITHOUT ANY WARRANTY; without even the implied warranty of b8a8959ec5 2007-09-23 jnc: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU b8a8959ec5 2007-09-23 jnc: ** General Public License for more details. b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** You should have received a copy of the GNU General Public b8a8959ec5 2007-09-23 jnc: ** License along with this library; if not, write to the b8a8959ec5 2007-09-23 jnc: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, b8a8959ec5 2007-09-23 jnc: ** Boston, MA 02111-1307, USA. b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** Author contact information: b8a8959ec5 2007-09-23 jnc: ** drh@hwaci.com b8a8959ec5 2007-09-23 jnc: ** http://www.hwaci.com/drh/ b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ******************************************************************************* b8a8959ec5 2007-09-23 jnc: ** 07eaead5dc 2007-09-23 jnc: ** This file contains code used to create new branches within a repository. b8a8959ec5 2007-09-23 jnc: */ b8a8959ec5 2007-09-23 jnc: #include "config.h" b8a8959ec5 2007-09-23 jnc: #include "branch.h" b8a8959ec5 2007-09-23 jnc: #include <assert.h> b8a8959ec5 2007-09-23 jnc: b6e22e62cf 2009-01-20 drh: /* b6e22e62cf 2009-01-20 drh: ** fossil branch new BRANCH-NAME ?ORIGIN-CHECK-IN? ?-bgcolor COLOR? b6e22e62cf 2009-01-20 drh: ** argv0 argv1 argv2 argv3 argv4 b6e22e62cf 2009-01-20 drh: */ b8a8959ec5 2007-09-23 jnc: void branch_new(void){ b6e22e62cf 2009-01-20 drh: int rootid; /* RID of the root check-in - what we branch off of */ b6e22e62cf 2009-01-20 drh: int brid; /* RID of the branch check-in */ b6e22e62cf 2009-01-20 drh: int noSign; /* True if the branch is unsigned */ b6e22e62cf 2009-01-20 drh: int i; /* Loop counter */ b6e22e62cf 2009-01-20 drh: char *zUuid; /* Artifact ID of origin */ b6e22e62cf 2009-01-20 drh: Stmt q; /* Generic query */ b6e22e62cf 2009-01-20 drh: const char *zBranch; /* Name of the new branch */ b6e22e62cf 2009-01-20 drh: char *zDate; /* Date that branch was created */ b6e22e62cf 2009-01-20 drh: char *zComment; /* Check-in comment for the new branch */ b6e22e62cf 2009-01-20 drh: const char *zColor; /* Color of the new branch */ b6e22e62cf 2009-01-20 drh: Blob branch; /* manifest for the new branch */ b6e22e62cf 2009-01-20 drh: Blob parent; /* root check-in manifest */ b6e22e62cf 2009-01-20 drh: Manifest mParent; /* Parsed parent manifest */ b6e22e62cf 2009-01-20 drh: Blob mcksum; /* Self-checksum on the manifest */ b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: noSign = find_option("nosign","",0)!=0; b8a8959ec5 2007-09-23 jnc: zColor = find_option("bgcolor","c",1); b8a8959ec5 2007-09-23 jnc: verify_all_options(); 4d39bbac10 2009-01-20 drh: if( g.argc<5 ){ 4d39bbac10 2009-01-20 drh: usage("branch new BRANCH-NAME BASE-CHECK-IN ?-bgcolor COLOR?"); b6e22e62cf 2009-01-20 drh: } b6e22e62cf 2009-01-20 drh: db_find_and_open_repository(1); b6e22e62cf 2009-01-20 drh: noSign = db_get_int("omitsign", 0)|noSign; b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: /* fossil branch new name */ b8a8959ec5 2007-09-23 jnc: zBranch = g.argv[3]; 2ad378d065 2007-09-23 jnc: if( zBranch==0 || zBranch[0]==0 ){ 2ad378d065 2007-09-23 jnc: fossil_panic("branch name cannot be empty"); 2ad378d065 2007-09-23 jnc: } b6e22e62cf 2009-01-20 drh: if( db_exists( b6e22e62cf 2009-01-20 drh: "SELECT 1 FROM tagxref" b6e22e62cf 2009-01-20 drh: " WHERE tagtype>0" b6e22e62cf 2009-01-20 drh: " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')", b6e22e62cf 2009-01-20 drh: zBranch)!=0 ){ b6e22e62cf 2009-01-20 drh: fossil_fatal("branch \"%s\" already exists", zBranch); b6e22e62cf 2009-01-20 drh: } b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: user_select(); b8a8959ec5 2007-09-23 jnc: db_begin_transaction(); 4d39bbac10 2009-01-20 drh: rootid = name_to_rid(g.argv[4]); b6e22e62cf 2009-01-20 drh: if( rootid==0 ){ b6e22e62cf 2009-01-20 drh: fossil_fatal("unable to locate check-in off of which to branch"); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: b6e22e62cf 2009-01-20 drh: /* Create a manifest for the new branch */ b6e22e62cf 2009-01-20 drh: blob_zero(&branch); b6e22e62cf 2009-01-20 drh: zComment = mprintf("Create new branch named \"%h\"", zBranch); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "C %F\n", zComment); b8a8959ec5 2007-09-23 jnc: zDate = db_text(0, "SELECT datetime('now')"); b8a8959ec5 2007-09-23 jnc: zDate[10] = 'T'; b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "D %s\n", zDate); b6e22e62cf 2009-01-20 drh: b6e22e62cf 2009-01-20 drh: /* Copy all of the content from the parent into the branch */ b6e22e62cf 2009-01-20 drh: content_get(rootid, &parent); b6e22e62cf 2009-01-20 drh: manifest_parse(&mParent, &parent); b6e22e62cf 2009-01-20 drh: if( mParent.type!=CFTYPE_MANIFEST ){ b6e22e62cf 2009-01-20 drh: fossil_fatal("%s is not a valid check-in", g.argv[4]); 2ad378d065 2007-09-23 jnc: } b6e22e62cf 2009-01-20 drh: for(i=0; i<mParent.nFile; ++i){ b6e22e62cf 2009-01-20 drh: if( mParent.aFile[i].zPerm[0] ){ b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "F %F %s %s\n", b6e22e62cf 2009-01-20 drh: mParent.aFile[i].zName, b6e22e62cf 2009-01-20 drh: mParent.aFile[i].zUuid, b6e22e62cf 2009-01-20 drh: mParent.aFile[i].zPerm); 2ad378d065 2007-09-23 jnc: }else{ b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "F %F %s\n", b6e22e62cf 2009-01-20 drh: mParent.aFile[i].zName, b6e22e62cf 2009-01-20 drh: mParent.aFile[i].zUuid); 2ad378d065 2007-09-23 jnc: } ce08928aaa 2008-02-08 drh: } b6e22e62cf 2009-01-20 drh: zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "P %s\n", zUuid); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "R %s\n", mParent.zRepoCksum); b6e22e62cf 2009-01-20 drh: manifest_clear(&mParent); b6e22e62cf 2009-01-20 drh: b6e22e62cf 2009-01-20 drh: /* Add the symbolic branch name and the "newbranch" tag to identify b6e22e62cf 2009-01-20 drh: ** this as a new branch */ ce08928aaa 2008-02-08 drh: if( zColor!=0 ){ b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "T *bgcolor * %F\n", zColor); 2ad378d065 2007-09-23 jnc: } b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "T *sym-%F *\n", zBranch); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "T +newbranch *\n"); b6e22e62cf 2009-01-20 drh: b6e22e62cf 2009-01-20 drh: /* Cancel all other symbolic tags */ b8a8959ec5 2007-09-23 jnc: db_prepare(&q, b6e22e62cf 2009-01-20 drh: "SELECT tagname FROM tagxref, tag" b6e22e62cf 2009-01-20 drh: " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" b6e22e62cf 2009-01-20 drh: " AND tagtype>0 AND tagname GLOB 'sym-*'" b6e22e62cf 2009-01-20 drh: " ORDER BY tagname", b6e22e62cf 2009-01-20 drh: rootid); b8a8959ec5 2007-09-23 jnc: while( db_step(&q)==SQLITE_ROW ){ b6e22e62cf 2009-01-20 drh: const char *zTag = db_column_text(&q, 0); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "T -%s *\n", zTag); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: db_finalize(&q); b8a8959ec5 2007-09-23 jnc: b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "U %F\n", g.zLogin); b6e22e62cf 2009-01-20 drh: md5sum_blob(&branch, &mcksum); b6e22e62cf 2009-01-20 drh: blob_appendf(&branch, "Z %b\n", &mcksum); b6e22e62cf 2009-01-20 drh: if( !noSign && clearsign(&branch, &branch) ){ b8a8959ec5 2007-09-23 jnc: Blob ans; b8a8959ec5 2007-09-23 jnc: blob_zero(&ans); b8a8959ec5 2007-09-23 jnc: prompt_user("unable to sign manifest. continue [y/N]? ", &ans); b8a8959ec5 2007-09-23 jnc: if( blob_str(&ans)[0]!='y' ){ b8a8959ec5 2007-09-23 jnc: db_end_transaction(1); b8a8959ec5 2007-09-23 jnc: exit(1); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: b6e22e62cf 2009-01-20 drh: brid = content_put(&branch, 0, 0); b6e22e62cf 2009-01-20 drh: if( brid==0 ){ b8a8959ec5 2007-09-23 jnc: fossil_panic("trouble committing manifest: %s", g.zErrMsg); b8a8959ec5 2007-09-23 jnc: } b6e22e62cf 2009-01-20 drh: db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); b6e22e62cf 2009-01-20 drh: if( manifest_crosslink(brid, &branch)==0 ){ b6e22e62cf 2009-01-20 drh: fossil_panic("unable to install new manifest"); b6e22e62cf 2009-01-20 drh: } b6e22e62cf 2009-01-20 drh: content_deltify(rootid, brid, 0); b6e22e62cf 2009-01-20 drh: zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); b6e22e62cf 2009-01-20 drh: printf("New branch: %s\n", zUuid); b6e22e62cf 2009-01-20 drh: if( g.argc==3 ){ b6e22e62cf 2009-01-20 drh: printf( b6e22e62cf 2009-01-20 drh: "\n" b6e22e62cf 2009-01-20 drh: "Note: the local check-out has not been updated to the new\n" b6e22e62cf 2009-01-20 drh: " branch. To begin working on the new branch, do this:\n" b6e22e62cf 2009-01-20 drh: "\n" b6e22e62cf 2009-01-20 drh: " %s update %s\n", b6e22e62cf 2009-01-20 drh: g.argv[0], zBranch b6e22e62cf 2009-01-20 drh: ); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: /* Commit */ b8a8959ec5 2007-09-23 jnc: db_end_transaction(0); e4517465f3 2007-09-25 jnc: fff234b77c 2007-09-25 drh: /* Do an autosync push, if requested */ 49b59bc559 2008-02-09 drh: autosync(AUTOSYNC_PUSH); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: b8a8959ec5 2007-09-23 jnc: /* b8a8959ec5 2007-09-23 jnc: ** COMMAND: branch b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE? b8a8959ec5 2007-09-23 jnc: ** 66b13f1015 2008-02-07 bch: ** Run various subcommands on the branches of the open repository or b8a8959ec5 2007-09-23 jnc: ** of the repository identified by the -R or --repository option. b8a8959ec5 2007-09-23 jnc: ** 4d39bbac10 2009-01-20 drh: ** %fossil branch new BRANCH-NAME BASIS ?-bgcolor COLOR? b8a8959ec5 2007-09-23 jnc: ** 4d39bbac10 2009-01-20 drh: ** Create a new branch BRANCH-NAME off of check-in BASIS. 4d39bbac10 2009-01-20 drh: ** You can optionally give the branch a default color. b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** %fossil branch list b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: ** List all branches b8a8959ec5 2007-09-23 jnc: ** b8a8959ec5 2007-09-23 jnc: */ b8a8959ec5 2007-09-23 jnc: void branch_cmd(void){ b8a8959ec5 2007-09-23 jnc: int n; 4e683ef07b 2008-05-05 drh: db_find_and_open_repository(1); b8a8959ec5 2007-09-23 jnc: if( g.argc<3 ){ b8a8959ec5 2007-09-23 jnc: usage("new|list ..."); b8a8959ec5 2007-09-23 jnc: } b8a8959ec5 2007-09-23 jnc: n = strlen(g.argv[2]); b8a8959ec5 2007-09-23 jnc: if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){ b8a8959ec5 2007-09-23 jnc: branch_new(); b8a8959ec5 2007-09-23 jnc: }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){ b7f32a71ab 2009-01-20 drh: Stmt q; b7f32a71ab 2009-01-20 drh: db_prepare(&q, b7f32a71ab 2009-01-20 drh: "%s" b7f32a71ab 2009-01-20 drh: " AND blob.rid IN (SELECT rid FROM tagxref" b7f32a71ab 2009-01-20 drh: " WHERE tagid=%d AND tagtype==1)" b7f32a71ab 2009-01-20 drh: " ORDER BY event.mtime DESC", b7f32a71ab 2009-01-20 drh: timeline_query_for_tty(), TAG_NEWBRANCH b7f32a71ab 2009-01-20 drh: ); b7f32a71ab 2009-01-20 drh: print_timeline(&q, 2000); b7f32a71ab 2009-01-20 drh: db_finalize(&q); b8a8959ec5 2007-09-23 jnc: }else{ b8a8959ec5 2007-09-23 jnc: fossil_panic("branch subcommand should be one of: " b8a8959ec5 2007-09-23 jnc: "new list"); b8a8959ec5 2007-09-23 jnc: } fecb3e5cc9 2009-01-20 drh: } fecb3e5cc9 2009-01-20 drh: fecb3e5cc9 2009-01-20 drh: /* bdcac62937 2009-01-21 drh: ** This routine is called while for each check-in that is rendered by bdcac62937 2009-01-21 drh: ** the timeline of a "brlist" page. Add some additional hyperlinks bdcac62937 2009-01-21 drh: ** to the end of the line. bdcac62937 2009-01-21 drh: */ bdcac62937 2009-01-21 drh: static void brlist_extra(int rid){ bdcac62937 2009-01-21 drh: Stmt q; bdcac62937 2009-01-21 drh: db_prepare(&q, e631d8af6d 2009-01-21 drh: "SELECT substr(tagname,5) FROM tagxref, tag" bdcac62937 2009-01-21 drh: " WHERE tagxref.rid=%d" bdcac62937 2009-01-21 drh: " AND tagxref.tagid=tag.tagid" bdcac62937 2009-01-21 drh: " AND tagxref.tagtype>0" bdcac62937 2009-01-21 drh: " AND tag.tagname GLOB 'sym-*'", bdcac62937 2009-01-21 drh: rid bdcac62937 2009-01-21 drh: ); bdcac62937 2009-01-21 drh: while( db_step(&q)==SQLITE_ROW ){ bdcac62937 2009-01-21 drh: const char *zTagName = db_column_text(&q, 0); 2fa4df1e47 2009-01-21 drh: @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[timeline]</a> bdcac62937 2009-01-21 drh: } bdcac62937 2009-01-21 drh: db_finalize(&q); bdcac62937 2009-01-21 drh: } bdcac62937 2009-01-21 drh: bdcac62937 2009-01-21 drh: /* fecb3e5cc9 2009-01-20 drh: ** WEBPAGE: brlist fecb3e5cc9 2009-01-20 drh: ** fecb3e5cc9 2009-01-20 drh: ** Show a timeline of all branches fecb3e5cc9 2009-01-20 drh: */ fecb3e5cc9 2009-01-20 drh: void brlist_page(void){ fecb3e5cc9 2009-01-20 drh: Stmt q; fecb3e5cc9 2009-01-20 drh: fecb3e5cc9 2009-01-20 drh: login_check_credentials(); fecb3e5cc9 2009-01-20 drh: if( !g.okRead ){ login_needed(); return; } fecb3e5cc9 2009-01-20 drh: fecb3e5cc9 2009-01-20 drh: style_header("Branches"); fecb3e5cc9 2009-01-20 drh: login_anonymous_available(); bdcac62937 2009-01-21 drh: @ <h2>The initial check-in for each branch:</h2> fecb3e5cc9 2009-01-20 drh: db_prepare(&q, fecb3e5cc9 2009-01-20 drh: "%s AND blob.rid IN (SELECT rid FROM tagxref WHERE tagtype>0 AND tagid=%d)" fecb3e5cc9 2009-01-20 drh: " ORDER BY event.mtime DESC", fecb3e5cc9 2009-01-20 drh: timeline_query_for_www(), TAG_NEWBRANCH fecb3e5cc9 2009-01-20 drh: ); bdcac62937 2009-01-21 drh: www_print_timeline(&q, 0, brlist_extra); 08db9e11cb 2009-01-21 drh: db_finalize(&q); 08db9e11cb 2009-01-21 drh: @ <br clear="both"> 08db9e11cb 2009-01-21 drh: @ <script> 08db9e11cb 2009-01-21 drh: @ function xin(id){ 08db9e11cb 2009-01-21 drh: @ } 08db9e11cb 2009-01-21 drh: @ function xout(id){ 08db9e11cb 2009-01-21 drh: @ } 08db9e11cb 2009-01-21 drh: @ </script> 08db9e11cb 2009-01-21 drh: style_footer(); 08db9e11cb 2009-01-21 drh: } 08db9e11cb 2009-01-21 drh: 08db9e11cb 2009-01-21 drh: /* 08db9e11cb 2009-01-21 drh: ** WEBPAGE: symtaglist 08db9e11cb 2009-01-21 drh: ** 08db9e11cb 2009-01-21 drh: ** Show a timeline of all check-ins that have a primary symbolic tag. 08db9e11cb 2009-01-21 drh: */ 08db9e11cb 2009-01-21 drh: void symtaglist_page(void){ 08db9e11cb 2009-01-21 drh: Stmt q; 08db9e11cb 2009-01-21 drh: 08db9e11cb 2009-01-21 drh: login_check_credentials(); 08db9e11cb 2009-01-21 drh: if( !g.okRead ){ login_needed(); return; } 08db9e11cb 2009-01-21 drh: 08db9e11cb 2009-01-21 drh: style_header("Tagged Check-ins"); 08db9e11cb 2009-01-21 drh: login_anonymous_available(); 08db9e11cb 2009-01-21 drh: @ <h2>Check-ins that have one or more primary symbolic tags</h2> 08db9e11cb 2009-01-21 drh: db_prepare(&q, 08db9e11cb 2009-01-21 drh: "%s AND blob.rid IN (SELECT rid FROM tagxref" 08db9e11cb 2009-01-21 drh: " WHERE tagtype>1 AND srcid>0" 08db9e11cb 2009-01-21 drh: " AND tagid IN (SELECT tagid FROM tag " 08db9e11cb 2009-01-21 drh: " WHERE tagname GLOB 'sym-*'))" 580d6ad8c7 2009-01-21 drh: " ORDER BY event.mtime DESC", 580d6ad8c7 2009-01-21 drh: timeline_query_for_www(), TAG_NEWBRANCH 580d6ad8c7 2009-01-21 drh: ); 580d6ad8c7 2009-01-21 drh: www_print_timeline(&q, 0, 0); fecb3e5cc9 2009-01-20 drh: db_finalize(&q); fecb3e5cc9 2009-01-20 drh: @ <br clear="both"> fecb3e5cc9 2009-01-20 drh: @ <script> fecb3e5cc9 2009-01-20 drh: @ function xin(id){ fecb3e5cc9 2009-01-20 drh: @ } fecb3e5cc9 2009-01-20 drh: @ function xout(id){ fecb3e5cc9 2009-01-20 drh: @ } fecb3e5cc9 2009-01-20 drh: @ </script> fecb3e5cc9 2009-01-20 drh: style_footer(); b8a8959ec5 2007-09-23 jnc: }