Fossil SCM

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.

drh 2009-01-20 16:51 trunk
Commit b6e22e62cf26115617822c7940cec5d0dc687184
+119 -102
--- src/branch.c
+++ src/branch.c
@@ -25,153 +25,170 @@
2525
*/
2626
#include "config.h"
2727
#include "branch.h"
2828
#include <assert.h>
2929
30
+/*
31
+** fossil branch new BRANCH-NAME ?ORIGIN-CHECK-IN? ?-bgcolor COLOR?
32
+** argv0 argv1 argv2 argv3 argv4
33
+*/
3034
void branch_new(void){
31
- int vid, nvid, noSign;
32
- Stmt q;
33
- char *zBranch, *zUuid, *zDate, *zComment;
34
- const char *zColor;
35
- Blob manifest;
35
+ int rootid; /* RID of the root check-in - what we branch off of */
36
+ int brid; /* RID of the branch check-in */
37
+ int noSign; /* True if the branch is unsigned */
38
+ int i; /* Loop counter */
39
+ char *zUuid; /* Artifact ID of origin */
40
+ Stmt q; /* Generic query */
41
+ const char *zBranch; /* Name of the new branch */
42
+ char *zDate; /* Date that branch was created */
43
+ char *zComment; /* Check-in comment for the new branch */
44
+ const char *zColor; /* Color of the new branch */
45
+ Blob branch; /* manifest for the new branch */
46
+ Blob parent; /* root check-in manifest */
47
+ Manifest mParent; /* Parsed parent manifest */
3648
Blob mcksum; /* Self-checksum on the manifest */
37
- Blob cksum1, cksum2; /* Before and after commit checksums */
38
- Blob cksum1b; /* Checksum recorded in the manifest */
3949
4050
noSign = find_option("nosign","",0)!=0;
41
- db_must_be_within_tree();
42
- noSign = db_get_int("omitsign", 0)|noSign;
4351
zColor = find_option("bgcolor","c",1);
44
-
4552
verify_all_options();
53
+ if( g.argc<3 ){
54
+ usage("branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR?");
55
+ }
56
+ db_find_and_open_repository(1);
57
+ noSign = db_get_int("omitsign", 0)|noSign;
4658
4759
/* fossil branch new name */
48
- if( g.argc<3 ){
49
- usage("branch new ?-bgcolor COLOR? BRANCH-NAME");
50
- }
5160
zBranch = g.argv[3];
5261
if( zBranch==0 || zBranch[0]==0 ){
5362
fossil_panic("branch name cannot be empty");
5463
}
64
+ if( db_exists(
65
+ "SELECT 1 FROM tagxref"
66
+ " WHERE tagtype>0"
67
+ " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')",
68
+ zBranch)!=0 ){
69
+ fossil_fatal("branch \"%s\" already exists", zBranch);
70
+ }
5571
5672
user_select();
5773
db_begin_transaction();
58
- if( unsaved_changes() ){
59
- fossil_panic("there are uncommitted changes. please commit first");
60
- }
61
-
62
- vid = db_lget_int("checkout", 0);
63
- vfile_aggregate_checksum_disk(vid, &cksum1);
64
-
65
- /* Create our new manifest */
66
- blob_zero(&manifest);
67
- zComment = mprintf("Branch created %s", zBranch);
68
- blob_appendf(&manifest, "C %F\n", zComment);
69
- zDate = db_text(0, "SELECT datetime('now')");
70
- zDate[10] = 'T';
71
- blob_appendf(&manifest, "D %s\n", zDate);
72
-
73
- db_prepare(&q,
74
- "SELECT pathname, uuid FROM vfile JOIN blob ON vfile.mrid=blob.rid"
75
- " WHERE NOT deleted AND vfile.vid=%d"
76
- " ORDER BY 1", vid);
77
- while( db_step(&q)==SQLITE_ROW ){
78
- const char *zName = db_column_text(&q, 0);
79
- const char *zUuid = db_column_text(&q, 1);
80
- blob_appendf(&manifest, "F %F %s\n", zName, zUuid);
81
- }
82
- db_finalize(&q);
83
-
84
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
85
- blob_appendf(&manifest, "P %s\n", zUuid);
86
- blob_appendf(&manifest, "R %b\n", &cksum1);
87
-
88
- if( zColor!=0 ){
89
- blob_appendf(&manifest, "T *bgcolor * %F\n", zColor);
90
- blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
91
- }else{
92
- blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
93
- }
94
-
95
- /* Cancel any tags that propagate */
96
- db_prepare(&q,
97
- "SELECT tagname"
98
- " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
99
- " WHERE rid=%d AND tagtype=2", vid);
100
- while( db_step(&q)==SQLITE_ROW ){
101
- const char *zTagname = db_column_text(&q, 0);
102
- blob_appendf(&manifest, "T -%s *\n", zTagname);
103
- }
104
- db_finalize(&q);
105
-
106
- blob_appendf(&manifest, "U %F\n", g.zLogin);
107
- md5sum_blob(&manifest, &mcksum);
108
- blob_appendf(&manifest, "Z %b\n", &mcksum);
109
- if( !noSign && clearsign(&manifest, &manifest) ){
74
+ if( g.argc<5 ){
75
+ if( unsaved_changes() ){
76
+ fossil_fatal("there are uncommitted changes. please commit first");
77
+ }
78
+ rootid = db_lget_int("checkout", 0);
79
+ }else{
80
+ rootid = name_to_rid(g.argv[4]);
81
+ }
82
+ if( rootid==0 ){
83
+ fossil_fatal("unable to locate check-in off of which to branch");
84
+ }
85
+
86
+ /* Create a manifest for the new branch */
87
+ blob_zero(&branch);
88
+ zComment = mprintf("Create new branch named \"%h\"", zBranch);
89
+ blob_appendf(&branch, "C %F\n", zComment);
90
+ zDate = db_text(0, "SELECT datetime('now')");
91
+ zDate[10] = 'T';
92
+ blob_appendf(&branch, "D %s\n", zDate);
93
+
94
+ /* Copy all of the content from the parent into the branch */
95
+ content_get(rootid, &parent);
96
+ manifest_parse(&mParent, &parent);
97
+ if( mParent.type!=CFTYPE_MANIFEST ){
98
+ fossil_fatal("%s is not a valid check-in", g.argv[4]);
99
+ }
100
+ for(i=0; i<mParent.nFile; ++i){
101
+ if( mParent.aFile[i].zPerm[0] ){
102
+ blob_appendf(&branch, "F %F %s %s\n",
103
+ mParent.aFile[i].zName,
104
+ mParent.aFile[i].zUuid,
105
+ mParent.aFile[i].zPerm);
106
+ }else{
107
+ blob_appendf(&branch, "F %F %s\n",
108
+ mParent.aFile[i].zName,
109
+ mParent.aFile[i].zUuid);
110
+ }
111
+ }
112
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
113
+ blob_appendf(&branch, "P %s\n", zUuid);
114
+ blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
115
+ manifest_clear(&mParent);
116
+
117
+ /* Add the symbolic branch name and the "newbranch" tag to identify
118
+ ** this as a new branch */
119
+ if( zColor!=0 ){
120
+ blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
121
+ }
122
+ blob_appendf(&branch, "T *sym-%F *\n", zBranch);
123
+ blob_appendf(&branch, "T +newbranch *\n");
124
+
125
+ /* Cancel all other symbolic tags */
126
+ db_prepare(&q,
127
+ "SELECT tagname FROM tagxref, tag"
128
+ " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
129
+ " AND tagtype>0 AND tagname GLOB 'sym-*'"
130
+ " ORDER BY tagname",
131
+ rootid);
132
+ while( db_step(&q)==SQLITE_ROW ){
133
+ const char *zTag = db_column_text(&q, 0);
134
+ blob_appendf(&branch, "T -%s *\n", zTag);
135
+ }
136
+ db_finalize(&q);
137
+
138
+ blob_appendf(&branch, "U %F\n", g.zLogin);
139
+ md5sum_blob(&branch, &mcksum);
140
+ blob_appendf(&branch, "Z %b\n", &mcksum);
141
+ if( !noSign && clearsign(&branch, &branch) ){
110142
Blob ans;
111143
blob_zero(&ans);
112144
prompt_user("unable to sign manifest. continue [y/N]? ", &ans);
113145
if( blob_str(&ans)[0]!='y' ){
114146
db_end_transaction(1);
115147
exit(1);
116148
}
117149
}
118
-
119
- /*blob_write_to_file(&manifest, "manifest.new");*/
120150
121
- nvid = content_put(&manifest, 0, 0);
122
- if( nvid==0 ){
151
+ brid = content_put(&branch, 0, 0);
152
+ if( brid==0 ){
123153
fossil_panic("trouble committing manifest: %s", g.zErrMsg);
124154
}
125
- db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
126
- manifest_crosslink(nvid, &manifest);
127
- content_deltify(vid, nvid, 0);
128
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
129
- printf("Branch Version: %s\n", zUuid);
130
- printf("\n");
131
- printf("Notice: working copy not updated to the new branch. If\n");
132
- printf(" you wish to work on the new branch, update to\n");
133
- printf(" that branch first:\n");
134
- printf("\n");
135
- printf(" fossil update %s\n", zBranch);
136
-
137
- /* Verify that the manifest checksum matches the expected checksum */
138
- vfile_aggregate_checksum_repository(nvid, &cksum2);
139
- vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
140
- if( blob_compare(&cksum1, &cksum1b) ){
141
- fossil_panic("manifest checksum does not agree with manifest: "
142
- "%b versus %b", &cksum1, &cksum1b);
143
- }
144
-
145
- /* Verify that the commit did not modify any disk images. */
146
- vfile_aggregate_checksum_disk(vid, &cksum2);
147
- if( blob_compare(&cksum1, &cksum2) ){
148
- fossil_panic("tree checksums before and after commit do not match");
149
- }
150
-
151
- /* Clear the undo/redo stack */
152
- undo_reset();
155
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
156
+ if( manifest_crosslink(brid, &branch)==0 ){
157
+ fossil_panic("unable to install new manifest");
158
+ }
159
+ content_deltify(rootid, brid, 0);
160
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
161
+ printf("New branch: %s\n", zUuid);
162
+ if( g.argc==3 ){
163
+ printf(
164
+ "\n"
165
+ "Note: the local check-out has not been updated to the new\n"
166
+ " branch. To begin working on the new branch, do this:\n"
167
+ "\n"
168
+ " %s update %s\n",
169
+ g.argv[0], zBranch
170
+ );
171
+ }
172
+
153173
154174
/* Commit */
155175
db_end_transaction(0);
156176
157177
/* Do an autosync push, if requested */
158178
autosync(AUTOSYNC_PUSH);
159179
}
160180
161181
/*
162
-** NB: The "branch" command is disabled pending further discussion of its
163
-** purpose and usefulness....
164
-**
165
-** COM MAND: branch
182
+** COMMAND: branch
166183
**
167184
** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE?
168185
**
169186
** Run various subcommands on the branches of the open repository or
170187
** of the repository identified by the -R or --repository option.
171188
**
172
-** %fossil branch new ?-bgcolor COLOR? BRANCH-NAME
189
+** %fossil branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR?
173190
**
174191
** Create a new branch BRANCH-NAME. You can optionally give
175192
** a commit message and branch color.
176193
**
177194
** %fossil branch list
178195
--- src/branch.c
+++ src/branch.c
@@ -25,153 +25,170 @@
25 */
26 #include "config.h"
27 #include "branch.h"
28 #include <assert.h>
29
 
 
 
 
30 void branch_new(void){
31 int vid, nvid, noSign;
32 Stmt q;
33 char *zBranch, *zUuid, *zDate, *zComment;
34 const char *zColor;
35 Blob manifest;
 
 
 
 
 
 
 
 
36 Blob mcksum; /* Self-checksum on the manifest */
37 Blob cksum1, cksum2; /* Before and after commit checksums */
38 Blob cksum1b; /* Checksum recorded in the manifest */
39
40 noSign = find_option("nosign","",0)!=0;
41 db_must_be_within_tree();
42 noSign = db_get_int("omitsign", 0)|noSign;
43 zColor = find_option("bgcolor","c",1);
44
45 verify_all_options();
 
 
 
 
 
46
47 /* fossil branch new name */
48 if( g.argc<3 ){
49 usage("branch new ?-bgcolor COLOR? BRANCH-NAME");
50 }
51 zBranch = g.argv[3];
52 if( zBranch==0 || zBranch[0]==0 ){
53 fossil_panic("branch name cannot be empty");
54 }
 
 
 
 
 
 
 
55
56 user_select();
57 db_begin_transaction();
58 if( unsaved_changes() ){
59 fossil_panic("there are uncommitted changes. please commit first");
60 }
61
62 vid = db_lget_int("checkout", 0);
63 vfile_aggregate_checksum_disk(vid, &cksum1);
64
65 /* Create our new manifest */
66 blob_zero(&manifest);
67 zComment = mprintf("Branch created %s", zBranch);
68 blob_appendf(&manifest, "C %F\n", zComment);
69 zDate = db_text(0, "SELECT datetime('now')");
70 zDate[10] = 'T';
71 blob_appendf(&manifest, "D %s\n", zDate);
72
73 db_prepare(&q,
74 "SELECT pathname, uuid FROM vfile JOIN blob ON vfile.mrid=blob.rid"
75 " WHERE NOT deleted AND vfile.vid=%d"
76 " ORDER BY 1", vid);
77 while( db_step(&q)==SQLITE_ROW ){
78 const char *zName = db_column_text(&q, 0);
79 const char *zUuid = db_column_text(&q, 1);
80 blob_appendf(&manifest, "F %F %s\n", zName, zUuid);
81 }
82 db_finalize(&q);
83
84 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
85 blob_appendf(&manifest, "P %s\n", zUuid);
86 blob_appendf(&manifest, "R %b\n", &cksum1);
87
88 if( zColor!=0 ){
89 blob_appendf(&manifest, "T *bgcolor * %F\n", zColor);
90 blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
91 }else{
92 blob_appendf(&manifest, "T *sym-%F *\n", zBranch);
93 }
94
95 /* Cancel any tags that propagate */
96 db_prepare(&q,
97 "SELECT tagname"
98 " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
99 " WHERE rid=%d AND tagtype=2", vid);
100 while( db_step(&q)==SQLITE_ROW ){
101 const char *zTagname = db_column_text(&q, 0);
102 blob_appendf(&manifest, "T -%s *\n", zTagname);
103 }
104 db_finalize(&q);
105
106 blob_appendf(&manifest, "U %F\n", g.zLogin);
107 md5sum_blob(&manifest, &mcksum);
108 blob_appendf(&manifest, "Z %b\n", &mcksum);
109 if( !noSign && clearsign(&manifest, &manifest) ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110 Blob ans;
111 blob_zero(&ans);
112 prompt_user("unable to sign manifest. continue [y/N]? ", &ans);
113 if( blob_str(&ans)[0]!='y' ){
114 db_end_transaction(1);
115 exit(1);
116 }
117 }
118
119 /*blob_write_to_file(&manifest, "manifest.new");*/
120
121 nvid = content_put(&manifest, 0, 0);
122 if( nvid==0 ){
123 fossil_panic("trouble committing manifest: %s", g.zErrMsg);
124 }
125 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
126 manifest_crosslink(nvid, &manifest);
127 content_deltify(vid, nvid, 0);
128 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
129 printf("Branch Version: %s\n", zUuid);
130 printf("\n");
131 printf("Notice: working copy not updated to the new branch. If\n");
132 printf(" you wish to work on the new branch, update to\n");
133 printf(" that branch first:\n");
134 printf("\n");
135 printf(" fossil update %s\n", zBranch);
136
137 /* Verify that the manifest checksum matches the expected checksum */
138 vfile_aggregate_checksum_repository(nvid, &cksum2);
139 vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
140 if( blob_compare(&cksum1, &cksum1b) ){
141 fossil_panic("manifest checksum does not agree with manifest: "
142 "%b versus %b", &cksum1, &cksum1b);
143 }
144
145 /* Verify that the commit did not modify any disk images. */
146 vfile_aggregate_checksum_disk(vid, &cksum2);
147 if( blob_compare(&cksum1, &cksum2) ){
148 fossil_panic("tree checksums before and after commit do not match");
149 }
150
151 /* Clear the undo/redo stack */
152 undo_reset();
153
154 /* Commit */
155 db_end_transaction(0);
156
157 /* Do an autosync push, if requested */
158 autosync(AUTOSYNC_PUSH);
159 }
160
161 /*
162 ** NB: The "branch" command is disabled pending further discussion of its
163 ** purpose and usefulness....
164 **
165 ** COM MAND: branch
166 **
167 ** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE?
168 **
169 ** Run various subcommands on the branches of the open repository or
170 ** of the repository identified by the -R or --repository option.
171 **
172 ** %fossil branch new ?-bgcolor COLOR? BRANCH-NAME
173 **
174 ** Create a new branch BRANCH-NAME. You can optionally give
175 ** a commit message and branch color.
176 **
177 ** %fossil branch list
178
--- src/branch.c
+++ src/branch.c
@@ -25,153 +25,170 @@
25 */
26 #include "config.h"
27 #include "branch.h"
28 #include <assert.h>
29
30 /*
31 ** fossil branch new BRANCH-NAME ?ORIGIN-CHECK-IN? ?-bgcolor COLOR?
32 ** argv0 argv1 argv2 argv3 argv4
33 */
34 void branch_new(void){
35 int rootid; /* RID of the root check-in - what we branch off of */
36 int brid; /* RID of the branch check-in */
37 int noSign; /* True if the branch is unsigned */
38 int i; /* Loop counter */
39 char *zUuid; /* Artifact ID of origin */
40 Stmt q; /* Generic query */
41 const char *zBranch; /* Name of the new branch */
42 char *zDate; /* Date that branch was created */
43 char *zComment; /* Check-in comment for the new branch */
44 const char *zColor; /* Color of the new branch */
45 Blob branch; /* manifest for the new branch */
46 Blob parent; /* root check-in manifest */
47 Manifest mParent; /* Parsed parent manifest */
48 Blob mcksum; /* Self-checksum on the manifest */
 
 
49
50 noSign = find_option("nosign","",0)!=0;
 
 
51 zColor = find_option("bgcolor","c",1);
 
52 verify_all_options();
53 if( g.argc<3 ){
54 usage("branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR?");
55 }
56 db_find_and_open_repository(1);
57 noSign = db_get_int("omitsign", 0)|noSign;
58
59 /* fossil branch new name */
 
 
 
60 zBranch = g.argv[3];
61 if( zBranch==0 || zBranch[0]==0 ){
62 fossil_panic("branch name cannot be empty");
63 }
64 if( db_exists(
65 "SELECT 1 FROM tagxref"
66 " WHERE tagtype>0"
67 " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%s')",
68 zBranch)!=0 ){
69 fossil_fatal("branch \"%s\" already exists", zBranch);
70 }
71
72 user_select();
73 db_begin_transaction();
74 if( g.argc<5 ){
75 if( unsaved_changes() ){
76 fossil_fatal("there are uncommitted changes. please commit first");
77 }
78 rootid = db_lget_int("checkout", 0);
79 }else{
80 rootid = name_to_rid(g.argv[4]);
81 }
82 if( rootid==0 ){
83 fossil_fatal("unable to locate check-in off of which to branch");
84 }
85
86 /* Create a manifest for the new branch */
87 blob_zero(&branch);
88 zComment = mprintf("Create new branch named \"%h\"", zBranch);
89 blob_appendf(&branch, "C %F\n", zComment);
90 zDate = db_text(0, "SELECT datetime('now')");
91 zDate[10] = 'T';
92 blob_appendf(&branch, "D %s\n", zDate);
93
94 /* Copy all of the content from the parent into the branch */
95 content_get(rootid, &parent);
96 manifest_parse(&mParent, &parent);
97 if( mParent.type!=CFTYPE_MANIFEST ){
98 fossil_fatal("%s is not a valid check-in", g.argv[4]);
99 }
100 for(i=0; i<mParent.nFile; ++i){
101 if( mParent.aFile[i].zPerm[0] ){
102 blob_appendf(&branch, "F %F %s %s\n",
103 mParent.aFile[i].zName,
104 mParent.aFile[i].zUuid,
105 mParent.aFile[i].zPerm);
106 }else{
107 blob_appendf(&branch, "F %F %s\n",
108 mParent.aFile[i].zName,
109 mParent.aFile[i].zUuid);
110 }
111 }
112 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
113 blob_appendf(&branch, "P %s\n", zUuid);
114 blob_appendf(&branch, "R %s\n", mParent.zRepoCksum);
115 manifest_clear(&mParent);
116
117 /* Add the symbolic branch name and the "newbranch" tag to identify
118 ** this as a new branch */
119 if( zColor!=0 ){
120 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
121 }
122 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
123 blob_appendf(&branch, "T +newbranch *\n");
124
125 /* Cancel all other symbolic tags */
126 db_prepare(&q,
127 "SELECT tagname FROM tagxref, tag"
128 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
129 " AND tagtype>0 AND tagname GLOB 'sym-*'"
130 " ORDER BY tagname",
131 rootid);
132 while( db_step(&q)==SQLITE_ROW ){
133 const char *zTag = db_column_text(&q, 0);
134 blob_appendf(&branch, "T -%s *\n", zTag);
135 }
136 db_finalize(&q);
137
138 blob_appendf(&branch, "U %F\n", g.zLogin);
139 md5sum_blob(&branch, &mcksum);
140 blob_appendf(&branch, "Z %b\n", &mcksum);
141 if( !noSign && clearsign(&branch, &branch) ){
142 Blob ans;
143 blob_zero(&ans);
144 prompt_user("unable to sign manifest. continue [y/N]? ", &ans);
145 if( blob_str(&ans)[0]!='y' ){
146 db_end_transaction(1);
147 exit(1);
148 }
149 }
 
 
150
151 brid = content_put(&branch, 0, 0);
152 if( brid==0 ){
153 fossil_panic("trouble committing manifest: %s", g.zErrMsg);
154 }
155 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
156 if( manifest_crosslink(brid, &branch)==0 ){
157 fossil_panic("unable to install new manifest");
158 }
159 content_deltify(rootid, brid, 0);
160 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
161 printf("New branch: %s\n", zUuid);
162 if( g.argc==3 ){
163 printf(
164 "\n"
165 "Note: the local check-out has not been updated to the new\n"
166 " branch. To begin working on the new branch, do this:\n"
167 "\n"
168 " %s update %s\n",
169 g.argv[0], zBranch
170 );
171 }
172
 
 
 
 
 
 
 
 
 
 
173
174 /* Commit */
175 db_end_transaction(0);
176
177 /* Do an autosync push, if requested */
178 autosync(AUTOSYNC_PUSH);
179 }
180
181 /*
182 ** COMMAND: branch
 
 
 
183 **
184 ** Usage: %fossil branch SUBCOMMAND ... ?-R|--repository FILE?
185 **
186 ** Run various subcommands on the branches of the open repository or
187 ** of the repository identified by the -R or --repository option.
188 **
189 ** %fossil branch new BRANCH-NAME ?ROOT-CHECK-IN? ?-bgcolor COLOR?
190 **
191 ** Create a new branch BRANCH-NAME. You can optionally give
192 ** a commit message and branch color.
193 **
194 ** %fossil branch list
195
+21 -2
--- src/checkin.c
+++ src/checkin.c
@@ -344,10 +344,29 @@
344344
blob_reset(&b);
345345
}
346346
g.aCommitFile[ii-2] = 0;
347347
}
348348
}
349
+
350
+/*
351
+** Return true if the check-in with RID=rid has one or more child
352
+** check-ins which are not tagged with "newbranch". In other words,
353
+** return true if the check-in is not a leaf.
354
+*/
355
+int is_not_a_leaf(int rid){
356
+ return db_exists(
357
+ "SELECT 1 FROM plink"
358
+ " WHERE pid=%d"
359
+ " AND NOT EXIST("
360
+ "SELECT 1 FROM tagxref"
361
+ " WHERE tagxref.rid=plink.cid"
362
+ " AND tagxref.tagid=%d"
363
+ " AND tagxref.tagtype=1"
364
+ ")",
365
+ rid, TAG_NEWBRANCH
366
+ );
367
+}
349368
350369
/*
351370
** COMMAND: ci
352371
** COMMAND: commit
353372
**
@@ -440,14 +459,14 @@
440459
fossil_panic("file %s has not changed", blob_str(&unmodified));
441460
}
442461
}
443462
444463
vid = db_lget_int("checkout", 0);
445
- if( db_exists("SELECT 1 FROM plink WHERE pid=%d", vid) ){
464
+ if( is_not_a_leaf(vid) ){
446465
wouldFork=1;
447466
if( forceFlag==0 ){
448
- fossil_fatal("would fork. use -f or --force");
467
+ fossil_fatal("would fork. \"update\" first or use -f or --force.");
449468
}
450469
}
451470
vfile_aggregate_checksum_disk(vid, &cksum1);
452471
if( zComment ){
453472
blob_zero(&comment);
454473
--- src/checkin.c
+++ src/checkin.c
@@ -344,10 +344,29 @@
344 blob_reset(&b);
345 }
346 g.aCommitFile[ii-2] = 0;
347 }
348 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
350 /*
351 ** COMMAND: ci
352 ** COMMAND: commit
353 **
@@ -440,14 +459,14 @@
440 fossil_panic("file %s has not changed", blob_str(&unmodified));
441 }
442 }
443
444 vid = db_lget_int("checkout", 0);
445 if( db_exists("SELECT 1 FROM plink WHERE pid=%d", vid) ){
446 wouldFork=1;
447 if( forceFlag==0 ){
448 fossil_fatal("would fork. use -f or --force");
449 }
450 }
451 vfile_aggregate_checksum_disk(vid, &cksum1);
452 if( zComment ){
453 blob_zero(&comment);
454
--- src/checkin.c
+++ src/checkin.c
@@ -344,10 +344,29 @@
344 blob_reset(&b);
345 }
346 g.aCommitFile[ii-2] = 0;
347 }
348 }
349
350 /*
351 ** Return true if the check-in with RID=rid has one or more child
352 ** check-ins which are not tagged with "newbranch". In other words,
353 ** return true if the check-in is not a leaf.
354 */
355 int is_not_a_leaf(int rid){
356 return db_exists(
357 "SELECT 1 FROM plink"
358 " WHERE pid=%d"
359 " AND NOT EXIST("
360 "SELECT 1 FROM tagxref"
361 " WHERE tagxref.rid=plink.cid"
362 " AND tagxref.tagid=%d"
363 " AND tagxref.tagtype=1"
364 ")",
365 rid, TAG_NEWBRANCH
366 );
367 }
368
369 /*
370 ** COMMAND: ci
371 ** COMMAND: commit
372 **
@@ -440,14 +459,14 @@
459 fossil_panic("file %s has not changed", blob_str(&unmodified));
460 }
461 }
462
463 vid = db_lget_int("checkout", 0);
464 if( is_not_a_leaf(vid) ){
465 wouldFork=1;
466 if( forceFlag==0 ){
467 fossil_fatal("would fork. \"update\" first or use -f or --force.");
468 }
469 }
470 vfile_aggregate_checksum_disk(vid, &cksum1);
471 if( zComment ){
472 blob_zero(&comment);
473
+103 -16
--- src/descendants.c
+++ src/descendants.c
@@ -30,46 +30,111 @@
3030
3131
3232
/*
3333
** Create a temporary table named "leaves" if it does not
3434
** already exist. Load this table with the RID of all
35
-** versions that are leaves which are decended from
36
-** version iBase.
35
+** check-ins that are leaves which are decended from
36
+** check-in iBase.
37
+**
38
+** A "leaf" is a check-in that has no children. For the purpose
39
+** of finding leaves, children marked with the "newbranch" tag are
40
+** not counted as children. For example:
41
+**
42
+**
43
+** A -> B -> C -> D
44
+** `-> E
45
+**
46
+** D and E are clearly leaves since they have no children. If
47
+** D has the "newbranch" tag, then C is also a leaf since its only
48
+** child is marked as a newbranch.
49
+**
50
+** The closeMode flag determines behavior associated with the "closed"
51
+** tag:
52
+**
53
+** closeMode==0 Show all leaves regardless of the "closed" tag.
54
+**
55
+** closeMode==1 Show only leaves without the "closed" tag.
56
+**
57
+** closeMode==2 Show only leaves with the "closed" tag.
58
+**
59
+** The default behavior is to ignore closed leaves (closeMode==0). To
60
+** Show all leaves, use closeMode==1. To show only closed leaves, use
61
+** closeMode==2.
3762
*/
38
-void compute_leaves(int iBase){
63
+void compute_leaves(int iBase, int closeMode){
3964
Bag seen; /* Descendants seen */
4065
Bag pending; /* Unpropagated descendants */
66
+ Stmt q; /* Query to find children of a check-in */
67
+ Stmt isBr; /* Query to check to see if a check-in starts a new branch */
68
+ Stmt ins; /* INSERT statement for a new record */
4169
4270
db_multi_exec(
4371
"CREATE TEMP TABLE IF NOT EXISTS leaves("
4472
" rid INTEGER PRIMARY KEY"
4573
");"
4674
"DELETE FROM leaves;"
4775
);
4876
bag_init(&seen);
4977
bag_init(&pending);
78
+ if( iBase<=0 ){
79
+ iBase = db_int(0, "SELECT objid FROM event WHERE type='ci'"
80
+ " ORDER BY mtime LIMIT 1");
81
+ }
5082
bag_insert(&pending, iBase);
83
+ db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid");
84
+ db_prepare(&isBr,
85
+ "SELECT 1 FROM tagxref WHERE rid=:rid AND tagid=%d AND tagtype=1",
86
+ TAG_NEWBRANCH
87
+ );
88
+ db_prepare(&ins, "INSERT OR IGNORE INTO leaves VALUES(:rid)");
5189
while( bag_count(&pending) ){
5290
int rid = bag_first(&pending);
5391
int cnt = 0;
54
- Stmt q;
5592
bag_remove(&pending, rid);
56
- db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid);
93
+ db_bind_int(&q, ":rid", rid);
5794
while( db_step(&q)==SQLITE_ROW ){
5895
int cid = db_column_int(&q, 0);
5996
if( bag_insert(&seen, cid) ){
6097
bag_insert(&pending, cid);
6198
}
62
- cnt++;
99
+ db_bind_int(&isBr, ":rid", cid);
100
+ if( db_step(&isBr)==SQLITE_DONE ){
101
+ cnt++;
102
+ }
103
+ db_reset(&isBr);
63104
}
64
- db_finalize(&q);
105
+ db_reset(&q);
65106
if( cnt==0 ){
66
- db_multi_exec("INSERT INTO leaves VALUES(%d)", rid);
107
+ db_bind_int(&ins, ":rid", rid);
108
+ db_step(&ins);
109
+ db_reset(&ins);
67110
}
68111
}
112
+ db_finalize(&ins);
113
+ db_finalize(&isBr);
114
+ db_finalize(&q);
69115
bag_clear(&pending);
70116
bag_clear(&seen);
117
+ if( closeMode==1 ){
118
+ db_multi_exec(
119
+ "DELETE FROM leaves WHERE rid IN"
120
+ " (SELECT leaves.rid FROM leaves, tagxref"
121
+ " WHERE tagxref.rid=leaves.rid "
122
+ " AND tagxref.tagid=%d"
123
+ " AND tagxref.tagtype>0)",
124
+ TAG_CLOSED
125
+ );
126
+ }else if( closeMode==2 ){
127
+ db_multi_exec(
128
+ "DELETE FROM leaves WHERE rid NOT IN"
129
+ " (SELECT leaves.rid FROM leaves, tagxref"
130
+ " WHERE tagxref.rid=leaves.rid "
131
+ " AND tagxref.tagid=%d"
132
+ " AND tagxref.tagtype>0)",
133
+ TAG_CLOSED
134
+ );
135
+ }
71136
}
72137
73138
/*
74139
** Load the record ID rid and up to N-1 closest ancestors into
75140
** the "ok" table.
@@ -146,11 +211,11 @@
146211
base = db_lget_int("checkout", 0);
147212
}else{
148213
base = name_to_rid(g.argv[2]);
149214
}
150215
if( base==0 ) return;
151
- compute_leaves(base);
216
+ compute_leaves(base, 0);
152217
db_prepare(&q,
153218
"%s"
154219
" AND event.objid IN (SELECT rid FROM leaves)"
155220
" ORDER BY event.mtime DESC",
156221
timeline_query_for_tty()
@@ -160,22 +225,26 @@
160225
}
161226
162227
/*
163228
** COMMAND: leaves
164229
**
165
-** Usage: %fossil leaves
230
+** Usage: %fossil leaves ?--all? ?--closed?
166231
**
167
-** Find leaves of all branches.
232
+** Find leaves of all branches. By default show only open leaves.
233
+** The --all flag causes all leaves (closed and open) to be shown.
234
+** The --closed flag shows only closed leaves.
168235
*/
169
-void branches_cmd(void){
236
+void leaves_cmd(void){
170237
Stmt q;
238
+ int showAll = find_option("all", 0, 0)!=0;
239
+ int showClosed = find_option("closed", 0, 0)!=0;
171240
172241
db_must_be_within_tree();
242
+ compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
173243
db_prepare(&q,
174244
"%s"
175
- " AND blob.rid IN"
176
- " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
245
+ " AND blob.rid IN leaves"
177246
" ORDER BY event.mtime DESC",
178247
timeline_query_for_tty()
179248
);
180249
print_timeline(&q, 2000);
181250
db_finalize(&q);
@@ -186,20 +255,38 @@
186255
**
187256
** Find leaves of all branches.
188257
*/
189258
void leaves_page(void){
190259
Stmt q;
260
+ int showAll = P("all")!=0;
261
+ int showClosed = P("closed")!=0;
191262
192263
login_check_credentials();
193264
if( !g.okRead ){ login_needed(); return; }
194265
266
+ if( !showAll ){
267
+ style_submenu_element("All", "All", "leaves?all");
268
+ }
269
+ if( !showClosed ){
270
+ style_submenu_element("Closed", "Closed", "leaves?closed");
271
+ }
272
+ if( showClosed || showAll ){
273
+ style_submenu_element("Open", "Open", "leaves");
274
+ }
195275
style_header("Leaves");
196276
login_anonymous_available();
277
+ compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
278
+ if( showAll ){
279
+ @ <h1>All leaves, both open and closed</h1>
280
+ }else if( showClosed ){
281
+ @ <h1>Closed leaves only</h1>
282
+ }else{
283
+ @ <h1>All open leaves</h1>
284
+ }
197285
db_prepare(&q,
198286
"%s"
199
- " AND blob.rid IN"
200
- " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
287
+ " AND blob.rid IN leaves"
201288
" ORDER BY event.mtime DESC",
202289
timeline_query_for_www()
203290
);
204291
www_print_timeline(&q);
205292
db_finalize(&q);
206293
--- src/descendants.c
+++ src/descendants.c
@@ -30,46 +30,111 @@
30
31
32 /*
33 ** Create a temporary table named "leaves" if it does not
34 ** already exist. Load this table with the RID of all
35 ** versions that are leaves which are decended from
36 ** version iBase.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37 */
38 void compute_leaves(int iBase){
39 Bag seen; /* Descendants seen */
40 Bag pending; /* Unpropagated descendants */
 
 
 
41
42 db_multi_exec(
43 "CREATE TEMP TABLE IF NOT EXISTS leaves("
44 " rid INTEGER PRIMARY KEY"
45 ");"
46 "DELETE FROM leaves;"
47 );
48 bag_init(&seen);
49 bag_init(&pending);
 
 
 
 
50 bag_insert(&pending, iBase);
 
 
 
 
 
 
51 while( bag_count(&pending) ){
52 int rid = bag_first(&pending);
53 int cnt = 0;
54 Stmt q;
55 bag_remove(&pending, rid);
56 db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid);
57 while( db_step(&q)==SQLITE_ROW ){
58 int cid = db_column_int(&q, 0);
59 if( bag_insert(&seen, cid) ){
60 bag_insert(&pending, cid);
61 }
62 cnt++;
 
 
 
 
63 }
64 db_finalize(&q);
65 if( cnt==0 ){
66 db_multi_exec("INSERT INTO leaves VALUES(%d)", rid);
 
 
67 }
68 }
 
 
 
69 bag_clear(&pending);
70 bag_clear(&seen);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71 }
72
73 /*
74 ** Load the record ID rid and up to N-1 closest ancestors into
75 ** the "ok" table.
@@ -146,11 +211,11 @@
146 base = db_lget_int("checkout", 0);
147 }else{
148 base = name_to_rid(g.argv[2]);
149 }
150 if( base==0 ) return;
151 compute_leaves(base);
152 db_prepare(&q,
153 "%s"
154 " AND event.objid IN (SELECT rid FROM leaves)"
155 " ORDER BY event.mtime DESC",
156 timeline_query_for_tty()
@@ -160,22 +225,26 @@
160 }
161
162 /*
163 ** COMMAND: leaves
164 **
165 ** Usage: %fossil leaves
166 **
167 ** Find leaves of all branches.
 
 
168 */
169 void branches_cmd(void){
170 Stmt q;
 
 
171
172 db_must_be_within_tree();
 
173 db_prepare(&q,
174 "%s"
175 " AND blob.rid IN"
176 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
177 " ORDER BY event.mtime DESC",
178 timeline_query_for_tty()
179 );
180 print_timeline(&q, 2000);
181 db_finalize(&q);
@@ -186,20 +255,38 @@
186 **
187 ** Find leaves of all branches.
188 */
189 void leaves_page(void){
190 Stmt q;
 
 
191
192 login_check_credentials();
193 if( !g.okRead ){ login_needed(); return; }
194
 
 
 
 
 
 
 
 
 
195 style_header("Leaves");
196 login_anonymous_available();
 
 
 
 
 
 
 
 
197 db_prepare(&q,
198 "%s"
199 " AND blob.rid IN"
200 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
201 " ORDER BY event.mtime DESC",
202 timeline_query_for_www()
203 );
204 www_print_timeline(&q);
205 db_finalize(&q);
206
--- src/descendants.c
+++ src/descendants.c
@@ -30,46 +30,111 @@
30
31
32 /*
33 ** Create a temporary table named "leaves" if it does not
34 ** already exist. Load this table with the RID of all
35 ** check-ins that are leaves which are decended from
36 ** check-in iBase.
37 **
38 ** A "leaf" is a check-in that has no children. For the purpose
39 ** of finding leaves, children marked with the "newbranch" tag are
40 ** not counted as children. For example:
41 **
42 **
43 ** A -> B -> C -> D
44 ** `-> E
45 **
46 ** D and E are clearly leaves since they have no children. If
47 ** D has the "newbranch" tag, then C is also a leaf since its only
48 ** child is marked as a newbranch.
49 **
50 ** The closeMode flag determines behavior associated with the "closed"
51 ** tag:
52 **
53 ** closeMode==0 Show all leaves regardless of the "closed" tag.
54 **
55 ** closeMode==1 Show only leaves without the "closed" tag.
56 **
57 ** closeMode==2 Show only leaves with the "closed" tag.
58 **
59 ** The default behavior is to ignore closed leaves (closeMode==0). To
60 ** Show all leaves, use closeMode==1. To show only closed leaves, use
61 ** closeMode==2.
62 */
63 void compute_leaves(int iBase, int closeMode){
64 Bag seen; /* Descendants seen */
65 Bag pending; /* Unpropagated descendants */
66 Stmt q; /* Query to find children of a check-in */
67 Stmt isBr; /* Query to check to see if a check-in starts a new branch */
68 Stmt ins; /* INSERT statement for a new record */
69
70 db_multi_exec(
71 "CREATE TEMP TABLE IF NOT EXISTS leaves("
72 " rid INTEGER PRIMARY KEY"
73 ");"
74 "DELETE FROM leaves;"
75 );
76 bag_init(&seen);
77 bag_init(&pending);
78 if( iBase<=0 ){
79 iBase = db_int(0, "SELECT objid FROM event WHERE type='ci'"
80 " ORDER BY mtime LIMIT 1");
81 }
82 bag_insert(&pending, iBase);
83 db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid");
84 db_prepare(&isBr,
85 "SELECT 1 FROM tagxref WHERE rid=:rid AND tagid=%d AND tagtype=1",
86 TAG_NEWBRANCH
87 );
88 db_prepare(&ins, "INSERT OR IGNORE INTO leaves VALUES(:rid)");
89 while( bag_count(&pending) ){
90 int rid = bag_first(&pending);
91 int cnt = 0;
 
92 bag_remove(&pending, rid);
93 db_bind_int(&q, ":rid", rid);
94 while( db_step(&q)==SQLITE_ROW ){
95 int cid = db_column_int(&q, 0);
96 if( bag_insert(&seen, cid) ){
97 bag_insert(&pending, cid);
98 }
99 db_bind_int(&isBr, ":rid", cid);
100 if( db_step(&isBr)==SQLITE_DONE ){
101 cnt++;
102 }
103 db_reset(&isBr);
104 }
105 db_reset(&q);
106 if( cnt==0 ){
107 db_bind_int(&ins, ":rid", rid);
108 db_step(&ins);
109 db_reset(&ins);
110 }
111 }
112 db_finalize(&ins);
113 db_finalize(&isBr);
114 db_finalize(&q);
115 bag_clear(&pending);
116 bag_clear(&seen);
117 if( closeMode==1 ){
118 db_multi_exec(
119 "DELETE FROM leaves WHERE rid IN"
120 " (SELECT leaves.rid FROM leaves, tagxref"
121 " WHERE tagxref.rid=leaves.rid "
122 " AND tagxref.tagid=%d"
123 " AND tagxref.tagtype>0)",
124 TAG_CLOSED
125 );
126 }else if( closeMode==2 ){
127 db_multi_exec(
128 "DELETE FROM leaves WHERE rid NOT IN"
129 " (SELECT leaves.rid FROM leaves, tagxref"
130 " WHERE tagxref.rid=leaves.rid "
131 " AND tagxref.tagid=%d"
132 " AND tagxref.tagtype>0)",
133 TAG_CLOSED
134 );
135 }
136 }
137
138 /*
139 ** Load the record ID rid and up to N-1 closest ancestors into
140 ** the "ok" table.
@@ -146,11 +211,11 @@
211 base = db_lget_int("checkout", 0);
212 }else{
213 base = name_to_rid(g.argv[2]);
214 }
215 if( base==0 ) return;
216 compute_leaves(base, 0);
217 db_prepare(&q,
218 "%s"
219 " AND event.objid IN (SELECT rid FROM leaves)"
220 " ORDER BY event.mtime DESC",
221 timeline_query_for_tty()
@@ -160,22 +225,26 @@
225 }
226
227 /*
228 ** COMMAND: leaves
229 **
230 ** Usage: %fossil leaves ?--all? ?--closed?
231 **
232 ** Find leaves of all branches. By default show only open leaves.
233 ** The --all flag causes all leaves (closed and open) to be shown.
234 ** The --closed flag shows only closed leaves.
235 */
236 void leaves_cmd(void){
237 Stmt q;
238 int showAll = find_option("all", 0, 0)!=0;
239 int showClosed = find_option("closed", 0, 0)!=0;
240
241 db_must_be_within_tree();
242 compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
243 db_prepare(&q,
244 "%s"
245 " AND blob.rid IN leaves"
 
246 " ORDER BY event.mtime DESC",
247 timeline_query_for_tty()
248 );
249 print_timeline(&q, 2000);
250 db_finalize(&q);
@@ -186,20 +255,38 @@
255 **
256 ** Find leaves of all branches.
257 */
258 void leaves_page(void){
259 Stmt q;
260 int showAll = P("all")!=0;
261 int showClosed = P("closed")!=0;
262
263 login_check_credentials();
264 if( !g.okRead ){ login_needed(); return; }
265
266 if( !showAll ){
267 style_submenu_element("All", "All", "leaves?all");
268 }
269 if( !showClosed ){
270 style_submenu_element("Closed", "Closed", "leaves?closed");
271 }
272 if( showClosed || showAll ){
273 style_submenu_element("Open", "Open", "leaves");
274 }
275 style_header("Leaves");
276 login_anonymous_available();
277 compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
278 if( showAll ){
279 @ <h1>All leaves, both open and closed</h1>
280 }else if( showClosed ){
281 @ <h1>Closed leaves only</h1>
282 }else{
283 @ <h1>All open leaves</h1>
284 }
285 db_prepare(&q,
286 "%s"
287 " AND blob.rid IN leaves"
 
288 " ORDER BY event.mtime DESC",
289 timeline_query_for_www()
290 );
291 www_print_timeline(&q);
292 db_finalize(&q);
293
+1 -1
--- src/info.c
+++ src/info.c
@@ -442,11 +442,11 @@
442442
}else{
443443
@ %h(zName)</li>
444444
}
445445
}
446446
@ </ul>
447
- compute_leaves(rid);
447
+ compute_leaves(rid, 0);
448448
showDescendants(rid, 2, "Descendants");
449449
showLeaves();
450450
showAncestors(rid, 2, "Ancestors");
451451
style_footer();
452452
}
453453
--- src/info.c
+++ src/info.c
@@ -442,11 +442,11 @@
442 }else{
443 @ %h(zName)</li>
444 }
445 }
446 @ </ul>
447 compute_leaves(rid);
448 showDescendants(rid, 2, "Descendants");
449 showLeaves();
450 showAncestors(rid, 2, "Ancestors");
451 style_footer();
452 }
453
--- src/info.c
+++ src/info.c
@@ -442,11 +442,11 @@
442 }else{
443 @ %h(zName)</li>
444 }
445 }
446 @ </ul>
447 compute_leaves(rid, 0);
448 showDescendants(rid, 2, "Descendants");
449 showLeaves();
450 showAncestors(rid, 2, "Ancestors");
451 style_footer();
452 }
453
+5 -1
--- src/schema.c
+++ src/schema.c
@@ -280,10 +280,12 @@
280280
@ INSERT INTO tag VALUES(2, 'comment'); -- TAG_COMMENT
281281
@ INSERT INTO tag VALUES(3, 'user'); -- TAG_USER
282282
@ INSERT INTO tag VALUES(4, 'hidden'); -- TAG_HIDDEN
283283
@ INSERT INTO tag VALUES(5, 'private'); -- TAG_PRIVATE
284284
@ INSERT INTO tag VALUES(6, 'cluster'); -- TAG_CLUSTER
285
+@ INSERT INTO tag VALUES(7, 'newbranch'); -- TAG_NEWBRANCH
286
+@ INSERT INTO tag VALUES(8, 'closed'); -- TAG_CLOSED
285287
@
286288
@ -- Assignments of tags to baselines. Note that we allow tags to
287289
@ -- have values assigned to them. So we are not really dealing with
288290
@ -- tags here. These are really properties. But we are going to
289291
@ -- keep calling them tags because in many cases the value is ignored.
@@ -331,13 +333,15 @@
331333
# define TAG_COMMENT 2 /* The check-in comment */
332334
# define TAG_USER 3 /* User who made a checking */
333335
# define TAG_HIDDEN 4 /* Do not display or sync */
334336
# define TAG_PRIVATE 5 /* Display but do not sync */
335337
# define TAG_CLUSTER 6 /* A cluster */
338
+# define TAG_NEWBRANCH 7 /* First check-in of a new named branch */
339
+# define TAG_CLOSED 8 /* Do not display this check-in as a leaf */
336340
#endif
337341
#if EXPORT_INTERFACE
338
-# define MAX_INT_TAG 6 /* The largest pre-assigned tag id */
342
+# define MAX_INT_TAG 8 /* The largest pre-assigned tag id */
339343
#endif
340344
341345
/*
342346
** The schema for the locate FOSSIL database file found at the root
343347
** of very check-out. This database contains the complete state of
344348
--- src/schema.c
+++ src/schema.c
@@ -280,10 +280,12 @@
280 @ INSERT INTO tag VALUES(2, 'comment'); -- TAG_COMMENT
281 @ INSERT INTO tag VALUES(3, 'user'); -- TAG_USER
282 @ INSERT INTO tag VALUES(4, 'hidden'); -- TAG_HIDDEN
283 @ INSERT INTO tag VALUES(5, 'private'); -- TAG_PRIVATE
284 @ INSERT INTO tag VALUES(6, 'cluster'); -- TAG_CLUSTER
 
 
285 @
286 @ -- Assignments of tags to baselines. Note that we allow tags to
287 @ -- have values assigned to them. So we are not really dealing with
288 @ -- tags here. These are really properties. But we are going to
289 @ -- keep calling them tags because in many cases the value is ignored.
@@ -331,13 +333,15 @@
331 # define TAG_COMMENT 2 /* The check-in comment */
332 # define TAG_USER 3 /* User who made a checking */
333 # define TAG_HIDDEN 4 /* Do not display or sync */
334 # define TAG_PRIVATE 5 /* Display but do not sync */
335 # define TAG_CLUSTER 6 /* A cluster */
 
 
336 #endif
337 #if EXPORT_INTERFACE
338 # define MAX_INT_TAG 6 /* The largest pre-assigned tag id */
339 #endif
340
341 /*
342 ** The schema for the locate FOSSIL database file found at the root
343 ** of very check-out. This database contains the complete state of
344
--- src/schema.c
+++ src/schema.c
@@ -280,10 +280,12 @@
280 @ INSERT INTO tag VALUES(2, 'comment'); -- TAG_COMMENT
281 @ INSERT INTO tag VALUES(3, 'user'); -- TAG_USER
282 @ INSERT INTO tag VALUES(4, 'hidden'); -- TAG_HIDDEN
283 @ INSERT INTO tag VALUES(5, 'private'); -- TAG_PRIVATE
284 @ INSERT INTO tag VALUES(6, 'cluster'); -- TAG_CLUSTER
285 @ INSERT INTO tag VALUES(7, 'newbranch'); -- TAG_NEWBRANCH
286 @ INSERT INTO tag VALUES(8, 'closed'); -- TAG_CLOSED
287 @
288 @ -- Assignments of tags to baselines. Note that we allow tags to
289 @ -- have values assigned to them. So we are not really dealing with
290 @ -- tags here. These are really properties. But we are going to
291 @ -- keep calling them tags because in many cases the value is ignored.
@@ -331,13 +333,15 @@
333 # define TAG_COMMENT 2 /* The check-in comment */
334 # define TAG_USER 3 /* User who made a checking */
335 # define TAG_HIDDEN 4 /* Do not display or sync */
336 # define TAG_PRIVATE 5 /* Display but do not sync */
337 # define TAG_CLUSTER 6 /* A cluster */
338 # define TAG_NEWBRANCH 7 /* First check-in of a new named branch */
339 # define TAG_CLOSED 8 /* Do not display this check-in as a leaf */
340 #endif
341 #if EXPORT_INTERFACE
342 # define MAX_INT_TAG 8 /* The largest pre-assigned tag id */
343 #endif
344
345 /*
346 ** The schema for the locate FOSSIL database file found at the root
347 ** of very check-out. This database contains the complete state of
348
+79 -147
--- src/tag.c
+++ src/tag.c
@@ -246,76 +246,20 @@
246246
db_begin_transaction();
247247
tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
248248
db_end_transaction(0);
249249
}
250250
251
-/*
252
-** Prepare an artifact that describes a fork from a certain baseline.
253
-** Furthermore a propagating symbolic tag will be inserted and
254
-** all other propagating symbolic tags will be cancelled.
255
-**
256
-** The changes are appended at the Blob pCtrl. However the manifest
257
-** is not complete at that stage.
258
-*/
259
-static void tag_prepare_fork(
260
- Blob *pCtrl,
261
- const char *zTagname,
262
- int rid,
263
- int preflen /* Tag prefix length to adjust name if reqd */
264
-){
265
- Stmt q;
266
- Manifest origin;
267
- Blob originContent;
268
- char *zDate;
269
- int i;
270
-
271
- blob_appendf(pCtrl, "C Create\\snamed\\sfork\\s%s\n", zTagname+preflen);
272
- content_get(rid, &originContent);
273
- manifest_parse(&origin, &originContent);
274
- zDate = db_text(0, "SELECT datetime('now')");
275
- zDate[10] = 'T';
276
- blob_appendf(pCtrl, "D %s\n", zDate);
277
- for(i=0; i<origin.nFile; ++i){
278
- blob_appendf(pCtrl, "F %s %s %s\n",
279
- origin.aFile[i].zName,
280
- origin.aFile[i].zUuid,
281
- origin.aFile[i].zPerm);
282
- }
283
- if( origin.nParent>0 ){
284
- blob_appendf(pCtrl, "P %s\n", origin.azParent[0]);
285
- }
286
- blob_appendf(pCtrl, "R %s\n", origin.zRepoCksum);
287
- blob_appendf(pCtrl, "T *%F *", zTagname);
288
-
289
- /* Cancel any sym- tags that propagate */
290
- db_prepare(&q,
291
- "SELECT tagname FROM tagxref, tag"
292
- " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
293
- " AND tagtype>0 AND tagname LIKE 'sym-%%'"
294
- " ORDER BY tagname",
295
- rid);
296
- while( db_step(&q)==SQLITE_ROW ){
297
- const char *zTag = db_column_text(&q, 0);
298
- blob_appendf(pCtrl, "\nT -%s *", zTag);
299
- }
300
- db_finalize(&q);
301
-
302
- /* Cleanup */
303
- manifest_clear(&origin);
304
-}
305
-
306251
/*
307252
** Add a control record to the repository that either creates
308253
** or cancels a tag.
309254
*/
310255
static void tag_add_artifact(
256
+ const char *zPrefix, /* Prefix to prepend to tag name */
311257
const char *zTagname, /* The tag to add or cancel */
312258
const char *zObjName, /* Name of object attached to */
313259
const char *zValue, /* Value for the tag. Might be NULL */
314
- int tagtype, /* 0:cancel 1:singleton 2:propagated */
315
- int fork, /* Should a fork created from zObjName? */
316
- int preflen /* Tag prefix length to adjust name if reqd */
260
+ int tagtype /* 0:cancel 1:singleton 2:propagated */
317261
){
318262
int rid;
319263
int nrid;
320264
char *zDate;
321265
Blob uuid;
@@ -331,26 +275,25 @@
331275
return;
332276
}
333277
rid = name_to_rid(blob_str(&uuid));
334278
blob_zero(&ctrl);
335279
280
+#if 0
336281
if( validate16(zTagname, strlen(zTagname)) ){
337282
fossil_fatal(
338283
"invalid tag name \"%s\" - might be confused with"
339284
" a hexadecimal artifact ID",
340285
zTagname
341286
);
342287
}
343
- if( fork ){
344
- tag_prepare_fork(&ctrl, zTagname, rid, preflen);
345
- }else{
346
- zDate = db_text(0, "SELECT datetime('now')");
347
- zDate[10] = 'T';
348
- blob_appendf(&ctrl, "D %s\n", zDate);
349
- blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
350
- }
351
- if( tagtype && zValue && zValue[0] ){
288
+#endif
289
+ zDate = db_text(0, "SELECT datetime('now')");
290
+ zDate[10] = 'T';
291
+ blob_appendf(&ctrl, "D %s\n", zDate);
292
+ blob_appendf(&ctrl, "T %c%s%F %b",
293
+ zTagtype[tagtype], zPrefix, zTagname, &uuid);
294
+ if( tagtype>0 && zValue && zValue[0] ){
352295
blob_appendf(&ctrl, " %F\n", zValue);
353296
}else{
354297
blob_appendf(&ctrl, "\n");
355298
}
356299
blob_appendf(&ctrl, "U %F\n", g.zLogin);
@@ -369,43 +312,30 @@
369312
** COMMAND: tag
370313
** Usage: %fossil tag SUBCOMMAND ...
371314
**
372315
** Run various subcommands to control tags and properties
373316
**
374
-** %fossil tag add ?--raw? TAGNAME BASELINE ?VALUE?
375
-**
376
-** Add a new tag or property to BASELINE. The tag will
377
-** be usable instead of a BASELINE in commands such as
378
-** update and merge.
379
-**
380
-** %fossil tag branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE?
381
-**
382
-** A fork will be created so that the new checkin
383
-** is a sibling of BASELINE and identical to it except
384
-** for a generated comment. Then the new tag will
385
-** be added to the new checkin and propagated to
386
-** all direct children. Additionally all symbolic
387
-** tags of that checkin inherited from BASELINE will
388
-** be cancelled.
389
-**
390
-** However, if the option --nofork is given, no
391
-** fork will be created and the tag/property will be
392
-** added to BASELINE directly. No tags will be canceled.
393
-**
394
-** %fossil tag cancel ?--raw? TAGNAME BASELINE
395
-**
396
-** Remove the tag TAGNAME from BASELINE, and also remove
317
+** %fossil tag add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?
318
+**
319
+** Add a new tag or property to CHECK-IN. The tag will
320
+** be usable instead of a CHECK-IN in commands such as
321
+** update and merge. If the --propagate flag is present,
322
+** the tag value propages to all descendants of CHECK-IN
323
+**
324
+** %fossil tag cancel ?--raw? TAGNAME CHECK-IN
325
+**
326
+** Remove the tag TAGNAME from CHECK-IN, and also remove
397327
** the propagation of the tag to any descendants.
398328
**
399329
** %fossil tag find ?--raw? TAGNAME
400330
**
401
-** List all baselines that use TAGNAME
331
+** List all check-ins that use TAGNAME
402332
**
403
-** %fossil tag list ?--raw? ?BASELINE?
333
+** %fossil tag list ?--raw? ?CHECK-IN?
404334
**
405
-** List all tags, or if BASELINE is supplied, list
406
-** all tags and their values for BASELINE.
335
+** List all tags, or if CHECK-IN is supplied, list
336
+** all tags and their values for CHECK-IN.
407337
**
408338
** The option --raw allows the manipulation of all types of tags
409339
** used for various internal purposes in fossil. It also shows
410340
** "cancel" tags for the "find" and "list" subcommands. You should
411341
** not use this option to make changes unless you are sure what
@@ -425,15 +355,13 @@
425355
** will assume that "decaf" is a tag/branch name.
426356
**
427357
*/
428358
void tag_cmd(void){
429359
int n;
430
- int raw = find_option("raw","",0)!=0;
431
- int fork = find_option("nofork","",0)==0;
432
- const char *prefix = raw ? "" : "sym-";
433
- int preflen = strlen(prefix);
434
- Blob tagname;
360
+ int fRaw = find_option("raw","",0)!=0;
361
+ int fPropagate = find_option("propagate","",0)!=0;
362
+ const char *zPrefix = fRaw ? "" : "sym-";
435363
436364
db_find_and_open_repository(1);
437365
if( g.argc<3 ){
438366
goto tag_cmd_usage;
439367
}
@@ -440,78 +368,83 @@
440368
n = strlen(g.argv[2]);
441369
if( n==0 ){
442370
goto tag_cmd_usage;
443371
}
444372
445
- blob_set(&tagname, prefix);
446
-
447373
if( strncmp(g.argv[2],"add",n)==0 ){
448374
char *zValue;
449375
if( g.argc!=5 && g.argc!=6 ){
450
- usage("add ?--raw? TAGNAME BASELINE ?VALUE?");
376
+ usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?");
451377
}
452
- blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
453378
zValue = g.argc==6 ? g.argv[5] : 0;
454
- tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 1, 0, 0);
379
+ tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, 1+fPropagate);
455380
}else
456381
457382
if( strncmp(g.argv[2],"branch",n)==0 ){
458
- char *zValue;
459
- if( g.argc!=5 && g.argc!=6 ){
460
- usage("branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE?");
461
- }
462
- blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
463
- zValue = g.argc==6 ? g.argv[5] : 0;
464
- tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 2, fork!=0,
465
- preflen);
466
- if( fork ){
467
- const char *zUuid = db_text(0, "SELECT uuid, MAX(rowid) FROM blob");
468
- printf("New_Fork \"%s\": %s\n", g.argv[3], zUuid);
469
- }
383
+ fossil_fatal("the \"fossil tag branch\" command is discontinued\n"
384
+ "Use the \"fossil branch new\" command instead.");
470385
}else
471386
472387
if( strncmp(g.argv[2],"cancel",n)==0 ){
473388
if( g.argc!=5 ){
474
- usage("cancel ?--raw? TAGNAME BASELINE");
389
+ usage("cancel ?--raw? TAGNAME CHECK-IN");
475390
}
476
- blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
477
- tag_add_artifact(blob_str(&tagname), g.argv[4], 0, 0, 0, 0);
391
+ tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0);
478392
}else
479393
480394
if( strncmp(g.argv[2],"find",n)==0 ){
481395
Stmt q;
482396
if( g.argc!=4 ){
483397
usage("find ?--raw? TAGNAME");
484398
}
485
- blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
486
- db_prepare(&q,
487
- "SELECT blob.uuid FROM tagxref, blob"
488
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)"
489
- " AND tagxref.tagtype > %d"
490
- " AND blob.rid=tagxref.rid", &tagname, raw ? -1 : 0
491
- );
492
- while( db_step(&q)==SQLITE_ROW ){
493
- printf("%s\n", db_column_text(&q, 0));
494
- }
495
- db_finalize(&q);
399
+ if( fRaw ){
400
+ db_prepare(&q,
401
+ "SELECT blob.uuid FROM tagxref, blob"
402
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
403
+ " AND tagxref.tagtype>0"
404
+ " AND blob.rid=tagxref.rid",
405
+ g.argv[3]
406
+ );
407
+ while( db_step(&q)==SQLITE_ROW ){
408
+ printf("%s\n", db_column_text(&q, 0));
409
+ }
410
+ db_finalize(&q);
411
+ }else{
412
+ int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
413
+ g.argv[3]);
414
+ if( tagid>0 ){
415
+ db_prepare(&q,
416
+ "%s"
417
+ " AND blob.rid IN ("
418
+ " SELECT rid FROM tagxref"
419
+ " WHERE tagtype>0 AND tagid=%d"
420
+ ")"
421
+ " ORDER BY event.mtime DESC",
422
+ timeline_query_for_tty(), tagid
423
+ );
424
+ print_timeline(&q, 2000);
425
+ db_finalize(&q);
426
+ }
427
+ }
496428
}else
497429
498430
if( strncmp(g.argv[2],"list",n)==0 ){
499431
Stmt q;
500432
if( g.argc==3 ){
501433
db_prepare(&q,
502434
"SELECT tagname FROM tag"
503435
" WHERE EXISTS(SELECT 1 FROM tagxref"
504436
" WHERE tagid=tag.tagid"
505
- " AND tagtype>%d)"
506
- " ORDER BY tagname",
507
- raw ? -1 : 0
437
+ " AND tagtype>0)"
438
+ " ORDER BY tagname"
508439
);
509440
while( db_step(&q)==SQLITE_ROW ){
510
- const char *name = db_column_text(&q, 0);
511
- if( raw || strncmp(name, prefix, preflen)==0 ){
512
- printf("%s\n", name+preflen);
441
+ const char *zName = db_column_text(&q, 0);
442
+ if( fRaw ){
443
+ printf("%s\n", zName);
444
+ }else if( strncmp(zName, "sym-", 4)==0 ){
445
+ printf("%s\n", &zName[4]);
513446
}
514447
}
515448
db_finalize(&q);
516449
}else if( g.argc==4 ){
517450
int rid = name_to_rid(g.argv[3]);
@@ -519,36 +452,35 @@
519452
"SELECT tagname, value FROM tagxref, tag"
520453
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
521454
" AND tagtype>%d"
522455
" ORDER BY tagname",
523456
rid,
524
- raw ? -1 : 0
457
+ fRaw ? -1 : 0
525458
);
526459
while( db_step(&q)==SQLITE_ROW ){
527460
const char *zName = db_column_text(&q, 0);
528461
const char *zValue = db_column_text(&q, 1);
529
- if( zValue ){
530
- if( raw || strncmp(zName, prefix, preflen)==0 ){
531
- printf("%s=%s\n", zName+preflen, zValue);
532
- }
462
+ if( fRaw==0 ){
463
+ if( strncmp(zName, "sym-", 4)!=0 ) continue;
464
+ zName += 4;
465
+ }
466
+ if( zValue && zValue[0] ){
467
+ printf("%s=%s\n", zName, zValue);
533468
}else{
534
- if( raw || strncmp(zName, prefix, preflen)==0 ){
535
- printf("%s\n", zName+preflen);
536
- }
469
+ printf("%s\n", zName);
537470
}
538471
}
539472
db_finalize(&q);
540473
}else{
541
- usage("tag list ?BASELINE?");
474
+ usage("tag list ?CHECK-IN?");
542475
}
543476
}else
544477
{
545478
goto tag_cmd_usage;
546479
}
547480
548481
/* Cleanup */
549
- blob_reset(&tagname);
550482
return;
551483
552484
tag_cmd_usage:
553
- usage("add|branch|cancel|find|list ...");
485
+ usage("add|cancel|find|list ...");
554486
}
555487
--- src/tag.c
+++ src/tag.c
@@ -246,76 +246,20 @@
246 db_begin_transaction();
247 tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
248 db_end_transaction(0);
249 }
250
251 /*
252 ** Prepare an artifact that describes a fork from a certain baseline.
253 ** Furthermore a propagating symbolic tag will be inserted and
254 ** all other propagating symbolic tags will be cancelled.
255 **
256 ** The changes are appended at the Blob pCtrl. However the manifest
257 ** is not complete at that stage.
258 */
259 static void tag_prepare_fork(
260 Blob *pCtrl,
261 const char *zTagname,
262 int rid,
263 int preflen /* Tag prefix length to adjust name if reqd */
264 ){
265 Stmt q;
266 Manifest origin;
267 Blob originContent;
268 char *zDate;
269 int i;
270
271 blob_appendf(pCtrl, "C Create\\snamed\\sfork\\s%s\n", zTagname+preflen);
272 content_get(rid, &originContent);
273 manifest_parse(&origin, &originContent);
274 zDate = db_text(0, "SELECT datetime('now')");
275 zDate[10] = 'T';
276 blob_appendf(pCtrl, "D %s\n", zDate);
277 for(i=0; i<origin.nFile; ++i){
278 blob_appendf(pCtrl, "F %s %s %s\n",
279 origin.aFile[i].zName,
280 origin.aFile[i].zUuid,
281 origin.aFile[i].zPerm);
282 }
283 if( origin.nParent>0 ){
284 blob_appendf(pCtrl, "P %s\n", origin.azParent[0]);
285 }
286 blob_appendf(pCtrl, "R %s\n", origin.zRepoCksum);
287 blob_appendf(pCtrl, "T *%F *", zTagname);
288
289 /* Cancel any sym- tags that propagate */
290 db_prepare(&q,
291 "SELECT tagname FROM tagxref, tag"
292 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
293 " AND tagtype>0 AND tagname LIKE 'sym-%%'"
294 " ORDER BY tagname",
295 rid);
296 while( db_step(&q)==SQLITE_ROW ){
297 const char *zTag = db_column_text(&q, 0);
298 blob_appendf(pCtrl, "\nT -%s *", zTag);
299 }
300 db_finalize(&q);
301
302 /* Cleanup */
303 manifest_clear(&origin);
304 }
305
306 /*
307 ** Add a control record to the repository that either creates
308 ** or cancels a tag.
309 */
310 static void tag_add_artifact(
 
311 const char *zTagname, /* The tag to add or cancel */
312 const char *zObjName, /* Name of object attached to */
313 const char *zValue, /* Value for the tag. Might be NULL */
314 int tagtype, /* 0:cancel 1:singleton 2:propagated */
315 int fork, /* Should a fork created from zObjName? */
316 int preflen /* Tag prefix length to adjust name if reqd */
317 ){
318 int rid;
319 int nrid;
320 char *zDate;
321 Blob uuid;
@@ -331,26 +275,25 @@
331 return;
332 }
333 rid = name_to_rid(blob_str(&uuid));
334 blob_zero(&ctrl);
335
 
