Fossil SCM

fossil-scm / src / sync.c
Blame History Raw 880 lines
1
/*
2
** Copyright (c) 2007 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to push, pull, and sync a repository
19
*/
20
#include "config.h"
21
#include "sync.h"
22
#include <assert.h>
23
24
/*
25
** Explain what type of sync operation is about to occur
26
*/
27
static void sync_explain(unsigned syncFlags){
28
if( (g.url.isAlias || g.url.useProxy) && (syncFlags & SYNC_QUIET)==0 ){
29
const char *url;
30
if( g.url.useProxy ){
31
url = g.url.proxyUrlCanonical;
32
}else{
33
url = g.url.canonical;
34
}
35
if( (syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
36
fossil_print("Sync with %s\n", url);
37
}else if( syncFlags & SYNC_PUSH ){
38
fossil_print("Push to %s\n", url);
39
}else if( syncFlags & SYNC_PULL ){
40
fossil_print("Pull from %s\n", url);
41
}else if( syncFlags & SYNC_PING ){
42
fossil_print("Ping %s\n", url);
43
}
44
}
45
}
46
47
48
/*
49
** Call client_sync() one or more times in order to complete a
50
** sync operation. Usually, client_sync() is called only once, though
51
** is can be called multiple times if the SYNC_ALLURL flags is set.
52
*/
53
static int client_sync_all_urls(
54
unsigned syncFlags, /* Mask of SYNC_* flags */
55
unsigned configRcvMask, /* Receive these configuration items */
56
unsigned configSendMask, /* Send these configuration items */
57
const char *zAltPCode /* Alternative project code (usually NULL) */
58
){
59
int nErr = 0; /* Number of errors seen */
60
int nOther; /* Number of extra remote URLs */
61
char **azOther; /* Text of extra remote URLs */
62
int i; /* Loop counter */
63
int iEnd; /* Loop termination point */
64
int nextIEnd; /* Loop termination point for next pass */
65
int iPass; /* Which pass through the remotes. 0 or 1 */
66
int nPass; /* Number of passes to make. 1 or 2 */
67
Stmt q; /* An SQL statement */
68
UrlData baseUrl; /* Saved parse of the default remote */
69
70
sync_explain(syncFlags);
71
if( (syncFlags & SYNC_ALLURL)==0 ){
72
/* Common-case: Only sync with the remote identified by g.url */
73
nErr = client_sync(syncFlags, configRcvMask, configSendMask, zAltPCode, 0);
74
if( nErr==0 ) url_remember();
75
return nErr;
76
}
77
78
/* If we reach this point, it means we want to sync with all remotes */
79
memset(&baseUrl, 0, sizeof(baseUrl));
80
url_move_parse(&baseUrl, &g.url);
81
nOther = 0;
82
azOther = 0;
83
db_prepare(&q,
84
"SELECT substr(name,10) FROM config"
85
" WHERE name glob 'sync-url:*'"
86
" AND value<>(SELECT value FROM config WHERE name='last-sync-url')"
87
);
88
while( db_step(&q)==SQLITE_ROW ){
89
const char *zUrl = db_column_text(&q, 0);
90
azOther = fossil_realloc(azOther, sizeof(*azOther)*(nOther+1));
91
azOther[nOther++] = fossil_strdup(zUrl);
92
}
93
db_finalize(&q);
94
iEnd = nOther+1;
95
nextIEnd = 0;
96
nPass = 1 + ((syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL));
97
for(iPass=0; iPass<nPass; iPass++){
98
for(i=0; i<iEnd; i++){
99
int rc;
100
int nRcvd;
101
if( i==0 ){
102
url_move_parse(&g.url, &baseUrl); /* Load canonical URL */
103
}else{
104
/* Load an auxiliary remote URL */
105
url_parse(azOther[i-1],
106
URL_PROMPT_PW|URL_ASK_REMEMBER_PW|URL_USE_CONFIG);
107
}
108
if( i>0 || iPass>0 ) sync_explain(syncFlags);
109
rc = client_sync(syncFlags, configRcvMask, configSendMask,
110
zAltPCode, &nRcvd);
111
if( nRcvd>0 ){
112
/* If new artifacts were received, we want to repeat all prior
113
** remotes on the second pass */
114
nextIEnd = i;
115
}
116
nErr += rc;
117
if( rc==0 && iPass==0 ){
118
if( i==0 ){
119
url_remember();
120
}else if( (g.url.flags & URL_REMEMBER_PW)!=0 ){
121
char *zKey = mprintf("sync-pw:%s", azOther[i-1]);
122
char *zPw = obscure(g.url.passwd);
123
if( zPw && zPw[0] ){
124
db_set(zKey/*works-like:""*/, zPw, 0);
125
}
126
fossil_free(zPw);
127
fossil_free(zKey);
128
}
129
}
130
if( i==0 ){
131
url_move_parse(&baseUrl, &g.url); /* Don't forget canonical URL */
132
}else{
133
url_unparse(&g.url); /* Delete auxiliary URL parses */
134
}
135
}
136
iEnd = nextIEnd;
137
}
138
for(i=0; i<nOther; i++){
139
fossil_free(azOther[i]);
140
azOther[i] = 0;
141
}
142
fossil_free(azOther);
143
url_move_parse(&g.url, &baseUrl); /* Restore the canonical URL parse */
144
return nErr;
145
}
146
147
148
/*
149
** If the repository is configured for autosyncing, then do an
150
** autosync. Bits of the "flags" parameter determine details of behavior:
151
**
152
** SYNC_PULL Pull content from the server to the local repo
153
** SYNC_PUSH Push content from local up to the server
154
** SYNC_CKIN_LOCK Take a check-in lock on the current check-out.
155
** SYNC_VERBOSE Extra output
156
**
157
** Return the number of errors.
158
**
159
** The autosync setting can be a boolean or "pullonly". No autosync
160
** is attempted if the autosync setting is off, and only auto-pull is
161
** attempted if autosync is set to "pullonly". The check-in lock is
162
** not acquired unless autosync is set to "on".
163
**
164
** If dont-push setting is true, that is the same as having autosync
165
** set to pullonly.
166
*/
167
static int autosync(int flags, const char *zSubsys){
168
const char *zAutosync;
169
int rc;
170
int configSync = 0; /* configuration changes transferred */
171
if( g.fNoSync ){
172
return 0;
173
}
174
zAutosync = db_get_for_subsystem("autosync", zSubsys);
175
if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */
176
if( is_false(zAutosync) ) return 0;
177
if( db_get_boolean("dont-push",0)
178
|| sqlite3_strglob("*pull*", zAutosync)==0
179
){
180
flags &= ~SYNC_CKIN_LOCK;
181
if( flags & SYNC_PUSH ) return 0;
182
}
183
if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
184
url_parse(0, URL_REMEMBER|URL_USE_CONFIG);
185
if( g.url.protocol==0 ) return 0;
186
if( g.url.user!=0 && g.url.passwd==0 ){
187
g.url.passwd = unobscure(db_get("last-sync-pw", 0));
188
g.url.flags |= URL_PROMPT_PW;
189
url_prompt_for_password();
190
}
191
g.zHttpAuth = get_httpauth();
192
if( sqlite3_strglob("*all*", zAutosync)==0 ){
193
rc = client_sync_all_urls(flags|SYNC_ALLURL, configSync, 0, 0);
194
}else{
195
url_remember();
196
sync_explain(flags);
197
url_enable_proxy("via proxy: ");
198
rc = client_sync(flags, configSync, 0, 0, 0);
199
}
200
return rc;
201
}
202
203
/*
204
** This routine will try a number of times to perform autosync with a
205
** 0.5 second sleep between attempts. The number of attempts is determined
206
** by the "autosync-tries" setting, which defaults to 1.
207
**
208
** Return zero on success and non-zero on a failure. If failure occurs
209
** and doPrompt flag is true, ask the user if they want to continue, and
210
** if they answer "yes" then return zero in spite of the failure.
211
*/
212
int autosync_loop(int flags, int doPrompt, const char *zSubsystem){
213
int n = 0;
214
int rc = 0;
215
int nTries = db_get_int("autosync-tries", 1);
216
if( (flags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
217
&& db_get_boolean("uv-sync",0)
218
){
219
flags |= SYNC_UNVERSIONED;
220
}
221
if( nTries<1 ) nTries = 1;
222
while( (n==0 || n<nTries) && (rc=autosync(flags, zSubsystem)) ){
223
if( rc ){
224
if( ++n<nTries ){
225
fossil_warning("Autosync failed, making another attempt.");
226
sqlite3_sleep(500);
227
}else{
228
fossil_warning("Autosync failed.");
229
}
230
}
231
}
232
if( rc && doPrompt ){
233
Blob ans;
234
char cReply;
235
prompt_user("continue in spite of sync failure (y/N)? ", &ans);
236
cReply = blob_str(&ans)[0];
237
if( cReply=='y' || cReply=='Y' ) rc = 0;
238
blob_reset(&ans);
239
}
240
return rc;
241
}
242
243
/*
244
** This routine processes the command-line argument for push, pull,
245
** and sync. If a command-line argument is given, that is the URL
246
** of a server to sync against. If no argument is given, use the
247
** most recently synced URL. Remember the current URL for next time.
248
*/
249
static void process_sync_args(
250
unsigned *pConfigFlags, /* Write configuration flags here */
251
unsigned *pSyncFlags, /* Write sync flags here */
252
int uvOnly, /* Special handling flags for UV sync */
253
unsigned urlOmitFlags /* Omit these URL flags */
254
){
255
const char *zUrl = 0;
256
const char *zHttpAuth = 0;
257
unsigned configSync = 0;
258
unsigned urlFlags = URL_REMEMBER | URL_PROMPT_PW;
259
int urlOptional = 0;
260
if( find_option("autourl",0,0)!=0 ){
261
urlOptional = 1;
262
urlFlags = 0;
263
}
264
zHttpAuth = find_option("httpauth","B",1);
265
if( find_option("once",0,0)!=0 ) urlFlags &= ~URL_REMEMBER;
266
if( (*pSyncFlags) & SYNC_FROMPARENT ) urlFlags |= URL_USE_PARENT;
267
if( !uvOnly ){
268
if( find_option("private",0,0)!=0 ){
269
*pSyncFlags |= SYNC_PRIVATE;
270
}
271
/* The --verily option to sync, push, and pull forces extra igot cards
272
** to be exchanged. This can overcome malfunctions in the sync protocol.
273
*/
274
if( find_option("verily",0,0)!=0 ){
275
*pSyncFlags |= SYNC_RESYNC;
276
}
277
}
278
if( find_option("private",0,0)!=0 ){
279
*pSyncFlags |= SYNC_PRIVATE;
280
}
281
if( find_option("verbose","v",0)!=0 ){
282
*pSyncFlags |= SYNC_VERBOSE;
283
if( find_option("verbose","v",0)!=0 ){
284
*pSyncFlags |= SYNC_XVERBOSE;
285
}
286
}
287
if( find_option("no-http-compression",0,0)!=0 ){
288
*pSyncFlags |= SYNC_NOHTTPCOMPRESS;
289
}
290
if( find_option("all",0,0)!=0 ){
291
*pSyncFlags |= SYNC_ALLURL;
292
}
293
294
/* Undocumented option to cause links transitive links to other
295
** repositories to be shared */
296
if( ((*pSyncFlags) & SYNC_PULL)!=0
297
&& find_option("share-links",0,0)!=0
298
){
299
*pSyncFlags |= SYNC_SHARE_LINKS;
300
}
301
302
/* Option: --transport-command COMMAND
303
**
304
** Causes COMMAND to be run with three argumentcense (also
305
** known as the "2-Clause License" or "FreeBSD License".)
306
307
** This program is distributed in the hope that it will be useful,
308
** but without any warranty; without even the implied warranty of
309
** merchantability or fitness for a particular purpose.
310
**
311
** Author contact information:
312
** [email protected]
313
** http://www.hwaci.com/drh/
314
**
315
*******************************************************************************
316
**
317
** This file contains code used to push, pull, and sync a repository
318
*/
319
#include "config.h"
320
#include "sync.h"
321
#include <assert.h>
322
323
/*
324
** Explain what type of sync operation is about to occur
325
*/
326
static void sync_explain(unsigned syncFlags){
327
if( (g.url.isAlias || g.url.useProxy) && (syncFlags & SYNC_QUIET)==0 ){
328
const char *url;
329
if( g.url.useProxy ){
330
url = g.url.proxyUrlCanonical;
331
}else{
332
url = g.url.canonical;
333
}
334
if( (syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
335
fossil_print("Sync with %s\n", url);
336
}else if( syncFlags & SYNC_PUSH ){
337
fossil_print("Push to %s\n", url);
338
}else if( syncFlags & SYNC_PULL ){
339
fossil_print("Pull from %s\n", url);
340
}else if( syncFlags & SYNC_PING ){
341
fossil_print("Ping %s\n", url);
342
}
343
}
344
}
345
346
347
/*
348
** Call client_sync() one or more times in order to complete a
349
** sync operation. Usually, client_sync() is called only once, though
350
** is can be called multiple times if the SYNC_ALLURL flags is set.
351
*/
352
static int client_sync_all_urls(
353
unsigned syncFlags, /* Mask of SYNC_* flags */
354
unsigned configRcvMask, /* Receive these configuration items */
355
unsigned configSendMask, /* Send these configuration items */
356
const char *zAltPCode /* Alternative project code (usually NULL) */
357
){
358
int nErr = 0; /* Number of errors seen */
359
int nOther; /* Number of extra remote URLs */
360
char **azOther; /* Text of extra remote URLs */
361
int i; /* Loop counter */
362
int iEnd; /* Loop termination point */
363
int nextIEnd; /* Loop termination point for ne/*
364
** Copyright the remote website
365
if( find_optiont-project",0,0)!=0 ){
366
syncFlags |= SYNC_FROMPARENT;
367
}
368
if( zAltPCode ) urlOmitFlags = URL_REMEMBER;
369
process_sync_args(&configFlags, &syncFlags, 0, urlOmitFlags);
370
371
/* We should be done with options.. */
372
--no-http-compressizAltPCode server.
373
** REPLY is sh Local SSL credpushh COMMAND should write thcense" or "FreeBSD License".)
374
375
** This program is distributed in the hope that it will be useful,
376
** but without any warranty; without even the implied warranty of
377
** merchantability or fitness for a particular purpose.
378
**
379
** Author contact information:
380
** [email protected]
381
** http://www.hwaci.com/drh/
382
**
383
*******************************************************************************
384
**
385
** This file contains code used to push, pull, and sync a repository
386
*/
387
#include "config.h"
388
#include "sync.h"
389
#include <assert.h>
390
391
/*
392
** Explain what type of sync operation is about to occur
393
*/
394
static void sync_explain(unsigned syncFlags){
395
if( (g.url.isAlias || g.url.useProxy) && (syncFlags & SYNC_QUIET)==0 ){
396
const char *url;
397
if( g.url.useProxy ){
398
url = g.url.proxyUrlCanonical;
399
}else{
400
url = g.url.canonical;
401
}
402
if( (syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
403
fossil_print("Sync with %s\n", url);
404
}else if( syncFlags & SYNC_PUSH ){
405
fossil_print("Push to %s\n", url);
406
}else if( syncFlags & SYNC_PULL ){
407
fossil_print("Pull from %s\n", url);
408
}else if( syncFlags & SYNC_PING ){
409
fossil_print("Ping %s\n", url);
410
}
411
}
412
}
413
414
415
/*
416
** Call client_sync() one or more times in order to complete a
417
** sync operation. Usually, client_sync() is called only once, though
418
** is can be called multiple times if the SYNC_ALLURL flags is set.
419
*/
420
static int client_sync_all_urls(
421
unsigned syncFlags, /* Mask of SYNC_* flags */
422
unsigned configRcvMask, /* Receive these configuration items */
423
unsigned configSendMask, /* Send these configuration items */
424
const char *zAltPCode /* Alternative project code (usually NULL) */
425
){
426
int nErr = 0; /* Number of errors seen */
427
/*
428
** Copyright ((&configFlags, &syncFlags, 0, 0);
429
430
/* We should be done with options.. */
431
verify_all_options();
432
433
if( db_get_boolean("dont-push",0) ){
434
fossil_fatal("pushing is prohibited: the 'dont-pu --no-http-compression Do 0, 0, 0);
435
}
436
437
438
/*
439
** COMMAND: sync
440
**
441
** Usage: %fossil sync ?REMOTE? ?options?
442
**
443
** Synchronize all sharable changes between the local repository and a
444
** remote repository, with the remote provided as a URL or a
445
** configured remote name (see the [[remote]] command). Sharable
446
** changes include public check-ins and edits to wiki pages, tickets,
447
** forum posts, and technical notes.
448
**
449
** If REMOTE is not specified, then the URL from the most recent clone, push,
450
** pull, remote, or sync command is used. See "fossil help clone" for
451
** details on the URL formats.
452
**
453
** Options:
454
** --all Sync with all remotes, not just the default
455
** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol,
456
** if required by the remote website
457
** --ipv4 Use only IPv4, not IPv6
458
** --no-http-compression Do not compress HTTP traffic
459
** --once Do not remember URL for subsequent syncs
460
** --ping Just verify that the server is alive
461
** --proxy PROXY Use the specified HTTP proxy
462
** --private Sync private branches too
463
** -q|--quiet Omit all output
464
** -R|--repository REPO Local repository to sync with
465
** --ssl-identity FILE Local SSL credentials, if requested by remote
466
** --ssh-command SSH Use SSH as the "ssh" command
467
** --transport-command CMD Use external command CMD to move message
468
** between the client and the server
469
** -u|--unversioned Also sync unversioned content
470
** -v|--verbose Additional (debugging) output - use twice to
471
** get network debug info
472
** --verily Exchange extra information with the remote
473
** to ensure no content is overlooked
474
**
475
** See also: [[clone]], [[pull]], [[push]], [[remote]]
476
*/
477
void sync_cmd(void){
478
unsigned configFlags = 0;
479
unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
480
if( find_option("s, tickets,
481
** forum posts, and technical notes.
482
**
483
** If REMOTE is not specified, then the URL from the most recent clone, push,
484
** pull, remote, or sync command is used. See "fossil help clone" for
485
** details on the URL formats.
486
**
487
** Options:
488
** --all Sync with all remotes, not just the default
489
** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol,
490
** if required by the remote website
491
** --ipv4 Use only IPv4, not I --no-http-compression bsequent syncs
492
** --ping Just verify that the server is alive
493
** --proxy PROXY Use the specified HTTP proxy
494
** --private Sync private branches too
495
** -q|--quiet Omit all output
496
** -R|--repository REPO Local repository to sync with
497
** --ssl-identity FILE Local SSL credentials, if requested by remote
498
** --ssh-command SSH Use SSH as the "ssh" command
499
** --transport-command CMD Use external command CMD to move message
500
** between the client and the server
501
** -u|--unversioned Also sync unversioned content
502
** -v|--verbose Additional (debugging) output - use twice to
503
** get network debug info
504
** --verily Exchange extra information with the remote
505
** to ensure no content is overlooked
506
**
507
** See also: [[clone]], [[pull]], [[push]], [[remote]]
508
*/
509
void sync_cmd(void){
510
unsigned configFlags = 0;
511
unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
512
int nErr;
513
if( find_option("unversioned","u",0)!=0 ){
514
syncFlags |= SYNC_UNVERSIONED;
515
}
516
if( find_option("ping",0,0)!=0 ){
517
syncFlags = SYNC_PING;
518
}
519
if( g.fQuiet ){
520
syncFlags |= SYNC_QUIET;
521
}
522
process_sync_args(&configFlags, &syncFlags, 0, 0);
523
524
/* We should be done with options.. */
525
verify_all_options();
526
527
if( (syncFlags & SYNC_PING)==0 ){
528
if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
529
if( (syncFlags & SYNC_PUSH)==0 ){
530
fossil_warning("pull only: the 'dont-push' option is set");
531
}
532
}
533
nErr = client_sync_all_urls(syncFlags, configFlags, 0, 0);
534
if( nErr ){ sync_errors("sync"); }
535
}
536
537
/*
538
** Handle the "fossil unversioned sync" and "fossil unversioned revert"
539
** commands.
540
*/
541
void sync_unversioned(unsigned syncFlags){
542
unsigned configFlags = 0;
543
(void)find_option("uv-noop",0,0);
544
process_sync_args(&configFlags, &syncFlags, 1, 0);
545
verify_all_options();
546
client_sync(syncFlags, 0, 0, 0, 0);
547
}
548
549
/*
550
** COMMAND: remote
551
** COMMAND: remote-url*
552
**
553
** Usage: %fossil remote ?SUBCOMMAND ...?
554
**
555
** View or modify the URLs of remote repositories used for syncing.
556
** The "default" remote is specially named by Fossil and corresponds to
557
** the URL used in the most recent "sync", "push", "pull", "clone", or
558
** similar command. As such, the default remote can be updated by
559
** Fossil with each sync command. Other named remotes are persistent.
560
**
561
** > fossil remote
562
**
563
** With no arguments, this command shows the current default remote
564
** URL. If there is no default, it shows "off".
565
**
566
** > fossil remote add NAME URL
567
**
568
** Add a new named URL. Afterwards, NAME can be used as a short
569
** symbolic name for URL in contexts where a URL is required. The
570
** URL argument can be "default" or a prior symbolic name to make
571
** a copy of an existing URL under the new NAME. The "default"
572
** remote cannot be defined with this subcommand; instead,
573
** use 'fossil remote REF' as documented below.
574
**
575
** > fossil remote config-data
576
**
577
** DEBUG USE ONLY - Show the name and value of every CONFIG table
578
** entry in the repository that is associated with the remote URL store.
579
** Passwords are obscured in the output.
580
**
581
** > fossil remote delete NAME
582
**
583
** Delete a named URL previously created by the "add" subcommand.
584
**
585
** > fossil remote hyperlink ?FILENAME? ?LINENUM? ?LINENUM?
586
**
587
** Print a URL that will access the current check-out on the remote
588
** repository. Or if the FILENAME argument is included, print the
589
** URL to access that particular file within the current check-out.
590
** If one or two linenumber arguments are provided after the filename,
591
** then the URL is for the line or range of lines specified.
592
**
593
** > fossil remote list|ls
594
**
595
** Show all remote repository URLs.
596
**
597
** > fossil remote off
598
**
599
** Forget the default URL. This disables autosync.
600
**
601
** This is a convenient way to enter "airplane mode". To enter
602
** airplane mode, first save the current default URL, then turn the
603
** default off. Perhaps like this:
604
**
605
** fossil remote add main default
606
** fossil remote off
607
**
608
** To exit airplane mode and turn autosync back on again:
609
**
610
** fossil remote main
611
**
612
** > fossil remote scrub
613
**
614
** Forget any saved passwords for remote repositories, but continue
615
** to remember the URLs themselves. You will be prompted for the
616
** password the next time it is needed.
617
**
618
** > fossil remote ui ?FILENAME? ?LINENUM? ?LINENUM?
619
**
620
** Bring up a web browser pointing at the remote repository, and
621
** specifically to the page that describes the current check-out
622
** on that remote repository. Or if FILENAME and/or LINENUM arguments
623
** are provided, to the specific file and range of lines. This
624
** command is similar to "fossil remote hyperlink" except that instead
625
** of printing the URL, it passes the URL off to the web browser.
626
**
627
** > fossil remote REF
628
**
629
** Make REF the new default URL, replacing the prior default.
630
** REF may be a URL or a NAME from a prior "add".
631
*/
632
void remote_url_cmd(void){
633
char *zUrl, *zArg;
634
int nArg;
635
int showPw;
636
db_find_and_open_repository(0, 0);
637
showPw = find_option("show-passwords",0,0)!=0;
638
639
/* We should be done with options.. */
640
verify_all_options();
641
642
/* 2021-10-25: A note about data structures.
643
**
644
** The remote URLs are stored in the CONFIG table. The URL is stored
645
** separately from the password. The password is obscured using the
646
** obscure() function.
647
**
648
** Originally, Fossil only preserved a single remote URL. That URL
649
** is stored in "last-sync-url" and the password in "last-sync-pw". The
650
** ability to have multiple remotes was added later so these names
651
** were retained for backwards compatibility. The other remotes are
652
** stored in "sync-url:NAME" and "sync-pw:NAME" where NAME is the name
653
** of the remote.
654
**
655
** The last-sync-url is called "default" for the display list.
656
**
657
** The last-sync-url might be duplicated into one of the sync-url:NAME
658
** entries. Thus, when doing a "fossil sync --all" or an autosync with
659
** autosync=all, each sync-url:NAME entry is checked to see if it is the
660
** same as last-sync-url and if it is then that entry is skipped.
661
*/
662
663
if( g.argc==2 ){
664
/* "fossil remote" with no arguments: Show the last sync URL. */
665
zUrl = db_get("last-sync-url", 0);
666
if( zUrl==0 ){
667
fossil_print("off\n");
668
}else{
669
url_parse(zUrl, 0);
670
fossil_print("%s\n", g.url.canonical);
671
}
672
return;
673
}
674
zArg = g.argv[2];
675
nArg = (int)strlen(zArg);
676
if( strcmp(zArg,"off")==0 ){
677
/* fossil remote off
678
** Forget the last-sync-URL and its password
679
*/
680
if( g.argc!=3 ) usage("off");
681
remote_delete_default:
682
db_unprotect(PROTECT_CONFIG);
683
db_multi_exec(
684
"DELETE FROM config WHERE name GLOB 'last-sync-*';"
685
);
686
db_protect_pop();
687
return;
688
}
689
if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){
690
Stmt q;
691
if( g.argc!=3 ) usage("list");
692
db_prepare(&q,
693
"SELECT 'default', value FROM config WHERE name='last-sync-url'"
694
" UNION ALL "
695
"SELECT substr(name,10), value FROM config"
696
" WHERE name GLOB 'sync-url:*'"
697
" ORDER BY 1"
698
);
699
while( db_step(&q)==SQLITE_ROW ){
700
fossil_print("%-18s %s\n", db_column_text(&q,0), db_column_text(&q,1));
701
}
702
db_finalize(&q);
703
return;
704
}
705
if( strcmp(zArg, "add")==0 ){
706
char *zName;
707
char *zUrl;
708
UrlData x;
709
if( g.argc!=5 ) usage("add NAME URL");
710
memset(&x, 0, sizeof(x));
711
zName = g.argv[3];
712
zUrl = g.argv[4];
713
if( strcmp(zName,"default")==0 ){
714
fossil_fatal("update the \"default\" remote-url with 'fossil remote REF'"
715
"\nsee 'fossil help remote' for complete usage information");
716
}
717
db_begin_write();
718
if( fossil_strcmp(zUrl,"default")==0 ){
719
x.canonical = db_get("last-sync-url",0);
720
x.passwd = unobscure(db_get("last-sync-pw",0));
721
}else{
722
url_parse_local(zUrl, URL_PROMPT_PW|URL_USE_CONFIG, &x);
723
}
724
db_unprotect(PROTECT_CONFIG);
725
db_multi_exec(
726
"REPLACE INTO config(name, value, mtime)"
727
" VALUES('sync-url:%q',%Q,now())",
728
zName, x.canonical
729
);
730
db_multi_exec(
731
"REPLACE INTO config(name, value, mtime)"
732
" VALUES('sync-pw:%q',obscure(%Q),now())",
733
zName, x.passwd
734
);
735
db_protect_pop();
736
db_commit_transaction();
737
return;
738
}
739
if( strncmp(zArg, "delete", nArg)==0 ){
740
char *zName;
741
if( g.argc!=4 ) usage("delete NAME");
742
zName = g.argv[3];
743
if( strcmp(zName,"default")==0 ) goto remote_delete_default;
744
db_begin_write();
745
db_unprotect(PROTECT_CONFIG);
746
db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName);
747
db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName);
748
db_protect_pop();
749
db_commit_transaction();
750
return;
751
}
752
if( strncmp(zArg, "hyperlink", nArg)==0
753
|| (nArg==2 && strcmp(zArg, "ui")==0)
754
){
755
char *zBase;
756
char *zUuid;
757
Blob fname;
758
Blob url;
759
char *zSubCmd = g.argv[2][0]=='u' ? "ui" : "hyperlink";
760
if( !db_table_exists("localdb","vvar") ){
761
fossil_fatal("the \"remote %s\" command only works from "
762
"within an open check-out", zSubCmd);
763
}
764
zUrl = db_get("last-sync-url", 0);
765
if( zUrl==0 ){
766
zUrl = "http://localhost:8080/";
767
}
768
url_parse(zUrl, 0);
769
if( g.url.isFile ){
770
url_parse("http://localhost:8080/", 0);
771
}
772
zBase = url_nouser(&g.url);
773
blob_init(&url, 0, 0);
774
if( g.argc==3 ){
775
blob_appendf(&url, "%s/info/%!S",
776
zBase,
777
db_text("???",
778
"SELECT uuid FROM blob, vvar"
779
" WHERE blob.rid=0+vvar.value"
780
" AND vvar.name='checkout';"
781
));
782
}else{
783
blob_init(&fname, 0, 0);
784
file_tree_name(g.argv[3], &fname, 0, 1);
785
zUuid = db_text(0,
786
"SELECT uuid FROM files_of_checkin"
787
" WHERE checkinID=(SELECT value FROM vvar WHERE name='checkout')"
788
" AND filename=%Q",
789
blob_str(&fname)
790
);
791
if( zUuid==0 ){
792
fossil_fatal("not a managed file: \"%s\"", g.argv[3]);
793
}
794
blob_appendf(&url, "%s/info/%S",zBase,zUuid);
795
if( g.argc>4 ){
796
int ln1 = atoi(g.argv[4]);
797
if( ln1<=0 || sqlite3_strglob("*[^0-9]*",g.argv[4])==0 ){
798
fossil_fatal("\"%s\" is not a valid line number", g.argv[4]);
799
}
800
if( g.argc>5 ){
801
int ln2 = atoi(g.argv[5]);
802
if( ln2==0 || sqlite3_strglob("*[^0-9]*",g.argv[5])==0 ){
803
fossil_fatal("\"%s\" is not a valid line number", g.argv[5]);
804
}
805
if( ln2<=ln1 ){
806
fossil_fatal("second line number should be greater than the first");
807
}
808
blob_appendf(&url,"?ln=%d,%d", ln1, ln2);
809
}else{
810
blob_appendf(&url,"?ln=%d", ln1);
811
}
812
}
813
if( g.argc>6 ){
814
usage(mprintf("%s ?FILENAME? ?LINENUMBER? ?LINENUMBER?", zSubCmd));
815
}
816
}
817
if( g.argv[2][0]=='u' ){
818
char *zCmd;
819
zCmd = mprintf("%s %!$ &", fossil_web_browser(), blob_str(&url));
820
fossil_system(zCmd);
821
}else{
822
fossil_print("%s\n", blob_str(&url));
823
}
824
return;
825
}
826
if( strncmp(zArg, "scrub", nArg)==0 ){
827
if( g.argc!=3 ) usage("scrub");
828
db_begin_write();
829
db_unprotect(PROTECT_CONFIG);
830
db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:*'");
831
db_multi_exec("DELETE FROM config WHERE name = 'last-sync-pw'");
832
db_protect_pop();
833
db_commit_transaction();
834
return;
835
}
836
if( strncmp(zArg, "config-data", nArg)==0 ){
837
/* Undocumented command: "fossil remote config-data [-show-passwords]"
838
**
839
** Show the CONFIG table entries that relate to remembering remote URLs
840
*/
841
Stmt q;
842
int n;
843
sqlite3_create_function(g.db, "unobscure", 1, SQLITE_UTF8, &g.db,
844
db_obscure, 0, 0);
845
n = db_int(13,
846
"SELECT max(length(name))"
847
" FROM config"
848
" WHERE name GLOB 'sync-*:*'"
849
" OR name GLOB 'last-sync-*'"
850
" OR name GLOB 'parent-project-*'"
851
);
852
db_prepare(&q,
853
"SELECT name,"
854
" CASE WHEN name NOT LIKE '%%sync-pw%%' AND name<>'parent-project-pw'"
855
" THEN value"
856
" WHEN %d THEN unobscure(value)"
857
" ELSE printf('%%.*c',length(value)/2-1,'*') END"
858
" FROM config"
859
" WHERE name GLOB 'sync-*:*'"
860
" OR name GLOB 'last-sync-*'"
861
" OR name GLOB 'parent-project-*'"
862
" ORDER BY name LIKE '%%sync-pw%%' OR name='parent-project-pw', name",
863
showPw
864
);
865
while( db_step(&q)==SQLITE_ROW ){
866
fossil_print("%-*s %s\n",
867
n, db_column_text(&q,0),
868
db_column_text(&q,1)
869
);
870
}
871
db_finalize(&q);
872
return;
873
}
874
if( sqlite3_strlike("http://%",zArg,0)==0
875
|| sqlite3_strlike("https://%",zArg,0)==0
876
|| sqlite3_strlike("ssh:%",zArg,0)==0
877
|| sqlite3_strlike("file:%",zArg,0)==0
878
|| db_exists("SELECT 1 FROM config WHERE name='sync-url:%q'",zArg)
879
){
880
db_unset(

Keyboard Shortcuts

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