shelf-cmd.c revision 362181
1/* 2 * shelf-cmd.c -- Shelving commands. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* We define this here to remove any further warnings about the usage of 25 experimental functions in this file. */ 26#define SVN_EXPERIMENTAL 27 28#include "svn_client.h" 29#include "svn_error_codes.h" 30#include "svn_error.h" 31#include "svn_hash.h" 32#include "svn_path.h" 33#include "svn_props.h" 34#include "svn_pools.h" 35#include "svn_utf.h" 36 37#include "shelf-cmd.h" 38#include "cl.h" 39 40#include "svn_private_config.h" 41#include "private/svn_sorts_private.h" 42#include "private/svn_client_private.h" 43#include "private/svn_client_shelf.h" 44 45 46/* Open the newest version of SHELF; error if no versions found. */ 47static svn_error_t * 48get_newest_version_existing(svn_client__shelf_version_t **shelf_version_p, 49 svn_client__shelf_t *shelf, 50 apr_pool_t *result_pool, 51 apr_pool_t *scratch_pool) 52{ 53 SVN_ERR(svn_client__shelf_get_newest_version(shelf_version_p, shelf, 54 result_pool, scratch_pool)); 55 if (!*shelf_version_p) 56 { 57 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 58 _("Shelf '%s': no versions found"), 59 shelf->name); 60 } 61 62 return SVN_NO_ERROR; 63} 64 65/* Fetch the next argument. */ 66static svn_error_t * 67get_next_argument(const char **arg, 68 apr_getopt_t *os, 69 apr_pool_t *result_pool, 70 apr_pool_t *scratch_pool) 71{ 72 apr_array_header_t *args; 73 74 SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool)); 75 SVN_ERR(svn_utf_cstring_to_utf8(arg, 76 APR_ARRAY_IDX(args, 0, const char *), 77 result_pool)); 78 return SVN_NO_ERROR; 79} 80 81/* Parse the remaining arguments as paths relative to a WC. 82 * 83 * TARGETS are relative to current working directory. 84 * 85 * Set *targets_by_wcroot to a hash mapping (char *)wcroot_abspath to 86 * (apr_array_header_t *)array of relpaths relative to that WC root. 87 */ 88static svn_error_t * 89targets_relative_to_wcs(apr_hash_t **targets_by_wcroot_p, 90 apr_array_header_t *targets, 91 svn_client_ctx_t *ctx, 92 apr_pool_t *result_pool, 93 apr_pool_t *scratch_pool) 94{ 95 apr_hash_t *targets_by_wcroot = apr_hash_make(result_pool); 96 int i; 97 98 /* Make each target relative to the WC root. */ 99 for (i = 0; i < targets->nelts; i++) 100 { 101 const char *target = APR_ARRAY_IDX(targets, i, const char *); 102 const char *wcroot_abspath; 103 apr_array_header_t *paths; 104 105 SVN_ERR(svn_dirent_get_absolute(&target, target, result_pool)); 106 SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, target, 107 ctx, result_pool, scratch_pool)); 108 paths = svn_hash_gets(targets_by_wcroot, wcroot_abspath); 109 if (! paths) 110 { 111 paths = apr_array_make(result_pool, 0, sizeof(char *)); 112 svn_hash_sets(targets_by_wcroot, wcroot_abspath, paths); 113 } 114 target = svn_dirent_skip_ancestor(wcroot_abspath, target); 115 116 if (target) 117 APR_ARRAY_PUSH(paths, const char *) = target; 118 } 119 *targets_by_wcroot_p = targets_by_wcroot; 120 return SVN_NO_ERROR; 121} 122 123/* Return targets relative to a WC. Error if they refer to more than one WC. */ 124static svn_error_t * 125targets_relative_to_a_wc(const char **wc_root_abspath_p, 126 apr_array_header_t **paths_p, 127 apr_getopt_t *os, 128 const apr_array_header_t *known_targets, 129 svn_client_ctx_t *ctx, 130 apr_pool_t *result_pool, 131 apr_pool_t *scratch_pool) 132{ 133 apr_array_header_t *targets; 134 apr_hash_t *targets_by_wcroot; 135 apr_hash_index_t *hi; 136 137 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 138 known_targets, 139 ctx, FALSE, result_pool)); 140 svn_opt_push_implicit_dot_target(targets, result_pool); 141 142 SVN_ERR(targets_relative_to_wcs(&targets_by_wcroot, targets, 143 ctx, result_pool, scratch_pool)); 144 if (apr_hash_count(targets_by_wcroot) != 1) 145 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, 146 _("All targets must be in the same WC")); 147 148 hi = apr_hash_first(scratch_pool, targets_by_wcroot); 149 *wc_root_abspath_p = apr_hash_this_key(hi); 150 *paths_p = apr_hash_this_val(hi); 151 return SVN_NO_ERROR; 152} 153 154/* Return a human-friendly description of DURATION. 155 */ 156static char * 157friendly_age_str(apr_time_t mtime, 158 apr_time_t time_now, 159 apr_pool_t *result_pool) 160{ 161 int minutes = (int)((time_now - mtime) / 1000000 / 60); 162 char *s; 163 164 if (minutes >= 60 * 24) 165 s = apr_psprintf(result_pool, 166 Q_("%d day ago", "%d days ago", 167 minutes / 60 / 24), 168 minutes / 60 / 24); 169 else if (minutes >= 60) 170 s = apr_psprintf(result_pool, 171 Q_("%d hour ago", "%d hours ago", 172 minutes / 60), 173 minutes / 60); 174 else 175 s = apr_psprintf(result_pool, 176 Q_("%d minute ago", "%d minutes ago", 177 minutes), 178 minutes); 179 return s; 180} 181 182/* A comparison function for svn_sort__hash(), comparing the mtime of two 183 svn_client_shelf_info_t's. */ 184static int 185compare_shelf_infos_by_mtime(const svn_sort__item_t *a, 186 const svn_sort__item_t *b) 187{ 188 svn_client__shelf_info_t *a_val = a->value; 189 svn_client__shelf_info_t *b_val = b->value; 190 191 return (a_val->mtime < b_val->mtime) 192 ? -1 : (a_val->mtime > b_val->mtime) ? 1 : 0; 193} 194 195/* Return a list of shelves sorted by their mtime, oldest first. 196 */ 197static svn_error_t * 198list_sorted_by_date(apr_array_header_t **list, 199 const char *local_abspath, 200 svn_client_ctx_t *ctx, 201 apr_pool_t *scratch_pool) 202{ 203 apr_hash_t *shelf_infos; 204 205 SVN_ERR(svn_client__shelf_list(&shelf_infos, local_abspath, 206 ctx, scratch_pool, scratch_pool)); 207 *list = svn_sort__hash(shelf_infos, 208 compare_shelf_infos_by_mtime, 209 scratch_pool); 210 return SVN_NO_ERROR; 211} 212 213/* */ 214static svn_error_t * 215stats(svn_client__shelf_t *shelf, 216 int version, 217 svn_client__shelf_version_t *shelf_version, 218 apr_time_t time_now, 219 svn_boolean_t with_logmsg, 220 apr_pool_t *scratch_pool) 221{ 222 char *age_str; 223 char *version_str; 224 apr_hash_t *paths; 225 const char *paths_str = ""; 226 227 if (! shelf_version) 228 { 229 return SVN_NO_ERROR; 230 } 231 232 age_str = friendly_age_str(shelf_version->mtime, time_now, scratch_pool); 233 if (version == shelf->max_version) 234 version_str = apr_psprintf(scratch_pool, 235 _("version %d"), version); 236 else 237 version_str = apr_psprintf(scratch_pool, 238 Q_("version %d of %d", "version %d of %d", 239 shelf->max_version), 240 version, shelf->max_version); 241 SVN_ERR(svn_client__shelf_paths_changed(&paths, shelf_version, 242 scratch_pool, scratch_pool)); 243 paths_str = apr_psprintf(scratch_pool, 244 Q_("%d path changed", "%d paths changed", 245 apr_hash_count(paths)), 246 apr_hash_count(paths)); 247 SVN_ERR(svn_cmdline_printf(scratch_pool, 248 "%-30s %s, %s, %s\n", 249 shelf->name, version_str, age_str, paths_str)); 250 251 if (with_logmsg) 252 { 253 char *log_message; 254 255 SVN_ERR(svn_client__shelf_get_log_message(&log_message, shelf, 256 scratch_pool)); 257 if (log_message) 258 { 259 SVN_ERR(svn_cmdline_printf(scratch_pool, 260 _(" %.50s\n"), 261 log_message)); 262 } 263 } 264 265 return SVN_NO_ERROR; 266} 267 268/* Display a list of shelves */ 269static svn_error_t * 270shelves_list(const char *local_abspath, 271 svn_boolean_t quiet, 272 svn_client_ctx_t *ctx, 273 apr_pool_t *scratch_pool) 274{ 275 apr_time_t time_now = apr_time_now(); 276 apr_array_header_t *list; 277 int i; 278 279 SVN_ERR(list_sorted_by_date(&list, 280 local_abspath, ctx, scratch_pool)); 281 282 for (i = 0; i < list->nelts; i++) 283 { 284 const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t); 285 const char *name = item->key; 286 svn_client__shelf_t *shelf; 287 svn_client__shelf_version_t *shelf_version; 288 289 SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath, 290 ctx, scratch_pool)); 291 SVN_ERR(svn_client__shelf_get_newest_version(&shelf_version, shelf, 292 scratch_pool, scratch_pool)); 293 if (quiet) 294 SVN_ERR(svn_cmdline_printf(scratch_pool, "%s\n", shelf->name)); 295 else if (!shelf_version) 296 SVN_ERR(svn_cmdline_printf(scratch_pool, "%-30s no versions\n", 297 shelf->name)); 298 else 299 SVN_ERR(stats(shelf, shelf->max_version, shelf_version, time_now, 300 TRUE /*with_logmsg*/, scratch_pool)); 301 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 302 } 303 304 return SVN_NO_ERROR; 305} 306 307/* Print info about each checkpoint of the shelf named NAME. 308 */ 309static svn_error_t * 310shelf_log(const char *name, 311 const char *local_abspath, 312 svn_client_ctx_t *ctx, 313 apr_pool_t *scratch_pool) 314{ 315 apr_time_t time_now = apr_time_now(); 316 svn_client__shelf_t *shelf; 317 apr_array_header_t *versions; 318 int i; 319 320 SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath, 321 ctx, scratch_pool)); 322 SVN_ERR(svn_client__shelf_get_all_versions(&versions, shelf, 323 scratch_pool, scratch_pool)); 324 for (i = 0; i < versions->nelts; i++) 325 { 326 svn_client__shelf_version_t *shelf_version 327 = APR_ARRAY_IDX(versions, i, void *); 328 329 SVN_ERR(stats(shelf, i + 1, shelf_version, time_now, 330 FALSE /*with_logmsg*/, scratch_pool)); 331 } 332 333 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 334 return SVN_NO_ERROR; 335} 336 337/* Find the name of the youngest shelf. 338 */ 339static svn_error_t * 340name_of_youngest(const char **name_p, 341 const char *local_abspath, 342 svn_client_ctx_t *ctx, 343 apr_pool_t *result_pool, 344 apr_pool_t *scratch_pool) 345{ 346 apr_array_header_t *list; 347 const svn_sort__item_t *youngest_item; 348 349 SVN_ERR(list_sorted_by_date(&list, 350 local_abspath, ctx, scratch_pool)); 351 if (list->nelts == 0) 352 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, 353 _("No shelves found")); 354 355 youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t); 356 *name_p = apr_pstrdup(result_pool, youngest_item->key); 357 return SVN_NO_ERROR; 358} 359 360struct status_baton 361{ 362 /* These fields correspond to the ones in the 363 svn_cl__print_status() interface. */ 364 const char *target_abspath; 365 const char *target_path; 366 367 svn_boolean_t quiet; /* don't display statuses while shelving them */ 368 int num_paths_shelved; 369 int num_paths_not_shelved; 370 svn_client_ctx_t *ctx; 371}; 372 373/* A status callback function for printing STATUS for PATH. */ 374static svn_error_t * 375print_status(void *baton, 376 const char *path, 377 const svn_client_status_t *status, 378 apr_pool_t *scratch_pool) 379{ 380 struct status_baton *sb = baton; 381 unsigned int conflicts; 382 383 return svn_cl__print_status(sb->target_abspath, sb->target_path, 384 path, status, 385 TRUE /*suppress_externals_placeholders*/, 386 FALSE /*detailed*/, 387 FALSE /*show_last_committed*/, 388 TRUE /*skip_unrecognized*/, 389 FALSE /*repos_locks*/, 390 &conflicts, &conflicts, &conflicts, 391 sb->ctx, 392 scratch_pool); 393} 394 395/* A callback function for shelved paths. */ 396static svn_error_t * 397was_shelved(void *baton, 398 const char *path, 399 const svn_client_status_t *status, 400 apr_pool_t *scratch_pool) 401{ 402 struct status_baton *sb = baton; 403 404 if (!sb->quiet) 405 { 406 SVN_ERR(print_status(baton, path, status, scratch_pool)); 407 } 408 409 ++sb->num_paths_shelved; 410 return SVN_NO_ERROR; 411} 412 413/* A callback function for not-shelved paths. */ 414static svn_error_t * 415was_not_shelved(void *baton, 416 const char *path, 417 const svn_client_status_t *status, 418 apr_pool_t *scratch_pool) 419{ 420 struct status_baton *sb = baton; 421 422 SVN_ERR(print_status(baton, path, status, scratch_pool)); 423 SVN_ERR(svn_cmdline_printf(scratch_pool, " > not shelved\n")); 424 ++sb->num_paths_not_shelved; 425 return SVN_NO_ERROR; 426} 427 428/** Shelve/save a new version of changes. 429 * 430 * Shelve in shelf @a name the local modifications found by @a paths, 431 * @a depth, @a changelists. Revert the shelved changes from the WC 432 * unless @a keep_local is true. 433 * 434 * If no local modifications are found, throw an error. 435 * 436 * If @a dry_run is true, don't actually do it. 437 * 438 * Report in @a *new_version_p the new version number (or, with dry run, 439 * what it would be). 440 */ 441static svn_error_t * 442shelve(int *new_version_p, 443 const char *name, 444 const apr_array_header_t *paths, 445 svn_depth_t depth, 446 const apr_array_header_t *changelists, 447 apr_hash_t *revprop_table, 448 svn_boolean_t keep_local, 449 svn_boolean_t dry_run, 450 svn_boolean_t quiet, 451 const char *local_abspath, 452 svn_client_ctx_t *ctx, 453 apr_pool_t *scratch_pool) 454{ 455 svn_client__shelf_t *shelf; 456 svn_client__shelf_version_t *previous_version; 457 svn_client__shelf_version_t *new_version; 458 struct status_baton sb; 459 460 SVN_ERR(svn_client__shelf_open_or_create(&shelf, 461 name, local_abspath, 462 ctx, scratch_pool)); 463 SVN_ERR(svn_client__shelf_get_newest_version(&previous_version, shelf, 464 scratch_pool, scratch_pool)); 465 466 if (! quiet) 467 { 468 SVN_ERR(svn_cmdline_printf(scratch_pool, keep_local 469 ? _("--- Save a new version of '%s' in WC root '%s'\n") 470 : _("--- Shelve '%s' in WC root '%s'\n"), 471 shelf->name, shelf->wc_root_abspath)); 472 SVN_ERR(stats(shelf, shelf->max_version, previous_version, apr_time_now(), 473 TRUE /*with_logmsg*/, scratch_pool)); 474 } 475 476 sb.target_abspath = shelf->wc_root_abspath; 477 sb.target_path = ""; 478 sb.quiet = quiet; 479 sb.num_paths_shelved = 0; 480 sb.num_paths_not_shelved = 0; 481 sb.ctx = ctx; 482 483 if (! quiet) 484 SVN_ERR(svn_cmdline_printf(scratch_pool, 485 keep_local ? _("--- Saving...\n") 486 : _("--- Shelving...\n"))); 487 SVN_ERR(svn_client__shelf_save_new_version3(&new_version, shelf, 488 paths, depth, changelists, 489 was_shelved, &sb, 490 was_not_shelved, &sb, 491 scratch_pool)); 492 if (sb.num_paths_not_shelved > 0) 493 { 494 SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, previous_version, 495 scratch_pool)); 496 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 497 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 498 Q_("%d path could not be shelved", 499 "%d paths could not be shelved", 500 sb.num_paths_not_shelved), 501 sb.num_paths_not_shelved); 502 } 503 if (sb.num_paths_shelved == 0 504 || ! new_version) 505 { 506 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 507 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 508 keep_local ? _("No local modifications could be saved") 509 : _("No local modifications could be shelved")); 510 } 511 512 /* Un-apply the changes, if required. */ 513 if (!keep_local) 514 { 515 SVN_ERR(svn_client__shelf_unapply(new_version, 516 dry_run, scratch_pool)); 517 } 518 519 /* Fetch the log message and any other revprops */ 520 if (ctx->log_msg_func3) 521 { 522 const char *tmp_file; 523 apr_array_header_t *commit_items 524 = apr_array_make(scratch_pool, 1, sizeof(void *)); 525 const char *message = ""; 526 527 SVN_ERR(ctx->log_msg_func3(&message, &tmp_file, commit_items, 528 ctx->log_msg_baton3, scratch_pool)); 529 /* Abort the shelving if the log message callback requested so. */ 530 if (! message) 531 return SVN_NO_ERROR; 532 533 if (message && !dry_run) 534 { 535 svn_string_t *propval = svn_string_create(message, scratch_pool); 536 537 if (! revprop_table) 538 revprop_table = apr_hash_make(scratch_pool); 539 svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, propval); 540 } 541 } 542 543 SVN_ERR(svn_client__shelf_revprop_set_all(shelf, revprop_table, scratch_pool)); 544 545 if (new_version_p) 546 *new_version_p = shelf->max_version; 547 548 if (dry_run) 549 { 550 SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, previous_version, 551 scratch_pool)); 552 } 553 554 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 555 return SVN_NO_ERROR; 556} 557 558/* Return the single character representation of STATUS. 559 * (Similar to subversion/svn/status.c:generate_status_code() 560 * and subversion/tests/libsvn_client/client-test.c:status_to_char().) */ 561static char 562status_to_char(enum svn_wc_status_kind status) 563{ 564 switch (status) 565 { 566 case svn_wc_status_none: return '.'; 567 case svn_wc_status_unversioned: return '?'; 568 case svn_wc_status_normal: return ' '; 569 case svn_wc_status_added: return 'A'; 570 case svn_wc_status_missing: return '!'; 571 case svn_wc_status_deleted: return 'D'; 572 case svn_wc_status_replaced: return 'R'; 573 case svn_wc_status_modified: return 'M'; 574 case svn_wc_status_merged: return 'G'; 575 case svn_wc_status_conflicted: return 'C'; 576 case svn_wc_status_ignored: return 'I'; 577 case svn_wc_status_obstructed: return '~'; 578 case svn_wc_status_external: return 'X'; 579 case svn_wc_status_incomplete: return ':'; 580 default: return '*'; 581 } 582} 583 584/* Throw an error if any path affected by SHELF_VERSION gives a conflict 585 * when applied (as a dry-run) to the WC. */ 586static svn_error_t * 587test_apply(svn_client__shelf_version_t *shelf_version, 588 svn_client_ctx_t *ctx, 589 apr_pool_t *scratch_pool) 590{ 591 apr_hash_t *paths; 592 apr_hash_index_t *hi; 593 594 SVN_ERR(svn_client__shelf_paths_changed(&paths, shelf_version, 595 scratch_pool, scratch_pool)); 596 for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi)) 597 { 598 const char *path = apr_hash_this_key(hi); 599 svn_boolean_t conflict; 600 601 SVN_ERR(svn_client__shelf_test_apply_file(&conflict, shelf_version, path, 602 scratch_pool)); 603 if (conflict) 604 { 605 char *to_wc_abspath 606 = svn_dirent_join(shelf_version->shelf->wc_root_abspath, path, 607 scratch_pool); 608 svn_wc_status3_t *status; 609 610 SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, to_wc_abspath, 611 scratch_pool, scratch_pool)); 612 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 613 _("Shelved path '%s' already has " 614 "status '%c' in the working copy"), 615 path, status_to_char(status->node_status)); 616 } 617 } 618 return SVN_NO_ERROR; 619} 620 621/** Restore/unshelve a given or newest version of changes. 622 * 623 * Restore local modifications from shelf @a name version @a arg, 624 * or the newest version is @a arg is null. 625 * 626 * If @a dry_run is true, don't actually do it. 627 * 628 * Error if any path would have a conflict, unless @a force_if_conflict. 629 */ 630static svn_error_t * 631shelf_restore(const char *name, 632 const char *arg, 633 svn_boolean_t dry_run, 634 svn_boolean_t quiet, 635 svn_boolean_t force_if_conflict, 636 const char *local_abspath, 637 svn_client_ctx_t *ctx, 638 apr_pool_t *scratch_pool) 639{ 640 int version, old_version; 641 apr_time_t time_now = apr_time_now(); 642 svn_client__shelf_t *shelf; 643 svn_client__shelf_version_t *shelf_version; 644 645 SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath, 646 ctx, scratch_pool)); 647 648 old_version = shelf->max_version; 649 if (arg) 650 { 651 SVN_ERR(svn_cstring_atoi(&version, arg)); 652 SVN_ERR(svn_client__shelf_version_open(&shelf_version, 653 shelf, version, 654 scratch_pool, scratch_pool)); 655 } 656 else 657 { 658 version = shelf->max_version; 659 SVN_ERR(get_newest_version_existing(&shelf_version, shelf, 660 scratch_pool, scratch_pool)); 661 } 662 663 if (! quiet) 664 { 665 SVN_ERR(svn_cmdline_printf(scratch_pool, 666 _("--- Unshelve '%s' in WC root '%s'\n"), 667 shelf->name, shelf->wc_root_abspath)); 668 SVN_ERR(stats(shelf, version, shelf_version, time_now, 669 TRUE /*with_logmsg*/, scratch_pool)); 670 } 671 if (! force_if_conflict) 672 { 673 SVN_ERR_W(test_apply(shelf_version, ctx, scratch_pool), 674 _("Cannot unshelve/restore, as at least one shelved " 675 "path would conflict with a local modification " 676 "or other status in the working copy")); 677 } 678 679 SVN_ERR(svn_client__shelf_apply(shelf_version, 680 dry_run, scratch_pool)); 681 682 if (! dry_run) 683 { 684 SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, shelf_version, 685 scratch_pool)); 686 } 687 688 if (!quiet) 689 { 690 if (version < old_version) 691 SVN_ERR(svn_cmdline_printf(scratch_pool, 692 Q_("restored '%s' version %d and deleted %d newer version\n", 693 "restored '%s' version %d and deleted %d newer versions\n", 694 old_version - version), 695 name, version, old_version - version)); 696 else 697 SVN_ERR(svn_cmdline_printf(scratch_pool, 698 _("restored '%s' version %d (the newest version)\n"), 699 name, version)); 700 } 701 702 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 703 return SVN_NO_ERROR; 704} 705 706static svn_error_t * 707shelf_diff(const char *name, 708 const char *arg, 709 const char *local_abspath, 710 svn_boolean_t summarize, 711 svn_depth_t depth, 712 svn_boolean_t ignore_ancestry, 713 svn_client_ctx_t *ctx, 714 apr_pool_t *scratch_pool) 715{ 716 svn_client__shelf_t *shelf; 717 svn_client__shelf_version_t *shelf_version; 718 svn_stream_t *stream, *errstream; 719 svn_diff_tree_processor_t *diff_processor; 720 721 SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath, 722 ctx, scratch_pool)); 723 724 if (arg) 725 { 726 int version; 727 728 SVN_ERR(svn_cstring_atoi(&version, arg)); 729 SVN_ERR(svn_client__shelf_version_open(&shelf_version, 730 shelf, version, 731 scratch_pool, scratch_pool)); 732 } 733 else 734 { 735 SVN_ERR(get_newest_version_existing(&shelf_version, shelf, 736 scratch_pool, scratch_pool)); 737 } 738 739 SVN_ERR(svn_stream_for_stdout(&stream, scratch_pool)); 740 errstream = svn_stream_empty(scratch_pool); 741 742 if (summarize) 743 { 744 svn_client_diff_summarize_func_t func; 745 void *baton; 746 747 SVN_ERR(svn_cl__get_diff_summary_writer(&func, &baton, 748 FALSE /*xml*/, 749 FALSE /*ignore_properties*/, 750 "" /*anchor/prefix*/, 751 scratch_pool, scratch_pool)); 752 SVN_ERR(svn_client__get_diff_summarize_callbacks(&diff_processor, 753 func, baton, 754 scratch_pool, 755 scratch_pool)); 756 } 757 else 758 { 759 SVN_ERR(svn_client__get_diff_writer_svn( 760 &diff_processor, 761 NULL /*anchor*/, 762 "", "", /*orig_path_1, orig_path_2,*/ 763 NULL /*options*/, 764 "" /*relative_to_dir*/, 765 FALSE /*no_diff_added*/, 766 FALSE /*no_diff_deleted*/, 767 FALSE /*show_copies_as_adds*/, 768 FALSE /*ignore_content_type*/, 769 FALSE /*ignore_properties*/, 770 FALSE /*properties_only*/, 771 TRUE /*pretty_print_mergeinfo*/, 772 svn_cmdline_output_encoding(scratch_pool), 773 stream, errstream, 774 ctx, scratch_pool)); 775 } 776 777 SVN_ERR(svn_client__shelf_diff(shelf_version, "", 778 depth, ignore_ancestry, 779 diff_processor, scratch_pool)); 780 SVN_ERR(svn_stream_close(stream)); 781 782 SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); 783 return SVN_NO_ERROR; 784} 785 786/* This implements the `svn_opt_subcommand_t' interface. */ 787static svn_error_t * 788shelf_drop(const char *name, 789 const char *local_abspath, 790 svn_boolean_t dry_run, 791 svn_boolean_t quiet, 792 svn_client_ctx_t *ctx, 793 apr_pool_t *scratch_pool) 794{ 795 SVN_ERR(svn_client__shelf_delete(name, local_abspath, dry_run, 796 ctx, scratch_pool)); 797 if (! quiet) 798 SVN_ERR(svn_cmdline_printf(scratch_pool, 799 _("deleted '%s'\n"), 800 name)); 801 return SVN_NO_ERROR; 802} 803 804/* */ 805static svn_error_t * 806shelf_shelve(int *new_version, 807 const char *name, 808 apr_array_header_t *targets, 809 svn_depth_t depth, 810 apr_array_header_t *changelists, 811 apr_hash_t *revprop_table, 812 svn_boolean_t keep_local, 813 svn_boolean_t dry_run, 814 svn_boolean_t quiet, 815 svn_client_ctx_t *ctx, 816 apr_pool_t *scratch_pool) 817{ 818 const char *local_abspath; 819 820 if (depth == svn_depth_unknown) 821 depth = svn_depth_infinity; 822 823 SVN_ERR(svn_cl__check_targets_are_local_paths(targets)); 824 825 SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool)); 826 827 svn_opt_push_implicit_dot_target(targets, scratch_pool); 828 829 /* ### TODO: check all paths are in same WC; for now use first path */ 830 SVN_ERR(svn_dirent_get_absolute(&local_abspath, 831 APR_ARRAY_IDX(targets, 0, char *), 832 scratch_pool)); 833 834 SVN_ERR(shelve(new_version, name, 835 targets, depth, changelists, 836 revprop_table, 837 keep_local, dry_run, quiet, 838 local_abspath, ctx, scratch_pool)); 839 840 return SVN_NO_ERROR; 841} 842 843static svn_error_t * 844svn_cl__shelf_shelve(apr_getopt_t *os, 845 void *baton, 846 apr_pool_t *pool); 847 848/* This implements the `svn_opt_subcommand_t' interface. */ 849static svn_error_t * 850svn_cl__shelf_save(apr_getopt_t *os, 851 void *baton, 852 apr_pool_t *pool) 853{ 854 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 855 856 opt_state->keep_local = TRUE; 857 SVN_ERR(svn_cl__shelf_shelve(os, baton, pool)); 858 return SVN_NO_ERROR; 859} 860 861/* This implements the `svn_opt_subcommand_t' interface. */ 862static svn_error_t * 863svn_cl__shelf_shelve(apr_getopt_t *os, 864 void *baton, 865 apr_pool_t *pool) 866{ 867 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 868 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 869 const char *name; 870 apr_array_header_t *targets; 871 872 if (opt_state->quiet) 873 ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */ 874 875 SVN_ERR(get_next_argument(&name, os, pool, pool)); 876 877 /* Parse the remaining arguments as paths. */ 878 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 879 opt_state->targets, 880 ctx, FALSE, pool)); 881 { 882 int new_version; 883 svn_error_t *err; 884 885 if (ctx->log_msg_func3) 886 SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3, 887 opt_state, NULL, ctx->config, 888 pool)); 889 err = shelf_shelve(&new_version, name, 890 targets, opt_state->depth, opt_state->changelists, 891 opt_state->revprop_table, 892 opt_state->keep_local, opt_state->dry_run, 893 opt_state->quiet, ctx, pool); 894 if (ctx->log_msg_func3) 895 SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, 896 err, pool)); 897 else 898 SVN_ERR(err); 899 900 if (! opt_state->quiet) 901 { 902 if (opt_state->keep_local) 903 SVN_ERR(svn_cmdline_printf(pool, 904 _("saved '%s' version %d\n"), 905 name, new_version)); 906 else 907 SVN_ERR(svn_cmdline_printf(pool, 908 _("shelved '%s' version %d\n"), 909 name, new_version)); 910 } 911 } 912 913 return SVN_NO_ERROR; 914} 915 916/* This implements the `svn_opt_subcommand_t' interface. */ 917static svn_error_t * 918svn_cl__shelf_unshelve(apr_getopt_t *os, 919 void *baton, 920 apr_pool_t *scratch_pool) 921{ 922 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 923 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 924 const char *local_abspath; 925 const char *name; 926 const char *arg = NULL; 927 928 SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", scratch_pool)); 929 930 if (os->ind < os->argc) 931 { 932 SVN_ERR(get_next_argument(&name, os, scratch_pool, scratch_pool)); 933 } 934 else 935 { 936 SVN_ERR(name_of_youngest(&name, 937 local_abspath, ctx, scratch_pool, scratch_pool)); 938 SVN_ERR(svn_cmdline_printf(scratch_pool, 939 _("unshelving the youngest shelf, '%s'\n"), 940 name)); 941 } 942 943 /* Which checkpoint number? */ 944 if (os->ind < os->argc) 945 SVN_ERR(get_next_argument(&arg, os, scratch_pool, scratch_pool)); 946 947 if (os->ind < os->argc) 948 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 949 _("Too many arguments")); 950 951 if (opt_state->quiet) 952 ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */ 953 954 SVN_ERR(shelf_restore(name, arg, 955 opt_state->dry_run, opt_state->quiet, 956 opt_state->force /*force_already_modified*/, 957 local_abspath, ctx, scratch_pool)); 958 959 if (opt_state->drop) 960 { 961 SVN_ERR(shelf_drop(name, local_abspath, 962 opt_state->dry_run, opt_state->quiet, 963 ctx, scratch_pool)); 964 } 965 return SVN_NO_ERROR; 966} 967 968/* This implements the `svn_opt_subcommand_t' interface. */ 969static svn_error_t * 970svn_cl__shelf_list(apr_getopt_t *os, 971 void *baton, 972 apr_pool_t *pool) 973{ 974 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 975 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 976 apr_array_header_t *targets = NULL; 977 apr_pool_t *iterpool = svn_pool_create(pool); 978 int i; 979 980 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 981 opt_state->targets, 982 ctx, FALSE, pool)); 983 /* Add "." if user passed 0 arguments */ 984 svn_opt_push_implicit_dot_target(targets, pool); 985 986 for (i = 0; i < targets->nelts; ++i) 987 { 988 const char *local_abspath; 989 const char *target = APR_ARRAY_IDX(targets, i, const char *); 990 991 svn_pool_clear(iterpool); 992 993 SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool)); 994 995 SVN_ERR(shelves_list(local_abspath, 996 opt_state->quiet, 997 ctx, iterpool)); 998 } 999 1000 svn_pool_destroy(iterpool); 1001 1002 return SVN_NO_ERROR; 1003} 1004 1005/* "svn shelf-list-by-paths [PATH...]" 1006 * 1007 * TARGET_RELPATHS are all within the same WC, relative to WC_ROOT_ABSPATH. 1008 */ 1009static svn_error_t * 1010shelf_list_by_paths(apr_array_header_t *target_relpaths, 1011 const char *wc_root_abspath, 1012 svn_client_ctx_t *ctx, 1013 apr_pool_t *scratch_pool) 1014{ 1015 apr_array_header_t *shelves; 1016 apr_hash_t *paths_to_shelf_name = apr_hash_make(scratch_pool); 1017 apr_array_header_t *array; 1018 int i; 1019 1020 SVN_ERR(list_sorted_by_date(&shelves, 1021 wc_root_abspath, ctx, scratch_pool)); 1022 1023 /* Check paths are valid */ 1024 for (i = 0; i < target_relpaths->nelts; i++) 1025 { 1026 char *target_relpath = APR_ARRAY_IDX(target_relpaths, i, char *); 1027 1028 if (svn_path_is_url(target_relpath)) 1029 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 1030 _("'%s' is not a local path"), target_relpath); 1031 SVN_ERR_ASSERT(svn_relpath_is_canonical(target_relpath)); 1032 } 1033 1034 /* Find the most recent shelf for each affected path */ 1035 for (i = 0; i < shelves->nelts; i++) 1036 { 1037 svn_sort__item_t *item = &APR_ARRAY_IDX(shelves, i, svn_sort__item_t); 1038 const char *name = item->key; 1039 svn_client__shelf_t *shelf; 1040 svn_client__shelf_version_t *shelf_version; 1041 apr_hash_t *shelf_paths; 1042 int j; 1043 1044 SVN_ERR(svn_client__shelf_open_existing(&shelf, 1045 name, wc_root_abspath, 1046 ctx, scratch_pool)); 1047 SVN_ERR(svn_client__shelf_get_newest_version(&shelf_version, shelf, 1048 scratch_pool, scratch_pool)); 1049 if (!shelf_version) 1050 continue; 1051 SVN_ERR(svn_client__shelf_paths_changed(&shelf_paths, 1052 shelf_version, 1053 scratch_pool, scratch_pool)); 1054 for (j = 0; j < target_relpaths->nelts; j++) 1055 { 1056 char *target_relpath = APR_ARRAY_IDX(target_relpaths, j, char *); 1057 apr_hash_index_t *hi; 1058 1059 for (hi = apr_hash_first(scratch_pool, shelf_paths); 1060 hi; hi = apr_hash_next(hi)) 1061 { 1062 const char *shelf_path = apr_hash_this_key(hi); 1063 1064 if (svn_relpath_skip_ancestor(target_relpath, shelf_path)) 1065 { 1066 if (! svn_hash_gets(paths_to_shelf_name, shelf_path)) 1067 { 1068 svn_hash_sets(paths_to_shelf_name, shelf_path, shelf->name); 1069 } 1070 } 1071 } 1072 } 1073 } 1074 1075 /* Print the results. */ 1076 array = svn_sort__hash(paths_to_shelf_name, 1077 svn_sort_compare_items_as_paths, 1078 scratch_pool); 1079 for (i = 0; i < array->nelts; i++) 1080 { 1081 svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); 1082 const char *path = item->key; 1083 const char *name = item->value; 1084 1085 SVN_ERR(svn_cmdline_printf(scratch_pool, "%-20.20s %s\n", 1086 name, 1087 svn_dirent_local_style(path, scratch_pool))); 1088 } 1089 return SVN_NO_ERROR; 1090} 1091 1092/* This implements the `svn_opt_subcommand_t' interface. */ 1093static svn_error_t * 1094svn_cl__shelf_list_by_paths(apr_getopt_t *os, 1095 void *baton, 1096 apr_pool_t *pool) 1097{ 1098 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 1099 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 1100 const char *wc_root_abspath; 1101 apr_array_header_t *targets; 1102 1103 /* Parse the remaining arguments as paths. */ 1104 SVN_ERR(targets_relative_to_a_wc(&wc_root_abspath, &targets, 1105 os, opt_state->targets, 1106 ctx, pool, pool)); 1107 1108 SVN_ERR(shelf_list_by_paths(targets, wc_root_abspath, ctx, pool)); 1109 return SVN_NO_ERROR; 1110} 1111 1112/* This implements the `svn_opt_subcommand_t' interface. */ 1113static svn_error_t * 1114svn_cl__shelf_diff(apr_getopt_t *os, 1115 void *baton, 1116 apr_pool_t *pool) 1117{ 1118 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 1119 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 1120 const char *local_abspath; 1121 const char *name; 1122 const char *arg = NULL; 1123 1124 SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool)); 1125 1126 SVN_ERR(get_next_argument(&name, os, pool, pool)); 1127 1128 /* Which checkpoint number? */ 1129 if (os->ind < os->argc) 1130 SVN_ERR(get_next_argument(&arg, os, pool, pool)); 1131 1132 if (os->ind < os->argc) 1133 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 1134 _("Too many arguments")); 1135 1136 SVN_ERR(shelf_diff(name, arg, local_abspath, 1137 opt_state->diff.summarize, 1138 opt_state->depth, opt_state->ignore_ancestry, 1139 ctx, pool)); 1140 1141 return SVN_NO_ERROR; 1142} 1143 1144/* This implements the `svn_opt_subcommand_t' interface. */ 1145static svn_error_t * 1146svn_cl__shelf_drop(apr_getopt_t *os, 1147 void *baton, 1148 apr_pool_t *pool) 1149{ 1150 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 1151 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 1152 const char *name; 1153 apr_array_header_t *targets = NULL; 1154 apr_pool_t *iterpool = svn_pool_create(pool); 1155 int i; 1156 1157 SVN_ERR(get_next_argument(&name, os, pool, pool)); 1158 1159 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 1160 opt_state->targets, 1161 ctx, FALSE, pool)); 1162 svn_opt_push_implicit_dot_target(targets, pool); 1163 1164 for (i = 0; i < targets->nelts; ++i) 1165 { 1166 const char *local_abspath; 1167 const char *target = APR_ARRAY_IDX(targets, i, const char *); 1168 1169 svn_pool_clear(iterpool); 1170 1171 SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool)); 1172 SVN_ERR(shelf_drop(name, local_abspath, 1173 opt_state->dry_run, opt_state->quiet, 1174 ctx, iterpool)); 1175 } 1176 1177 svn_pool_destroy(iterpool); 1178 1179 return SVN_NO_ERROR; 1180} 1181 1182/* This implements the `svn_opt_subcommand_t' interface. */ 1183static svn_error_t * 1184svn_cl__shelf_log(apr_getopt_t *os, 1185 void *baton, 1186 apr_pool_t *pool) 1187{ 1188 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 1189 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 1190 const char *name; 1191 apr_array_header_t *targets = NULL; 1192 apr_pool_t *iterpool = svn_pool_create(pool); 1193 int i; 1194 1195 SVN_ERR(get_next_argument(&name, os, pool, pool)); 1196 1197 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 1198 opt_state->targets, 1199 ctx, FALSE, pool)); 1200 svn_opt_push_implicit_dot_target(targets, pool); 1201 1202 for (i = 0; i < targets->nelts; ++i) 1203 { 1204 const char *local_abspath; 1205 const char *target = APR_ARRAY_IDX(targets, i, const char *); 1206 1207 svn_pool_clear(iterpool); 1208 1209 SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool)); 1210 SVN_ERR(shelf_log(name, local_abspath, ctx, iterpool)); 1211 } 1212 1213 svn_pool_destroy(iterpool); 1214 1215 return SVN_NO_ERROR; 1216} 1217 1218/**************************************************************************/ 1219 1220/* This implements the `svn_opt_subcommand_t' interface. */ 1221static svn_error_t * 1222svn_cl__wc_copy_mods(apr_getopt_t *os, 1223 void *baton, 1224 apr_pool_t *pool) 1225{ 1226 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 1227 const char *src_wc_abspath, *dst_wc_abspath; 1228 1229 SVN_ERR(get_next_argument(&src_wc_abspath, os, pool, pool)); 1230 SVN_ERR(svn_dirent_get_absolute(&src_wc_abspath, src_wc_abspath, pool)); 1231 1232 SVN_ERR(get_next_argument(&dst_wc_abspath, os, pool, pool)); 1233 SVN_ERR(svn_dirent_get_absolute(&dst_wc_abspath, dst_wc_abspath, pool)); 1234 1235 SVN_ERR(svn_client__wc_copy_mods(src_wc_abspath, dst_wc_abspath, 1236 ctx->notify_func2, ctx->notify_baton2, 1237 ctx, pool)); 1238 1239 return SVN_NO_ERROR; 1240} 1241 1242const svn_opt_subcommand_desc3_t 1243svn_cl__cmd_table_shelf3[] = 1244{ 1245 { "x-shelf-diff", svn_cl__shelf_diff, {0}, {N_( 1246 "Show shelved changes as a diff.\n" 1247 "usage: x-shelf-diff SHELF [VERSION]\n" 1248 "\n"), N_( 1249 " Show the changes in SHELF:VERSION (default: latest) as a diff.\n" 1250 "\n"), N_( 1251 " See also: 'svn diff --cl=svn:shelf:SHELF' which supports most options of\n" 1252 " 'svn diff'.\n" 1253 "\n"), N_( 1254 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1255 " in the next release, and there is no promise of backward compatibility.\n" 1256 )}, 1257 {opt_summarize}, 1258 }, 1259 1260 { "x-shelf-drop", svn_cl__shelf_drop, {0}, {N_( 1261 "Delete a shelf.\n" 1262 "usage: x-shelf-drop SHELF [PATH ...]\n" 1263 "\n"), N_( 1264 " Delete the shelves named SHELF from the working copies containing PATH\n" 1265 " (default PATH is '.')\n" 1266 "\n"), N_( 1267 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1268 " in the next release, and there is no promise of backward compatibility.\n" 1269 )}, 1270 }, 1271 1272 { "x-shelf-list", svn_cl__shelf_list, {"x-shelves"}, {N_( 1273 "List shelves.\n" 1274 "usage: x-shelf-list [PATH ...]\n" 1275 "\n"), N_( 1276 " List shelves for each working copy containing PATH (default is '.')\n" 1277 " Include the first line of any log message and some details about the\n" 1278 " contents of the shelf, unless '-q' is given.\n" 1279 "\n"), N_( 1280 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1281 " in the next release, and there is no promise of backward compatibility.\n" 1282 )}, 1283 {'q', 'v'} 1284 }, 1285 1286 { "x-shelf-list-by-paths", svn_cl__shelf_list_by_paths, {0}, {N_( 1287 "List which shelf affects each path.\n" 1288 "usage: x-shelf-list-by-paths [PATH...]\n" 1289 "\n"), N_( 1290 " List which shelf most recently affects each path below the given PATHs.\n" 1291 "\n"), N_( 1292 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1293 " in the next release, and there is no promise of backward compatibility.\n" 1294 )}, 1295 }, 1296 1297 { "x-shelf-log", svn_cl__shelf_log, {0}, {N_( 1298 "Show the versions of a shelf.\n" 1299 "usage: x-shelf-log SHELF [PATH...]\n" 1300 "\n"), N_( 1301 " Show all versions of SHELF for each working copy containing PATH (the\n" 1302 " default PATH is '.').\n" 1303 "\n"), N_( 1304 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1305 " in the next release, and there is no promise of backward compatibility.\n" 1306 )}, 1307 {'q', 'v'} 1308 }, 1309 1310 { "x-shelf-save", svn_cl__shelf_save, {0}, {N_( 1311 "Copy local changes onto a new version of a shelf.\n" 1312 "usage: x-shelf-save SHELF [PATH...]\n" 1313 "\n"), N_( 1314 " Save local changes in the given PATHs as a new version of SHELF.\n" 1315 " The shelf's log message can be set with -m, -F, etc.\n" 1316 "\n"), N_( 1317 " The same as 'svn shelve --keep-local'.\n" 1318 "\n"), N_( 1319 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1320 " in the next release, and there is no promise of backward compatibility.\n" 1321 )}, 1322 {'q', opt_dry_run, 1323 opt_depth, opt_targets, opt_changelist, 1324 SVN_CL__LOG_MSG_OPTIONS, 1325 } 1326 }, 1327 1328 { "x-shelve", svn_cl__shelf_shelve, {0}, {N_( 1329 "Move local changes onto a shelf.\n" 1330 "usage: x-shelve [--keep-local] SHELF [PATH...]\n" 1331 "\n"), N_( 1332 " Save the local changes in the given PATHs to a new or existing SHELF.\n" 1333 " Revert those changes from the WC unless '--keep-local' is given.\n" 1334 " The shelf's log message can be set with -m, -F, etc.\n" 1335 "\n"), N_( 1336 " 'svn shelve --keep-local' is the same as 'svn shelf-save'.\n" 1337 "\n"), N_( 1338 " The kinds of change you can shelve are committable changes to files and\n" 1339 " properties, except the following kinds which are not yet supported:\n" 1340 " * copies and moves\n" 1341 " * mkdir and rmdir\n" 1342 " Uncommittable states such as conflicts, unversioned and missing cannot\n" 1343 " be shelved.\n" 1344 "\n"), N_( 1345 " To bring back shelved changes, use 'svn unshelve SHELF'.\n" 1346 "\n"), N_( 1347 " Shelves are currently stored under <WC>/.svn/experimental/shelves/ .\n" 1348 " (In Subversion 1.10, shelves were stored under <WC>/.svn/shelves/ as\n" 1349 " patch files. To recover a shelf created by 1.10, either use a 1.10\n" 1350 " client to find and unshelve it, or find the patch file and use any\n" 1351 " 1.10 or later 'svn patch' to apply it.)\n" 1352 "\n"), N_( 1353 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1354 " in the next release, and there is no promise of backward compatibility.\n" 1355 )}, 1356 {'q', opt_dry_run, opt_keep_local, 1357 opt_depth, opt_targets, opt_changelist, 1358 SVN_CL__LOG_MSG_OPTIONS, 1359 } }, 1360 1361 { "x-unshelve", svn_cl__shelf_unshelve, {0}, {N_( 1362 "Copy shelved changes back into the WC.\n" 1363 "usage: x-unshelve [--drop] [SHELF [VERSION]]\n" 1364 "\n"), N_( 1365 " Apply the changes stored in SHELF to the working copy.\n" 1366 " SHELF defaults to the newest shelf.\n" 1367 "\n"), N_( 1368 " Apply the newest version of the shelf, by default. If VERSION is\n" 1369 " specified, apply that version and discard all versions newer than that.\n" 1370 " In any case, retain the unshelved version and versions older than that\n" 1371 " (unless --drop is specified).\n" 1372 "\n"), N_( 1373 " With --drop, delete the entire shelf (like 'svn shelf-drop') after\n" 1374 " successfully unshelving with no conflicts.\n" 1375 "\n"), N_( 1376 " The working files involved should be in a clean, unmodified state\n" 1377 " before using this command. To roll back to an older version of the\n" 1378 " shelf, first ensure any current working changes are removed, such as\n" 1379 " by shelving or reverting them, and then unshelve the desired version.\n" 1380 "\n"), N_( 1381 " Unshelve normally refuses to apply any changes if any path involved is\n" 1382 " already modified (or has any other abnormal status) in the WC. With\n" 1383 " --force, it does not check and may error out and/or produce partial or\n" 1384 " unexpected results.\n" 1385 "\n"), N_( 1386 " The shelving feature is EXPERIMENTAL. This command is likely to change\n" 1387 " in the next release, and there is no promise of backward compatibility.\n" 1388 )}, 1389 {opt_drop, 'q', opt_dry_run, opt_force} }, 1390 1391 { "x-wc-copy-mods", svn_cl__wc_copy_mods, {0}, {N_( 1392 "Copy local modifications from one WC to another.\n" 1393 "usage: x-wc-copy-mods SRC_WC_PATH DST_WC_PATH\n" 1394 "\n"), N_( 1395 " The source and destination WC paths may be in the same WC or in different" 1396 " WCs.\n" 1397 "\n"), N_( 1398 " This feature is EXPERIMENTAL. This command is likely to change\n" 1399 " in the next release, and there is no promise of backward compatibility.\n" 1400 )}, 1401 }, 1402 1403 { NULL, NULL, {0}, {NULL}, {0} } 1404}; 1405 1406