Fossil SCM

In the "fossil purge BRANCHNAME" command, purge the entire branch, not just the last checkin of the branch.

drh 2014-11-25 04:19 UTC DBP-workflow
Commit cbe1fa51097d2ea3c974fd74566f9e12b2b44446
+52 -28
--- src/name.c
+++ src/name.c
@@ -42,10 +42,44 @@
4242
if( z[7]!='-') return 0;
4343
if( !fossil_isdigit(z[8]) ) return 0;
4444
if( !fossil_isdigit(z[9]) ) return 0;
4545
return 1;
4646
}
47
+
48
+/*
49
+** Return the RID that is the "root" of the branch that contains
50
+** check-in "rid" if inBranch==0 or the first check-in in the branch
51
+** if inBranch==1.
52
+*/
53
+int start_of_branch(int rid, int inBranch){
54
+ Stmt q;
55
+ int rc;
56
+ char *zBr;
57
+ zBr = db_text("trunk","SELECT value FROM tagxref"
58
+ " WHERE rid=%d AND tagid=%d"
59
+ " AND tagtype>0",
60
+ rid, TAG_BRANCH);
61
+ db_prepare(&q,
62
+ "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
63
+ " WHERE tagid=%d AND tagtype>0"
64
+ " AND value=%Q AND rid=plink.pid)"
65
+ " FROM plink"
66
+ " WHERE cid=:cid AND isprim",
67
+ TAG_BRANCH, zBr
68
+ );
69
+ fossil_free(zBr);
70
+ do{
71
+ db_reset(&q);
72
+ db_bind_int(&q, ":cid", rid);
73
+ rc = db_step(&q);
74
+ if( rc!=SQLITE_ROW ) break;
75
+ if( inBranch && db_column_int(&q,1)==0 ) break;
76
+ rid = db_column_int(&q, 0);
77
+ }while( db_column_int(&q, 1)==1 && rid>0 );
78
+ db_finalize(&q);
79
+ return rid;
80
+}
4781
4882
/*
4983
** Convert a symbolic name into a RID. Acceptable forms:
5084
**
5185
** * SHA1 hash
@@ -66,20 +100,28 @@
66100
** Return the RID of the matching artifact. Or return 0 if the name does not
67101
** match any known object. Or return -1 if the name is ambiguous.
68102
**
69103
** The zType parameter specifies the type of artifact: ci, t, w, e, g.
70104
** If zType is NULL or "" or "*" then any type of artifact will serve.
105
+** If zType is "br" then find the first check-in of the named branch
106
+** rather than the last.
71107
** zType is "ci" in most use cases since we are usually searching for
72108
** a check-in.
73109
*/
74110
int symbolic_name_to_rid(const char *zTag, const char *zType){
75111
int vid;
76112
int rid = 0;
77113
int nTag;
78114
int i;
115
+ int startOfBranch = 0;
79116
80
- if( zType==0 || zType[0]==0 ) zType = "*";
117
+ if( zType==0 || zType[0]==0 ){
118
+ zType = "*";
119
+ }else if( zType[0]=='b' ){
120
+ zType = "ci";
121
+ startOfBranch = 1;
122
+ }
81123
if( zTag==0 || zTag[0]==0 ) return 0;
82124
83125
/* special keyword: "tip" */
84126
if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
85127
rid = db_int(0,
@@ -151,41 +193,18 @@
151193
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
152194
" AND event.objid=tagxref.rid "
153195
" AND event.type GLOB '%q'",
154196
&zTag[4], zType
155197
);
198
+ if( startOfBranch ) rid = start_of_branch(rid,1);
156199
return rid;
157200
}
158201
159202
/* root:TAG -> The origin of the branch */
160203
if( memcmp(zTag, "root:", 5)==0 ){
161
- Stmt q;
162
- int rc;
163
- char *zBr;
164204
rid = symbolic_name_to_rid(zTag+5, zType);
165
- zBr = db_text("trunk","SELECT value FROM tagxref"
166
- " WHERE rid=%d AND tagid=%d"
167
- " AND tagtype>0",
168
- rid, TAG_BRANCH);
169
- db_prepare(&q,
170
- "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
171
- " WHERE tagid=%d AND tagtype>0"
172
- " AND value=%Q AND rid=plink.pid)"
173
- " FROM plink"
174
- " WHERE cid=:cid AND isprim",
175
- TAG_BRANCH, zBr
176
- );
177
- fossil_free(zBr);
178
- do{
179
- db_reset(&q);
180
- db_bind_int(&q, ":cid", rid);
181
- rc = db_step(&q);
182
- if( rc!=SQLITE_ROW ) break;
183
- rid = db_column_int(&q, 0);
184
- }while( db_column_int(&q, 1)==1 && rid>0 );
185
- db_finalize(&q);
186
- return rid;
205
+ return start_of_branch(rid, 0);
187206
}
188207
189208
/* symbolic-name ":" date-time */
190209
nTag = strlen(zTag);
191210
for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
@@ -245,11 +264,14 @@
245264
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
246265
" AND event.objid=tagxref.rid "
247266
" AND event.type GLOB '%q'",
248267
zTag, zType
249268
);
250
- if( rid>0 ) return rid;
269
+ if( rid>0 ){
270
+ if( startOfBranch ) rid = start_of_branch(rid,1);
271
+ return rid;
272
+ }
251273
252274
/* Undocumented: numeric tags get translated directly into the RID */
253275
if( memcmp(zTag, "rid:", 4)==0 ){
254276
zTag += 4;
255277
for(i=0; fossil_isdigit(zTag[i]); i++){}
@@ -657,21 +679,23 @@
657679
void whatis_cmd(void){
658680
int rid;
659681
const char *zName;
660682
int verboseFlag;
661683
int i;
684
+ const char *zType = 0;
662685
db_find_and_open_repository(0,0);
663686
verboseFlag = find_option("verbose","v",0)!=0;
687
+ zType = find_option("type",0,1);
664688
665689
/* We should be done with options.. */
666690
verify_all_options();
667691
668692
if( g.argc<3 ) usage("whatis NAME ...");
669693
for(i=2; i<g.argc; i++){
670694
zName = g.argv[i];
671695
if( i>2 ) fossil_print("%.79c\n",'-');
672
- rid = symbolic_name_to_rid(zName, 0);
696
+ rid = symbolic_name_to_rid(zName, zType);
673697
if( rid<0 ){
674698
Stmt q;
675699
int cnt = 0;
676700
fossil_print("name: %s (ambiguous)\n", zName);
677701
db_prepare(&q,
678702
--- src/name.c
+++ src/name.c
@@ -42,10 +42,44 @@
42 if( z[7]!='-') return 0;
43 if( !fossil_isdigit(z[8]) ) return 0;
44 if( !fossil_isdigit(z[9]) ) return 0;
45 return 1;
46 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
48 /*
49 ** Convert a symbolic name into a RID. Acceptable forms:
50 **
51 ** * SHA1 hash
@@ -66,20 +100,28 @@
66 ** Return the RID of the matching artifact. Or return 0 if the name does not
67 ** match any known object. Or return -1 if the name is ambiguous.
68 **
69 ** The zType parameter specifies the type of artifact: ci, t, w, e, g.
70 ** If zType is NULL or "" or "*" then any type of artifact will serve.
 
 
71 ** zType is "ci" in most use cases since we are usually searching for
72 ** a check-in.
73 */
74 int symbolic_name_to_rid(const char *zTag, const char *zType){
75 int vid;
76 int rid = 0;
77 int nTag;
78 int i;
 
79
80 if( zType==0 || zType[0]==0 ) zType = "*";
 
 
 
 
 
81 if( zTag==0 || zTag[0]==0 ) return 0;
82
83 /* special keyword: "tip" */
84 if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
85 rid = db_int(0,
@@ -151,41 +193,18 @@
151 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
152 " AND event.objid=tagxref.rid "
153 " AND event.type GLOB '%q'",
154 &zTag[4], zType
155 );
 
156 return rid;
157 }
158
159 /* root:TAG -> The origin of the branch */
160 if( memcmp(zTag, "root:", 5)==0 ){
161 Stmt q;
162 int rc;
163 char *zBr;
164 rid = symbolic_name_to_rid(zTag+5, zType);
165 zBr = db_text("trunk","SELECT value FROM tagxref"
166 " WHERE rid=%d AND tagid=%d"
167 " AND tagtype>0",
168 rid, TAG_BRANCH);
169 db_prepare(&q,
170 "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
171 " WHERE tagid=%d AND tagtype>0"
172 " AND value=%Q AND rid=plink.pid)"
173 " FROM plink"
174 " WHERE cid=:cid AND isprim",
175 TAG_BRANCH, zBr
176 );
177 fossil_free(zBr);
178 do{
179 db_reset(&q);
180 db_bind_int(&q, ":cid", rid);
181 rc = db_step(&q);
182 if( rc!=SQLITE_ROW ) break;
183 rid = db_column_int(&q, 0);
184 }while( db_column_int(&q, 1)==1 && rid>0 );
185 db_finalize(&q);
186 return rid;
187 }
188
189 /* symbolic-name ":" date-time */
190 nTag = strlen(zTag);
191 for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
@@ -245,11 +264,14 @@
245 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
246 " AND event.objid=tagxref.rid "
247 " AND event.type GLOB '%q'",
248 zTag, zType
249 );
250 if( rid>0 ) return rid;
 
 
 
251
252 /* Undocumented: numeric tags get translated directly into the RID */
253 if( memcmp(zTag, "rid:", 4)==0 ){
254 zTag += 4;
255 for(i=0; fossil_isdigit(zTag[i]); i++){}
@@ -657,21 +679,23 @@
657 void whatis_cmd(void){
658 int rid;
659 const char *zName;
660 int verboseFlag;
661 int i;
 
662 db_find_and_open_repository(0,0);
663 verboseFlag = find_option("verbose","v",0)!=0;
 
664
665 /* We should be done with options.. */
666 verify_all_options();
667
668 if( g.argc<3 ) usage("whatis NAME ...");
669 for(i=2; i<g.argc; i++){
670 zName = g.argv[i];
671 if( i>2 ) fossil_print("%.79c\n",'-');
672 rid = symbolic_name_to_rid(zName, 0);
673 if( rid<0 ){
674 Stmt q;
675 int cnt = 0;
676 fossil_print("name: %s (ambiguous)\n", zName);
677 db_prepare(&q,
678
--- src/name.c
+++ src/name.c
@@ -42,10 +42,44 @@
42 if( z[7]!='-') return 0;
43 if( !fossil_isdigit(z[8]) ) return 0;
44 if( !fossil_isdigit(z[9]) ) return 0;
45 return 1;
46 }
47
48 /*
49 ** Return the RID that is the "root" of the branch that contains
50 ** check-in "rid" if inBranch==0 or the first check-in in the branch
51 ** if inBranch==1.
52 */
53 int start_of_branch(int rid, int inBranch){
54 Stmt q;
55 int rc;
56 char *zBr;
57 zBr = db_text("trunk","SELECT value FROM tagxref"
58 " WHERE rid=%d AND tagid=%d"
59 " AND tagtype>0",
60 rid, TAG_BRANCH);
61 db_prepare(&q,
62 "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
63 " WHERE tagid=%d AND tagtype>0"
64 " AND value=%Q AND rid=plink.pid)"
65 " FROM plink"
66 " WHERE cid=:cid AND isprim",
67 TAG_BRANCH, zBr
68 );
69 fossil_free(zBr);
70 do{
71 db_reset(&q);
72 db_bind_int(&q, ":cid", rid);
73 rc = db_step(&q);
74 if( rc!=SQLITE_ROW ) break;
75 if( inBranch && db_column_int(&q,1)==0 ) break;
76 rid = db_column_int(&q, 0);
77 }while( db_column_int(&q, 1)==1 && rid>0 );
78 db_finalize(&q);
79 return rid;
80 }
81
82 /*
83 ** Convert a symbolic name into a RID. Acceptable forms:
84 **
85 ** * SHA1 hash
@@ -66,20 +100,28 @@
100 ** Return the RID of the matching artifact. Or return 0 if the name does not
101 ** match any known object. Or return -1 if the name is ambiguous.
102 **
103 ** The zType parameter specifies the type of artifact: ci, t, w, e, g.
104 ** If zType is NULL or "" or "*" then any type of artifact will serve.
105 ** If zType is "br" then find the first check-in of the named branch
106 ** rather than the last.
107 ** zType is "ci" in most use cases since we are usually searching for
108 ** a check-in.
109 */
110 int symbolic_name_to_rid(const char *zTag, const char *zType){
111 int vid;
112 int rid = 0;
113 int nTag;
114 int i;
115 int startOfBranch = 0;
116
117 if( zType==0 || zType[0]==0 ){
118 zType = "*";
119 }else if( zType[0]=='b' ){
120 zType = "ci";
121 startOfBranch = 1;
122 }
123 if( zTag==0 || zTag[0]==0 ) return 0;
124
125 /* special keyword: "tip" */
126 if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
127 rid = db_int(0,
@@ -151,41 +193,18 @@
193 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
194 " AND event.objid=tagxref.rid "
195 " AND event.type GLOB '%q'",
196 &zTag[4], zType
197 );
198 if( startOfBranch ) rid = start_of_branch(rid,1);
199 return rid;
200 }
201
202 /* root:TAG -> The origin of the branch */
203 if( memcmp(zTag, "root:", 5)==0 ){
 
 
 
204 rid = symbolic_name_to_rid(zTag+5, zType);
205 return start_of_branch(rid, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206 }
207
208 /* symbolic-name ":" date-time */
209 nTag = strlen(zTag);
210 for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
@@ -245,11 +264,14 @@
264 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
265 " AND event.objid=tagxref.rid "
266 " AND event.type GLOB '%q'",
267 zTag, zType
268 );
269 if( rid>0 ){
270 if( startOfBranch ) rid = start_of_branch(rid,1);
271 return rid;
272 }
273
274 /* Undocumented: numeric tags get translated directly into the RID */
275 if( memcmp(zTag, "rid:", 4)==0 ){
276 zTag += 4;
277 for(i=0; fossil_isdigit(zTag[i]); i++){}
@@ -657,21 +679,23 @@
679 void whatis_cmd(void){
680 int rid;
681 const char *zName;
682 int verboseFlag;
683 int i;
684 const char *zType = 0;
685 db_find_and_open_repository(0,0);
686 verboseFlag = find_option("verbose","v",0)!=0;
687 zType = find_option("type",0,1);
688
689 /* We should be done with options.. */
690 verify_all_options();
691
692 if( g.argc<3 ) usage("whatis NAME ...");
693 for(i=2; i<g.argc; i++){
694 zName = g.argv[i];
695 if( i>2 ) fossil_print("%.79c\n",'-');
696 rid = symbolic_name_to_rid(zName, zType);
697 if( rid<0 ){
698 Stmt q;
699 int cnt = 0;
700 fossil_print("name: %s (ambiguous)\n", zName);
701 db_prepare(&q,
702
+6 -4
--- src/purge.c
+++ src/purge.c
@@ -248,21 +248,23 @@
248248
** "graveyard" and also to show manage the graveyard and optionally restored
249249
** content into the repository from the graveyard.
250250
**
251251
** fossil purge list|ls [-l]
252252
**
253
-** Show the graveyard of prior purges.
253
+** Show the graveyard of prior purges. The -l option gives more
254
+** detail in the output.
254255
**
255256
** fossil purge undo ID
256257
**
257258
** Restore the content previously removed by purge ID.
258259
**
259260
** fossil purge [checkin] TAGS... [--explain]
260261
**
261262
** Move the checkins identified by TAGS and all of their descendants
262
-** out of the repository and into the graveyard. If the --explain option
263
-** is included, then the repository and graveyard are unchanged and
263
+** out of the repository and into the graveyard. If a TAG is a branch
264
+ name then it means all the checkins on that branch. If the --explain
265
+ option appears, then the repository and graveyard are unchanged and
264266
** an explaination of what would have happened is shown instead.
265267
**
266268
** SUMMARY:
267269
** fossil purge [checkin] TAGS... [--explain]
268270
** fossil purge list
@@ -302,11 +304,11 @@
302304
db_begin_transaction();
303305
i = strncmp(zSubcmd,"checkin",n)==0 ? 3 : 2;
304306
if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
305307
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
306308
for(; i<g.argc; i++){
307
- int r = symbolic_name_to_rid(g.argv[i], "ci");
309
+ int r = symbolic_name_to_rid(g.argv[i], "br");
308310
if( r>0 ){
309311
compute_descendants(r, 1000000000);
310312
}else if( r==0 ){
311313
fossil_fatal("not found: %s", g.argv[i]);
312314
}else{
313315
--- src/purge.c
+++ src/purge.c
@@ -248,21 +248,23 @@
248 ** "graveyard" and also to show manage the graveyard and optionally restored
249 ** content into the repository from the graveyard.
250 **
251 ** fossil purge list|ls [-l]
252 **
253 ** Show the graveyard of prior purges.
 
254 **
255 ** fossil purge undo ID
256 **
257 ** Restore the content previously removed by purge ID.
258 **
259 ** fossil purge [checkin] TAGS... [--explain]
260 **
261 ** Move the checkins identified by TAGS and all of their descendants
262 ** out of the repository and into the graveyard. If the --explain option
263 ** is included, then the repository and graveyard are unchanged and
 
264 ** an explaination of what would have happened is shown instead.
265 **
266 ** SUMMARY:
267 ** fossil purge [checkin] TAGS... [--explain]
268 ** fossil purge list
@@ -302,11 +304,11 @@
302 db_begin_transaction();
303 i = strncmp(zSubcmd,"checkin",n)==0 ? 3 : 2;
304 if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
305 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
306 for(; i<g.argc; i++){
307 int r = symbolic_name_to_rid(g.argv[i], "ci");
308 if( r>0 ){
309 compute_descendants(r, 1000000000);
310 }else if( r==0 ){
311 fossil_fatal("not found: %s", g.argv[i]);
312 }else{
313
--- src/purge.c
+++ src/purge.c
@@ -248,21 +248,23 @@
248 ** "graveyard" and also to show manage the graveyard and optionally restored
249 ** content into the repository from the graveyard.
250 **
251 ** fossil purge list|ls [-l]
252 **
253 ** Show the graveyard of prior purges. The -l option gives more
254 ** detail in the output.
255 **
256 ** fossil purge undo ID
257 **
258 ** Restore the content previously removed by purge ID.
259 **
260 ** fossil purge [checkin] TAGS... [--explain]
261 **
262 ** Move the checkins identified by TAGS and all of their descendants
263 ** out of the repository and into the graveyard. If a TAG is a branch
264 name then it means all the checkins on that branch. If the --explain
265 option appears, then the repository and graveyard are unchanged and
266 ** an explaination of what would have happened is shown instead.
267 **
268 ** SUMMARY:
269 ** fossil purge [checkin] TAGS... [--explain]
270 ** fossil purge list
@@ -302,11 +304,11 @@
304 db_begin_transaction();
305 i = strncmp(zSubcmd,"checkin",n)==0 ? 3 : 2;
306 if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
307 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
308 for(; i<g.argc; i++){
309 int r = symbolic_name_to_rid(g.argv[i], "br");
310 if( r>0 ){
311 compute_descendants(r, 1000000000);
312 }else if( r==0 ){
313 fossil_fatal("not found: %s", g.argv[i]);
314 }else{
315
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -115,10 +115,31 @@
115115
116116
The "tag:deed2" name will refer to the most recent check-in
117117
tagged with "deed2" not to the
118118
check-in whose canonical name begins with "deed2".
119119
120
+<h2>Whole Branches</h2>
121
+
122
+Usually whan a branch name is specified, it means the latest checkin on
123
+that branch. But for some commands (ex: [/help/purge|purge]) a branch name
124
+on the argument means the earliest connected checkin on the branch. This
125
+seems confusing when being explained here, but it works out to be intuitive
126
+in practice.
127
+
128
+For example, the command "fossil purge XYZ" means to purge the checkin XYZ
129
+and all of its descendents. But when XYZ is in the form of a branch name, one
130
+generally wants to purge the entire branch, not just the last checkin on the
131
+branch. And so for this reason, commands like purge will interpret a branch
132
+name to be the first checkin of the branch rather than the last. If there
133
+are two or more branches with the same name, then these commands will select
134
+the first check-in of the branch that has the most recent checkin. What
135
+happens is that Fossil searches for the most recent checkin with the given
136
+tag, just as it always does. But if that tag is a branch name, it then walks
137
+back down the branch looking for the first check-in of that branch.
138
+
139
+Again, this behavior only occurs on a few commands where it make sense.
140
+
120141
<h2>Timestamps</h2>
121142
122143
A timestamp in one of the formats shown below means the most recent
123144
check-in that occurs no later than the timestamp given:
124145
125146
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -115,10 +115,31 @@
115
116 The "tag:deed2" name will refer to the most recent check-in
117 tagged with "deed2" not to the
118 check-in whose canonical name begins with "deed2".
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120 <h2>Timestamps</h2>
121
122 A timestamp in one of the formats shown below means the most recent
123 check-in that occurs no later than the timestamp given:
124
125
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -115,10 +115,31 @@
115
116 The "tag:deed2" name will refer to the most recent check-in
117 tagged with "deed2" not to the
118 check-in whose canonical name begins with "deed2".
119
120 <h2>Whole Branches</h2>
121
122 Usually whan a branch name is specified, it means the latest checkin on
123 that branch. But for some commands (ex: [/help/purge|purge]) a branch name
124 on the argument means the earliest connected checkin on the branch. This
125 seems confusing when being explained here, but it works out to be intuitive
126 in practice.
127
128 For example, the command "fossil purge XYZ" means to purge the checkin XYZ
129 and all of its descendents. But when XYZ is in the form of a branch name, one
130 generally wants to purge the entire branch, not just the last checkin on the
131 branch. And so for this reason, commands like purge will interpret a branch
132 name to be the first checkin of the branch rather than the last. If there
133 are two or more branches with the same name, then these commands will select
134 the first check-in of the branch that has the most recent checkin. What
135 happens is that Fossil searches for the most recent checkin with the given
136 tag, just as it always does. But if that tag is a branch name, it then walks
137 back down the branch looking for the first check-in of that branch.
138
139 Again, this behavior only occurs on a few commands where it make sense.
140
141 <h2>Timestamps</h2>
142
143 A timestamp in one of the formats shown below means the most recent
144 check-in that occurs no later than the timestamp given:
145
146

Keyboard Shortcuts

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