77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
}
db_finalize(&q);
db_set("uv-hash", sha1sum_finish(0), 0);
zHash = db_get("uv-hash",0);
}
return zHash;
}
/*
** COMMAND: unversioned
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
**
** Unversioned files (UV-files) are artifacts that are synced and are available
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
}
db_finalize(&q);
db_set("uv-hash", sha1sum_finish(0), 0);
zHash = db_get("uv-hash",0);
}
return zHash;
}
/*
** Initialize pContent to be the content of an unversioned file zName.
**
** Return 0 on success. Return 1 if zName is not found.
*/
int unversioned_content(const char *zName, Blob *pContent){
Stmt q;
int rc = 1;
blob_init(pContent, 0, 0);
db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName);
if( db_step(&q)==SQLITE_ROW ){
db_column_blob(&q, 1, pContent);
if( db_column_int(&q, 0)==1 ){
blob_uncompress(pContent, pContent);
}
rc = 0;
}
db_finalize(&q);
return rc;
}
/*
** COMMAND: unversioned
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
**
** Unversioned files (UV-files) are artifacts that are synced and are available
|
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
zAs = find_option("as",0,1);
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
verify_all_options();
db_begin_transaction();
content_rcvid_init();
db_prepare(&ins,
"REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,content)"
" VALUES(:name,:rcvid,:mtime,:hash,:sz,:content)"
);
for(i=3; i<g.argc; i++){
zIn = zAs ? zAs : g.argv[i];
if( zIn[0]==0 || zIn[0]=='/' || !file_is_simple_pathname(zIn,1) ){
fossil_fatal("'%Q' is not an acceptable filename", zIn);
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i]);
sha1sum_blob(&file, &hash);
blob_compress(&file, &compressed);
db_bind_text(&ins, ":name", zIn);
db_bind_int(&ins, ":rcvid", g.rcvid);
db_bind_int64(&ins, ":mtime", mtime);
db_bind_text(&ins, ":hash", blob_str(&hash));
db_bind_int(&ins, ":sz", blob_size(&file));
db_bind_blob(&ins, ":content", &compressed);
db_step(&ins);
db_reset(&ins);
blob_reset(&compressed);
blob_reset(&hash);
blob_reset(&file);
}
db_finalize(&ins);
db_unset("uv-hash", 0);
db_end_transaction(0);
}else if( memcmp(zCmd, "cat", nCmd)==0 ){
int i;
verify_all_options();
db_begin_transaction();
for(i=3; i<g.argc; i++){
Blob content;
blob_init(&content, 0, 0);
db_blob(&content, "SELECT content FROM unversioned WHERE name=%Q",g.argv[i]);
if( blob_size(&content)==0 ){
fossil_fatal("no such uv-file: %Q", g.argv[i]);
}
blob_uncompress(&content, &content);
blob_write_to_file(&content, "-");
blob_reset(&content);
}
db_end_transaction(0);
}else if( memcmp(zCmd, "export", nCmd)==0 ){
Blob content;
verify_all_options();
if( g.argc!=5 ) usage("export UVFILE OUTPUT");
blob_init(&content, 0, 0);
db_blob(&content, "SELECT content FROM unversioned WHERE name=%Q", g.argv[3]);
if( blob_size(&content)==0 ){
fossil_fatal("no such uv-file: %Q", g.argv[3]);
}
blob_uncompress(&content, &content);
blob_write_to_file(&content, g.argv[4]);
blob_reset(&content);
}else if( memcmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */
/* Show the hash value used during uv sync */
int debugFlag = find_option("debug",0,0)!=0;
fossil_print("%s\n", unversioned_content_hash(debugFlag));
}else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
|
|
|
>
>
|
>
>
>
>
|
<
|
<
<
<
<
<
|
<
|
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
zAs = find_option("as",0,1);
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
verify_all_options();
db_begin_transaction();
content_rcvid_init();
db_prepare(&ins,
"REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)"
" VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)"
);
for(i=3; i<g.argc; i++){
zIn = zAs ? zAs : g.argv[i];
if( zIn[0]==0 || zIn[0]=='/' || !file_is_simple_pathname(zIn,1) ){
fossil_fatal("'%Q' is not an acceptable filename", zIn);
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i]);
sha1sum_blob(&file, &hash);
blob_compress(&file, &compressed);
db_bind_text(&ins, ":name", zIn);
db_bind_int(&ins, ":rcvid", g.rcvid);
db_bind_int64(&ins, ":mtime", mtime);
db_bind_text(&ins, ":hash", blob_str(&hash));
db_bind_int(&ins, ":sz", blob_size(&file));
if( blob_size(&compressed) <= 0.8*blob_size(&file) ){
db_bind_int(&ins, ":encoding", 1);
db_bind_blob(&ins, ":content", &compressed);
}else{
db_bind_int(&ins, ":encoding", 0);
db_bind_blob(&ins, ":content", &file);
}
db_step(&ins);
db_reset(&ins);
blob_reset(&compressed);
blob_reset(&hash);
blob_reset(&file);
}
db_finalize(&ins);
db_unset("uv-hash", 0);
db_end_transaction(0);
}else if( memcmp(zCmd, "cat", nCmd)==0 ){
int i;
verify_all_options();
db_begin_transaction();
for(i=3; i<g.argc; i++){
Blob content;
if( unversioned_content(g.argv[i], &content)==0 ){
blob_write_to_file(&content, "-");
}
blob_reset(&content);
}
db_end_transaction(0);
}else if( memcmp(zCmd, "export", nCmd)==0 ){
Blob content;
verify_all_options();
if( g.argc!=5 ) usage("export UVFILE OUTPUT");
if( unversioned_content(g.argv[3], &content) ){
fossil_fatal("no such uv-file: %Q", g.argv[3]);
}
blob_write_to_file(&content, g.argv[4]);
blob_reset(&content);
}else if( memcmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */
/* Show the hash value used during uv sync */
int debugFlag = find_option("debug",0,0)!=0;
fossil_print("%s\n", unversioned_content_hash(debugFlag));
}else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
|
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
}
db_unset("uv-hash", 0);
db_end_transaction(0);
}else{
usage("add|cat|export|ls|revert|rm|sync");
}
}
#if 0
***************************************************************************
DESIGN NOTES
Web interface:
/uv/NAME
/uvctrl
/uvctrl?add=NAME&content=CONTENT
/uvctrl?rm=NAME
Sync protocol:
Client sends
pragma uvhash UVHASH
If the server support UV and if the UVHASH is different, then the
server replies with one or more of:
uvigot NAME TIMESTAMP HASH
The HASH argument is omitted from deleted uv files. The UVHASH in
the initial pragma is simply the SHA1 of the uvigot lines, each
terminated by \n, in lexicographical order.
The client examines the uvigot lines and for each difference
issues either:
uvgimme NAME
or
uvfile NAME TIMESTAMP SIZE FLAGS \n CONTENT
The client sends uvgimme if
(a) it does not possess NAME or
(b) if the NAME it holds has an earlier timestamp than TIMESTAMP or
(c) if the NAME it holds has the exact timestamp TIMESTAMP but a
lexicographically earliers HASH.
Otherwise the client sends a uvfile. The client also sends uvfile
cards for each unversioned file it holds which was not named by any
uvigot card.
On the uvfile card, the FLAGS value is an unsigned integer with
the meaning assigned to the following bits:
0x0001 Record is deleted. No CONTENT transmitted.
0x0002 CONTENT is zlib compressed. SIZE is the compressed size.
0x0004 CONTENT is oversize and is omitted. HASH sent instead. SIZE
is the uncompressed size
New FLAGS values may be added in future releases.
Internal representation:
CREATE TABLE unversioned(
name TEXT PRIMARY KEY, -- Name of the uv file
rcvid INTEGER, -- Where received from
mtime DATETIME, -- timestamp. Julian day
hash TEXT, -- Content hash
sz INTEGER, -- size of content after decompression
content BLOB -- zlib compressed content
) WITHOUT ROWID;
The hash field is NULL for deleted content. The content field is
NULL for content that is unavailable.
Other notes:
The mimetype of UV content is determine by the suffix on the
filename.
UV content can be sent to any user with read/check-out privilege 'o'.
New UV content will be accepted from users with write/check-in privilege 'i'.
The current UVHASH value can be cached in the config table under the
name of "uvhash".
Clients that are UV-aware and would like to be initialized with UV
content may send "pragma uvhash 0" upon initial clone, and the server
will include all necessary uvfile cards in its replies.
Clients or servers may send "pragma uv-size-limit SIZE" to inform the
other side that UV files larger than SIZE should be transmitted using
the "4" flag ("content omitted"). The hash is an extra parameter on
the end of a uvfile/4 card.
Clients and servers reject any UVFile with a timestamp that is too
far in the future.
***************************************************************************
#endif
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
288
289
290
291
292
293
294
|
}
db_unset("uv-hash", 0);
db_end_transaction(0);
}else{
usage("add|cat|export|ls|revert|rm|sync");
}
}
|