336 if( validate16(zTagname, strlen(zTagname)) ){
337 fossil_fatal(
338 "invalid tag name \"%s\" - might be confused with"
339 " a hexadecimal artifact ID",
340 zTagname
341 );
342 }
343 if( fork ){
344 tag_prepare_fork(&ctrl, zTagname, rid, preflen);
345 }else{
346 zDate = db_text(0, "SELECT datetime('now')");
347 zDate[10] = 'T';
348 blob_appendf(&ctrl, "D %s\n", zDate);
349 blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
350 }
351 if( tagtype && zValue && zValue[0] ){
352 blob_appendf(&ctrl, " %F\n", zValue);
353 }else{
354 blob_appendf(&ctrl, "\n");
355 }
356 blob_appendf(&ctrl, "U %F\n", g.zLogin);
@@ -369,43 +312,30 @@
369 ** COMMAND: tag
370 ** Usage: %fossil tag SUBCOMMAND ...
371 **
372 ** Run various subcommands to control tags and properties
373 **
374 ** %fossil tag add ?--raw? TAGNAME BASELINE ?VALUE?
375 **
376 ** Add a new tag or property to BASELINE. The tag will
377 ** be usable instead of a BASELINE in commands such as
378 ** update and merge.
379 **
380 ** %fossil tag branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE?
381 **
382 ** A fork will be created so that the new checkin
383 ** is a sibling of BASELINE and identical to it except
384 ** for a generated comment. Then the new tag will
385 ** be added to the new checkin and propagated to
386 ** all direct children. Additionally all symbolic
387 ** tags of that checkin inherited from BASELINE will
388 ** be cancelled.
389 **
390 ** However, if the option --nofork is given, no
391 ** fork will be created and the tag/property will be
392 ** added to BASELINE directly. No tags will be canceled.
393 **
394 ** %fossil tag cancel ?--raw? TAGNAME BASELINE
395 **
396 ** Remove the tag TAGNAME from BASELINE, and also remove
397 ** the propagation of the tag to any descendants.
398 **
399 ** %fossil tag find ?--raw? TAGNAME
400 **
401 ** List all baselines that use TAGNAME
402 **
403 ** %fossil tag list ?--raw? ?BASELINE?
404 **
405 ** List all tags, or if BASELINE is supplied, list
406 ** all tags and their values for BASELINE.
407 **
408 ** The option --raw allows the manipulation of all types of tags
409 ** used for various internal purposes in fossil. It also shows
410 ** "cancel" tags for the "find" and "list" subcommands. You should
411 ** not use this option to make changes unless you are sure what
@@ -425,15 +355,13 @@
425 ** will assume that "decaf" is a tag/branch name.
426 **
427 */
428 void tag_cmd(void){
429 int n;
430 int raw = find_option("raw","",0)!=0;
431 int fork = find_option("nofork","",0)==0;
432 const char *prefix = raw ? "" : "sym-";
433 int preflen = strlen(prefix);
434 Blob tagname;
435
436 db_find_and_open_repository(1);
437 if( g.argc<3 ){
438 goto tag_cmd_usage;
439 }
@@ -440,78 +368,83 @@
440 n = strlen(g.argv[2]);
441 if( n==0 ){
442 goto tag_cmd_usage;
443 }
444
445 blob_set(&tagname, prefix);
446
447 if( strncmp(g.argv[2],"add",n)==0 ){
448 char *zValue;
449 if( g.argc!=5 && g.argc!=6 ){
450 usage("add ?--raw? TAGNAME BASELINE ?VALUE?");
451 }
452 blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
453 zValue = g.argc==6 ? g.argv[5] : 0;
454 tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 1, 0, 0);
455 }else
456
457 if( strncmp(g.argv[2],"branch",n)==0 ){
458 char *zValue;
459 if( g.argc!=5 && g.argc!=6 ){
460 usage("branch ?--raw? ?--nofork? TAGNAME BASELINE ?VALUE?");
461 }
462 blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
463 zValue = g.argc==6 ? g.argv[5] : 0;
464 tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 2, fork!=0,
465 preflen);
466 if( fork ){
467 const char *zUuid = db_text(0, "SELECT uuid, MAX(rowid) FROM blob");
468 printf("New_Fork \"%s\": %s\n", g.argv[3], zUuid);
469 }
470 }else
471
472 if( strncmp(g.argv[2],"cancel",n)==0 ){
473 if( g.argc!=5 ){
474 usage("cancel ?--raw? TAGNAME BASELINE");
475 }
476 blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
477 tag_add_artifact(blob_str(&tagname), g.argv[4], 0, 0, 0, 0);
478 }else
479
480 if( strncmp(g.argv[2],"find",n)==0 ){
481 Stmt q;
482 if( g.argc!=4 ){
483 usage("find ?--raw? TAGNAME");
484 }
485 blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
486 db_prepare(&q,
487 "SELECT blob.uuid FROM tagxref, blob"
488 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)"
489 " AND tagxref.tagtype > %d"
490 " AND blob.rid=tagxref.rid", &tagname, raw ? -1 : 0
491 );
492 while( db_step(&q)==SQLITE_ROW ){
493 printf("%s\n", db_column_text(&q, 0));
494 }
495 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496 }else
497
498 if( strncmp(g.argv[2],"list",n)==0 ){
499 Stmt q;
500 if( g.argc==3 ){
501 db_prepare(&q,
502 "SELECT tagname FROM tag"
503 " WHERE EXISTS(SELECT 1 FROM tagxref"
504 " WHERE tagid=tag.tagid"
505 " AND tagtype>%d)"
506 " ORDER BY tagname",
507 raw ? -1 : 0
508 );
509 while( db_step(&q)==SQLITE_ROW ){
510 const char *name = db_column_text(&q, 0);
511 if( raw || strncmp(name, prefix, preflen)==0 ){
512 printf("%s\n", name+preflen);
 
 
513 }
514 }
515 db_finalize(&q);
516 }else if( g.argc==4 ){
517 int rid = name_to_rid(g.argv[3]);
@@ -519,36 +452,35 @@
519 "SELECT tagname, value FROM tagxref, tag"
520 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
521 " AND tagtype>%d"
522 " ORDER BY tagname",
523 rid,
524 raw ? -1 : 0
525 );
526 while( db_step(&q)==SQLITE_ROW ){
527 const char *zName = db_column_text(&q, 0);
528 const char *zValue = db_column_text(&q, 1);
529 if( zValue ){
530 if( raw || strncmp(zName, prefix, preflen)==0 ){
531 printf("%s=%s\n", zName+preflen, zValue);
532 }
 
 
533 }else{
534 if( raw || strncmp(zName, prefix, preflen)==0 ){
535 printf("%s\n", zName+preflen);
536 }
537 }
538 }
539 db_finalize(&q);
540 }else{
541 usage("tag list ?BASELINE?");
542 }
543 }else
544 {
545 goto tag_cmd_usage;
546 }
547
548 /* Cleanup */
549 blob_reset(&tagname);
550 return;
551
552 tag_cmd_usage:
553 usage("add|branch|cancel|find|list ...");
554 }
555
--- src/tag.c
+++ src/tag.c
@@ -246,76 +246,20 @@
246 db_begin_transaction();
247 tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
248 db_end_transaction(0);
249 }
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251 /*
252 ** Add a control record to the repository that either creates
253 ** or cancels a tag.
254 */
255 static void tag_add_artifact(
256 const char *zPrefix, /* Prefix to prepend to tag name */
257 const char *zTagname, /* The tag to add or cancel */
258 const char *zObjName, /* Name of object attached to */
259 const char *zValue, /* Value for the tag. Might be NULL */
260 int tagtype /* 0:cancel 1:singleton 2:propagated */
 
 
261 ){
262 int rid;
263 int nrid;
264 char *zDate;
265 Blob uuid;
@@ -331,26 +275,25 @@
275 return;
276 }
277 rid = name_to_rid(blob_str(&uuid));
278 blob_zero(&ctrl);
279
280 #if 0
281 if( validate16(zTagname, strlen(zTagname)) ){
282 fossil_fatal(
283 "invalid tag name \"%s\" - might be confused with"
284 " a hexadecimal artifact ID",
285 zTagname
286 );
287 }
288 #endif
289 zDate = db_text(0, "SELECT datetime('now')");
290 zDate[10] = 'T';
291 blob_appendf(&ctrl, "D %s\n", zDate);
292 blob_appendf(&ctrl, "T %c%s%F %b",
293 zTagtype[tagtype], zPrefix, zTagname, &uuid);
294 if( tagtype>0 && zValue && zValue[0] ){
 
 
295 blob_appendf(&ctrl, " %F\n", zValue);
296 }else{
297 blob_appendf(&ctrl, "\n");
298 }
299 blob_appendf(&ctrl, "U %F\n", g.zLogin);
@@ -369,43 +312,30 @@
312 ** COMMAND: tag
313 ** Usage: %fossil tag SUBCOMMAND ...
314 **
315 ** Run various subcommands to control tags and properties
316 **
317 ** %fossil tag add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?
318 **
319 ** Add a new tag or property to CHECK-IN. The tag will
320 ** be usable instead of a CHECK-IN in commands such as
321 ** update and merge. If the --propagate flag is present,
322 ** the tag value propages to all descendants of CHECK-IN
323 **
324 ** %fossil tag cancel ?--raw? TAGNAME CHECK-IN
325 **
326 ** Remove the tag TAGNAME from CHECK-IN, and also remove
 
 
 
 
 
 
 
 
 
 
 
 
 
