Fossil SCM
Add the "fossil backup" command to safely make a backup copy of a repository.
Commit
aadbe015dfa56542f9338634623dbca1abb91c68c5570d2c9c871bb3bfd3a099
Parent
9d8db79c03ce14d…
2 files changed
+13
+44
+13
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -62,10 +62,13 @@ | ||
| 62 | 62 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 63 | 63 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 64 | 64 | ** |
| 65 | 65 | ** Available operations are: |
| 66 | 66 | ** |
| 67 | +** backup Backup all repositories. The argument must the name of | |
| 68 | +** a directory into which all backup repositories are written. | |
| 69 | +** | |
| 67 | 70 | ** cache Manages the cache used for potentially expensive web |
| 68 | 71 | ** pages. Any additional arguments are passed on verbatim |
| 69 | 72 | ** to the cache command. |
| 70 | 73 | ** |
| 71 | 74 | ** changes Shows all local checkouts that have uncommitted changes. |
| @@ -174,10 +177,20 @@ | ||
| 174 | 177 | return; |
| 175 | 178 | } |
| 176 | 179 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 177 | 180 | zCmd = "list"; |
| 178 | 181 | useCheckouts = find_option("ckout","c",0)!=0; |
| 182 | + }else if( strncmp(zCmd, "backup", n)==0 ){ | |
| 183 | + char *zDest; | |
| 184 | + zCmd = "backup -R"; | |
| 185 | + collect_argument(&extra, "overwrite",0); | |
| 186 | + if( g.argc!=4 ) usage("backup DIRECTORY"); | |
| 187 | + zDest = g.argv[3]; | |
| 188 | + if( file_isdir(zDest, ExtFILE)!=1 ){ | |
| 189 | + fossil_fatal("argument to \"fossil all backup\" must be a directory"); | |
| 190 | + } | |
| 191 | + blob_appendf(&extra, " %$", zDest); | |
| 179 | 192 | }else if( strncmp(zCmd, "clean", n)==0 ){ |
| 180 | 193 | zCmd = "clean --chdir"; |
| 181 | 194 | collect_argument(&extra, "allckouts",0); |
| 182 | 195 | collect_argument_value(&extra, "case-sensitive"); |
| 183 | 196 | collect_argument_value(&extra, "clean"); |
| 184 | 197 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -62,10 +62,13 @@ | |
| 62 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 63 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 64 | ** |
| 65 | ** Available operations are: |
| 66 | ** |
| 67 | ** cache Manages the cache used for potentially expensive web |
| 68 | ** pages. Any additional arguments are passed on verbatim |
| 69 | ** to the cache command. |
| 70 | ** |
| 71 | ** changes Shows all local checkouts that have uncommitted changes. |
| @@ -174,10 +177,20 @@ | |
| 174 | return; |
| 175 | } |
| 176 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 177 | zCmd = "list"; |
| 178 | useCheckouts = find_option("ckout","c",0)!=0; |
| 179 | }else if( strncmp(zCmd, "clean", n)==0 ){ |
| 180 | zCmd = "clean --chdir"; |
| 181 | collect_argument(&extra, "allckouts",0); |
| 182 | collect_argument_value(&extra, "case-sensitive"); |
| 183 | collect_argument_value(&extra, "clean"); |
| 184 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -62,10 +62,13 @@ | |
| 62 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 63 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 64 | ** |
| 65 | ** Available operations are: |
| 66 | ** |
| 67 | ** backup Backup all repositories. The argument must the name of |
| 68 | ** a directory into which all backup repositories are written. |
| 69 | ** |
| 70 | ** cache Manages the cache used for potentially expensive web |
| 71 | ** pages. Any additional arguments are passed on verbatim |
| 72 | ** to the cache command. |
| 73 | ** |
| 74 | ** changes Shows all local checkouts that have uncommitted changes. |
| @@ -174,10 +177,20 @@ | |
| 177 | return; |
| 178 | } |
| 179 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 180 | zCmd = "list"; |
| 181 | useCheckouts = find_option("ckout","c",0)!=0; |
| 182 | }else if( strncmp(zCmd, "backup", n)==0 ){ |
| 183 | char *zDest; |
| 184 | zCmd = "backup -R"; |
| 185 | collect_argument(&extra, "overwrite",0); |
| 186 | if( g.argc!=4 ) usage("backup DIRECTORY"); |
| 187 | zDest = g.argv[3]; |
| 188 | if( file_isdir(zDest, ExtFILE)!=1 ){ |
| 189 | fossil_fatal("argument to \"fossil all backup\" must be a directory"); |
| 190 | } |
| 191 | blob_appendf(&extra, " %$", zDest); |
| 192 | }else if( strncmp(zCmd, "clean", n)==0 ){ |
| 193 | zCmd = "clean --chdir"; |
| 194 | collect_argument(&extra, "allckouts",0); |
| 195 | collect_argument_value(&extra, "case-sensitive"); |
| 196 | collect_argument_value(&extra, "clean"); |
| 197 |
+44
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -387,5 +387,49 @@ | ||
| 387 | 387 | }else{ |
| 388 | 388 | url_parse(zUrl, 0); |
| 389 | 389 | fossil_print("%s\n", g.url.canonical); |
| 390 | 390 | } |
| 391 | 391 | } |
| 392 | + | |
| 393 | +/* | |
| 394 | +** COMMAND: backup* | |
| 395 | +** | |
| 396 | +** Usage: %fossil backup ?OPTIONS? FILE|DIRECTORY | |
| 397 | +** | |
| 398 | +** Make a backup of the repository into the named file or into the named | |
| 399 | +** directory. This backup is guaranteed to be consistent even if there are | |
| 400 | +** concurrent chnages taking place on the repository. In other words, it | |
| 401 | +** is safe to run "fossil backup" on a repository that is in active use. | |
| 402 | +** | |
| 403 | +** Only the main repository database is backed up by this command. The | |
| 404 | +** open checkout file (if any) is not saved. Nor is the global configuration | |
| 405 | +** database. | |
| 406 | +** | |
| 407 | +** Options: | |
| 408 | +** | |
| 409 | +** --overwrite Ok to overwrite an existing file. | |
| 410 | +** -R NAME Filename of the repository to backup | |
| 411 | +*/ | |
| 412 | +void backup_cmd(void){ | |
| 413 | + char *zDest; | |
| 414 | + int bOverwrite = 0; | |
| 415 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 416 | + bOverwrite = find_option("overwrite",0,0)!=0; | |
| 417 | + verify_all_options(); | |
| 418 | + if( g.argc!=3 ){ | |
| 419 | + usage("FILE|DIRECTORY"); | |
| 420 | + } | |
| 421 | + zDest = g.argv[2]; | |
| 422 | + if( file_isdir(zDest, ExtFILE)==1 ){ | |
| 423 | + zDest = mprintf("%s/%s", zDest, file_tail(g.zRepositoryName)); | |
| 424 | + } | |
| 425 | + if( file_isfile(zDest, ExtFILE) ){ | |
| 426 | + if( bOverwrite ){ | |
| 427 | + if( file_delete(zDest) ){ | |
| 428 | + fossil_fatal("unable to delete old copy of \"%s\"", zDest); | |
| 429 | + } | |
| 430 | + }else{ | |
| 431 | + fossil_fatal("backup \"%s\" already exists", zDest); | |
| 432 | + } | |
| 433 | + } | |
| 434 | + db_multi_exec("VACUUM repository INTO %Q", zDest); | |
| 435 | +} | |
| 392 | 436 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -387,5 +387,49 @@ | |
| 387 | }else{ |
| 388 | url_parse(zUrl, 0); |
| 389 | fossil_print("%s\n", g.url.canonical); |
| 390 | } |
| 391 | } |
| 392 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -387,5 +387,49 @@ | |
| 387 | }else{ |
| 388 | url_parse(zUrl, 0); |
| 389 | fossil_print("%s\n", g.url.canonical); |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /* |
| 394 | ** COMMAND: backup* |
| 395 | ** |
| 396 | ** Usage: %fossil backup ?OPTIONS? FILE|DIRECTORY |
| 397 | ** |
| 398 | ** Make a backup of the repository into the named file or into the named |
| 399 | ** directory. This backup is guaranteed to be consistent even if there are |
| 400 | ** concurrent chnages taking place on the repository. In other words, it |
| 401 | ** is safe to run "fossil backup" on a repository that is in active use. |
| 402 | ** |
| 403 | ** Only the main repository database is backed up by this command. The |
| 404 | ** open checkout file (if any) is not saved. Nor is the global configuration |
| 405 | ** database. |
| 406 | ** |
| 407 | ** Options: |
| 408 | ** |
| 409 | ** --overwrite Ok to overwrite an existing file. |
| 410 | ** -R NAME Filename of the repository to backup |
| 411 | */ |
| 412 | void backup_cmd(void){ |
| 413 | char *zDest; |
| 414 | int bOverwrite = 0; |
| 415 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 416 | bOverwrite = find_option("overwrite",0,0)!=0; |
| 417 | verify_all_options(); |
| 418 | if( g.argc!=3 ){ |
| 419 | usage("FILE|DIRECTORY"); |
| 420 | } |
| 421 | zDest = g.argv[2]; |
| 422 | if( file_isdir(zDest, ExtFILE)==1 ){ |
| 423 | zDest = mprintf("%s/%s", zDest, file_tail(g.zRepositoryName)); |
| 424 | } |
| 425 | if( file_isfile(zDest, ExtFILE) ){ |
| 426 | if( bOverwrite ){ |
| 427 | if( file_delete(zDest) ){ |
| 428 | fossil_fatal("unable to delete old copy of \"%s\"", zDest); |
| 429 | } |
| 430 | }else{ |
| 431 | fossil_fatal("backup \"%s\" already exists", zDest); |
| 432 | } |
| 433 | } |
| 434 | db_multi_exec("VACUUM repository INTO %Q", zDest); |
| 435 | } |
| 436 |