327 ** the propagation of the tag to any descendants.
328 **
329 ** %fossil tag find ?--raw? TAGNAME
330 **
331 ** List all check-ins that use TAGNAME
332 **
333 ** %fossil tag list ?--raw? ?CHECK-IN?
334 **
335 ** List all tags, or if CHECK-IN is supplied, list
336 ** all tags and their values for CHECK-IN.
337 **
338 ** The option --raw allows the manipulation of all types of tags
339 ** used for various internal purposes in fossil. It also shows
340 ** "cancel" tags for the "find" and "list" subcommands. You should
341 ** not use this option to make changes unless you are sure what
@@ -425,15 +355,13 @@
355 ** will assume that "decaf" is a tag/branch name.
356 **
357 */
358 void tag_cmd(void){
359 int n;
360 int fRaw = find_option("raw","",0)!=0;
361 int fPropagate = find_option("propagate","",0)!=0;
362 const char *zPrefix = fRaw ? "" : "sym-";
 
 
363
364 db_find_and_open_repository(1);
365 if( g.argc<3 ){
366 goto tag_cmd_usage;
367 }
@@ -440,78 +368,83 @@
368 n = strlen(g.argv[2]);
369 if( n==0 ){
370 goto tag_cmd_usage;
371 }
372
 
 
373 if( strncmp(g.argv[2],"add",n)==0 ){
374 char *zValue;
375 if( g.argc!=5 && g.argc!=6 ){
376 usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?");
377 }
 
378 zValue = g.argc==6 ? g.argv[5] : 0;
379 tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, 1+fPropagate);
380 }else
381
382 if( strncmp(g.argv[2],"branch",n)==0 ){
383 fossil_fatal("the \"fossil tag branch\" command is discontinued\n"
384 "Use the \"fossil branch new\" command instead.");
 
 
 
 
 
 
 
 
 
 
385 }else
386
387 if( strncmp(g.argv[2],"cancel",n)==0 ){
388 if( g.argc!=5 ){
389 usage("cancel ?--raw? TAGNAME CHECK-IN");
390 }
391 tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0);
 
392 }else
393
394 if( strncmp(g.argv[2],"find",n)==0 ){
395 Stmt q;
396 if( g.argc!=4 ){
397 usage("find ?--raw? TAGNAME");
398 }
399 if( fRaw ){
400 db_prepare(&q,
401 "SELECT blob.uuid FROM tagxref, blob"
402 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
403 " AND tagxref.tagtype>0"
404 " AND blob.rid=tagxref.rid",
405 g.argv[3]
406 );
407 while( db_step(&q)==SQLITE_ROW ){
408 printf("%s\n", db_column_text(&q, 0));
409 }
410 db_finalize(&q);
411 }else{
412 int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
413 g.argv[3]);
414 if( tagid>0 ){
415 db_prepare(&q,
416 "%s"
417 " AND blob.rid IN ("
418 " SELECT rid FROM tagxref"
419 " WHERE tagtype>0 AND tagid=%d"
420 ")"
421 " ORDER BY event.mtime DESC",
422 timeline_query_for_tty(), tagid
423 );
424 print_timeline(&q, 2000);
425 db_finalize(&q);
426 }
427 }
428 }else
429
430 if( strncmp(g.argv[2],"list",n)==0 ){
431 Stmt q;
432 if( g.argc==3 ){
433 db_prepare(&q,
434 "SELECT tagname FROM tag"
435 " WHERE EXISTS(SELECT 1 FROM tagxref"
436 " WHERE tagid=tag.tagid"
437 " AND tagtype>0)"
438 " ORDER BY tagname"
 
439 );
440 while( db_step(&q)==SQLITE_ROW ){
441 const char *zName = db_column_text(&q, 0);
442 if( fRaw ){
443 printf("%s\n", zName);
444 }else if( strncmp(zName, "sym-", 4)==0 ){
445 printf("%s\n", &zName[4]);
446 }
447 }
448 db_finalize(&q);
449 }else if( g.argc==4 ){
450 int rid = name_to_rid(g.argv[3]);
@@ -519,36 +452,35 @@
452 "SELECT tagname, value FROM tagxref, tag"
453 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
454 " AND tagtype>%d"
455 " ORDER BY tagname",
456 rid,
457 fRaw ? -1 : 0
458 );
459 while( db_step(&q)==SQLITE_ROW ){
460 const char *zName = db_column_text(&q, 0);
461 const char *zValue = db_column_text(&q, 1);
462 if( fRaw==0 ){
463 if( strncmp(zName, "sym-", 4)!=0 ) continue;
464 zName += 4;
465 }
466 if( zValue && zValue[0] ){
467 printf("%s=%s\n", zName, zValue);
468 }else{
469 printf("%s\n", zName);
 
 
470 }
471 }
472 db_finalize(&q);
473 }else{
474 usage("tag list ?CHECK-IN?");
475 }
476 }else
477 {
478 goto tag_cmd_usage;
479 }
480
481 /* Cleanup */
 
482 return;
483
484 tag_cmd_usage:
485 usage("add|cancel|find|list ...");
486 }
487
+33 -2
--- src/timeline.c
+++ src/timeline.c
@@ -73,10 +73,30 @@
7373
}else{
7474
@ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
7575
}
7676
}
7777
}
78
+
79
+/*
80
+** Count the number of non-branch children for the given check-in.
81
+** A non-branch child is a child that omits the "newbranch" tag.
82
+*/
83
+int count_nonbranch_children(int pid){
84
+ int nNonBranch;
85
+
86
+ nNonBranch = db_int(0,
87
+ "SELECT count(*) FROM plink"
88
+ " WHERE pid=%d"
89
+ " AND NOT EXISTS(SELECT 1 FROM tagxref"
90
+ " WHERE tagid=%d"
91
+ " AND rid=cid"
92
+ " AND tagtype>0"
93
+ " )",
94
+ pid, TAG_NEWBRANCH
95
+ );
96
+ return nNonBranch;
97
+}
7898
7999
/*
80100
** Output a timeline in the web format given a query. The query
81101
** should return these columns:
82102
**
@@ -145,11 +165,15 @@
145165
hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
146166
if( nParent>1 ){
147167
@ <b>Merge</b>
148168
}
149169
if( nPChild>1 ){
150
- @ <b>Fork</b>
170
+ if( count_nonbranch_children(rid)>1 ){
171
+ @ <b>Fork</b>
172
+ }else{
173
+ @ <b>Branch</b>
174
+ }
151175
}
152176
if( isLeaf ){
153177
@ <b>Leaf</b>
154178
}
155179
}else{
@@ -547,10 +571,11 @@
547571
int rid = db_lget_int("checkout", 0);
548572
zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
549573
}
550574
551575
while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
576
+ int rid = db_column_int(q, 0);
552577
const char *zId = db_column_text(q, 1);
553578
const char *zDate = db_column_text(q, 2);
554579
const char *zCom = db_column_text(q, 3);
555580
int nChild = db_column_int(q, 4);
556581
int nParent = db_column_int(q, 5);
@@ -571,11 +596,17 @@
571596
if( nParent>1 ){
572597
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
573598
n = strlen(zPrefix);
574599
}
575600
if( nChild>1 ){
576
- sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
601
+ const char *zBrType;
602
+ if( count_nonbranch_children(rid)>1 ){
603
+ zBrType = "*FORK* ";
604
+ }else{
605
+ zBrType = "*BRANCH* ";
606
+ }
607
+ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType);
577608
n = strlen(zPrefix);
578609
}
579610
if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){
580611
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
581612
n += strlen(zPrefix);
582613
--- src/timeline.c
+++ src/timeline.c
@@ -73,10 +73,30 @@
73 }else{
74 @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
75 }
76 }
77 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
79 /*
80 ** Output a timeline in the web format given a query. The query
81 ** should return these columns:
82 **
@@ -145,11 +165,15 @@
145 hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
146 if( nParent>1 ){
147 @ <b>Merge</b>
148 }
149 if( nPChild>1 ){
150 @ <b>Fork</b>
 
 
 
 
151 }
152 if( isLeaf ){
153 @ <b>Leaf</b>
154 }
155 }else{
@@ -547,10 +571,11 @@
547 int rid = db_lget_int("checkout", 0);
548 zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
549 }
550
551 while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
 
552 const char *zId = db_column_text(q, 1);
553 const char *zDate = db_column_text(q, 2);
554 const char *zCom = db_column_text(q, 3);
555 int nChild = db_column_int(q, 4);
556 int nParent = db_column_int(q, 5);
@@ -571,11 +596,17 @@
571 if( nParent>1 ){
572 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
573 n = strlen(zPrefix);
574 }
575 if( nChild>1 ){
576 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
 
 
 
 
 
 
577 n = strlen(zPrefix);
578 }
579 if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){
580 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
581 n += strlen(zPrefix);
582
--- src/timeline.c
+++ src/timeline.c
@@ -73,10 +73,30 @@
73 }else{
74 @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
75 }
76 }
77 }
78
79 /*
80 ** Count the number of non-branch children for the given check-in.
81 ** A non-branch child is a child that omits the "newbranch" tag.
82 */
83 int count_nonbranch_children(int pid){
84 int nNonBranch;
85
86 nNonBranch = db_int(0,
87 "SELECT count(*) FROM plink"
88 " WHERE pid=%d"
89 " AND NOT EXISTS(SELECT 1 FROM tagxref"
90 " WHERE tagid=%d"
91 " AND rid=cid"
92 " AND tagtype>0"
93 " )",
94 pid, TAG_NEWBRANCH
95 );
96 return nNonBranch;
97 }
98
99 /*
100 ** Output a timeline in the web format given a query. The query
101 ** should return these columns:
102 **
@@ -145,11 +165,15 @@
165 hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
166 if( nParent>1 ){
167 @ <b>Merge</b>
168 }
169 if( nPChild>1 ){
170 if( count_nonbranch_children(rid)>1 ){
171 @ <b>Fork</b>
172 }else{
173 @ <b>Branch</b>
174 }
175 }
176 if( isLeaf ){
177 @ <b>Leaf</b>
178 }
179 }else{
@@ -547,10 +571,11 @@
571 int rid = db_lget_int("checkout", 0);
572 zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
573 }
574
575 while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
576 int rid = db_column_int(q, 0);
577 const char *zId = db_column_text(q, 1);
578 const char *zDate = db_column_text(q, 2);
579 const char *zCom = db_column_text(q, 3);
580 int nChild = db_column_int(q, 4);
581 int nParent = db_column_int(q, 5);
@@ -571,11 +596,17 @@
596 if( nParent>1 ){
597 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
598 n = strlen(zPrefix);
599 }
600 if( nChild>1 ){
601 const char *zBrType;
602 if( count_nonbranch_children(rid)>1 ){
603 zBrType = "*FORK* ";
604 }else{
605 zBrType = "*BRANCH* ";
606 }
607 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType);
608 n = strlen(zPrefix);
609 }
610 if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){
611 sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
612 n += strlen(zPrefix);
613
+1 -1
--- src/update.c
+++ src/update.c
@@ -90,11 +90,11 @@
9090
*/
9191
autosync(AUTOSYNC_PULL);
9292
}
9393
9494
if( tid==0 ){
95
- compute_leaves(vid);
95
+ compute_leaves(vid, 1);
9696
if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){
9797
db_prepare(&q,
9898
"%s "
9999
" AND event.objid IN leaves"
100100
" ORDER BY event.mtime DESC",
101101
--- src/update.c
+++ src/update.c
@@ -90,11 +90,11 @@
90 */
91 autosync(AUTOSYNC_PULL);
92 }
93
94 if( tid==0 ){
95 compute_leaves(vid);
96 if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){
97 db_prepare(&q,
98 "%s "
99 " AND event.objid IN leaves"
100 " ORDER BY event.mtime DESC",
101
--- src/update.c
+++ src/update.c
@@ -90,11 +90,11 @@
90 */
91 autosync(AUTOSYNC_PULL);
92 }
93
94 if( tid==0 ){
95 compute_leaves(vid, 1);
96 if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){
97 db_prepare(&q,
98 "%s "
99 " AND event.objid IN leaves"
100 " ORDER BY event.mtime DESC",
101

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button