1169689Skan/* 2169689Skan * notify.c: feedback handlers for cmdline client. 3169689Skan * 4169689Skan * ==================================================================== 5169689Skan * Licensed to the Apache Software Foundation (ASF) under one 6169689Skan * or more contributor license agreements. See the NOTICE file 7169689Skan * distributed with this work for additional information 8169689Skan * regarding copyright ownership. The ASF licenses this file 9169689Skan * to you under the Apache License, Version 2.0 (the 10169689Skan * "License"); you may not use this file except in compliance 11169689Skan * with the License. You may obtain a copy of the License at 12169689Skan * 13169689Skan * http://www.apache.org/licenses/LICENSE-2.0 14169689Skan * 15169689Skan * Unless required by applicable law or agreed to in writing, 16169689Skan * software distributed under the License is distributed on an 17169689Skan * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18169689Skan * KIND, either express or implied. See the License for the 19169689Skan * specific language governing permissions and limitations 20169689Skan * under the License. 21169689Skan * ==================================================================== 22169689Skan */ 23169689Skan 24169689Skan/* ==================================================================== */ 25169689Skan 26169689Skan 27169689Skan 28169689Skan/*** Includes. ***/ 29169689Skan 30169689Skan#define APR_WANT_STDIO 31169689Skan#define APR_WANT_STRFUNC 32169689Skan#include <apr_want.h> 33169689Skan 34169689Skan#include "svn_cmdline.h" 35169689Skan#include "svn_pools.h" 36169689Skan#include "svn_dirent_uri.h" 37169689Skan#include "svn_path.h" 38169689Skan#include "svn_sorts.h" 39169689Skan#include "cl.h" 40 41#include "svn_private_config.h" 42 43 44/* Baton for notify and friends. */ 45struct notify_baton 46{ 47 svn_boolean_t received_some_change; 48 svn_boolean_t is_checkout; 49 svn_boolean_t is_export; 50 svn_boolean_t is_wc_to_repos_copy; 51 svn_boolean_t sent_first_txdelta; 52 svn_boolean_t in_external; 53 svn_boolean_t had_print_error; /* Used to not keep printing error messages 54 when we've already had one print error. */ 55 56 /* Conflict stats for update and merge. */ 57 unsigned int text_conflicts; 58 unsigned int prop_conflicts; 59 unsigned int tree_conflicts; 60 unsigned int skipped_paths; 61 apr_hash_t *conflicted_paths; 62 63 /* The cwd, for use in decomposing absolute paths. */ 64 const char *path_prefix; 65}; 66 67 68/* Add a conflicted path to the list of conflicted paths stored 69 * in the notify baton. */ 70static void 71add_conflicted_path(struct notify_baton *nb, const char *path) 72{ 73 apr_hash_set(nb->conflicted_paths, 74 apr_pstrdup(apr_hash_pool_get(nb->conflicted_paths), path), 75 APR_HASH_KEY_STRING, ""); 76} 77 78/* This implements `svn_wc_notify_func2_t'. 79 * NOTE: This function can't fail, so we just ignore any print errors. */ 80static void 81notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool) 82{ 83 struct notify_baton *nb = baton; 84 char statchar_buf[5] = " "; 85 const char *path_local; 86 svn_error_t *err; 87 88 if (n->url) 89 path_local = n->url; 90 else 91 { 92 if (n->path_prefix) 93 path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path, 94 pool); 95 else /* skip nb->path_prefix, if it's non-null */ 96 path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path, 97 pool); 98 } 99 100 switch (n->action) 101 { 102 case svn_wc_notify_skip: 103 nb->skipped_paths++; 104 if (n->content_state == svn_wc_notify_state_missing) 105 { 106 if ((err = svn_cmdline_printf 107 (pool, _("Skipped missing target: '%s'\n"), 108 path_local))) 109 goto print_error; 110 } 111 else if (n->content_state == svn_wc_notify_state_source_missing) 112 { 113 if ((err = svn_cmdline_printf 114 (pool, _("Skipped target: '%s' -- copy-source is missing\n"), 115 path_local))) 116 goto print_error; 117 } 118 else 119 { 120 if ((err = svn_cmdline_printf 121 (pool, _("Skipped '%s'\n"), path_local))) 122 goto print_error; 123 } 124 break; 125 case svn_wc_notify_update_skip_obstruction: 126 nb->skipped_paths++; 127 if ((err = svn_cmdline_printf( 128 pool, _("Skipped '%s' -- An obstructing working copy was found\n"), 129 path_local))) 130 goto print_error; 131 break; 132 case svn_wc_notify_update_skip_working_only: 133 nb->skipped_paths++; 134 if ((err = svn_cmdline_printf( 135 pool, _("Skipped '%s' -- Has no versioned parent\n"), 136 path_local))) 137 goto print_error; 138 break; 139 case svn_wc_notify_update_skip_access_denied: 140 nb->skipped_paths++; 141 if ((err = svn_cmdline_printf( 142 pool, _("Skipped '%s' -- Access denied\n"), 143 path_local))) 144 goto print_error; 145 break; 146 case svn_wc_notify_skip_conflicted: 147 nb->skipped_paths++; 148 if ((err = svn_cmdline_printf( 149 pool, _("Skipped '%s' -- Node remains in conflict\n"), 150 path_local))) 151 goto print_error; 152 break; 153 case svn_wc_notify_update_delete: 154 case svn_wc_notify_exclude: 155 nb->received_some_change = TRUE; 156 if ((err = svn_cmdline_printf(pool, "D %s\n", path_local))) 157 goto print_error; 158 break; 159 case svn_wc_notify_update_broken_lock: 160 if ((err = svn_cmdline_printf(pool, "B %s\n", path_local))) 161 goto print_error; 162 break; 163 164 case svn_wc_notify_update_external_removed: 165 nb->received_some_change = TRUE; 166 if (n->err && n->err->message) 167 { 168 if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n", 169 path_local, n->err->message))) 170 goto print_error; 171 } 172 else 173 { 174 if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n", 175 path_local))) 176 goto print_error; 177 } 178 break; 179 180 case svn_wc_notify_left_local_modifications: 181 if ((err = svn_cmdline_printf(pool, "Left local modifications as '%s'\n", 182 path_local))) 183 goto print_error; 184 break; 185 186 case svn_wc_notify_update_replace: 187 nb->received_some_change = TRUE; 188 if ((err = svn_cmdline_printf(pool, "R %s\n", path_local))) 189 goto print_error; 190 break; 191 192 case svn_wc_notify_update_add: 193 nb->received_some_change = TRUE; 194 if (n->content_state == svn_wc_notify_state_conflicted) 195 { 196 nb->text_conflicts++; 197 add_conflicted_path(nb, n->path); 198 if ((err = svn_cmdline_printf(pool, "C %s\n", path_local))) 199 goto print_error; 200 } 201 else 202 { 203 if ((err = svn_cmdline_printf(pool, "A %s\n", path_local))) 204 goto print_error; 205 } 206 break; 207 208 case svn_wc_notify_exists: 209 nb->received_some_change = TRUE; 210 if (n->content_state == svn_wc_notify_state_conflicted) 211 { 212 nb->text_conflicts++; 213 add_conflicted_path(nb, n->path); 214 statchar_buf[0] = 'C'; 215 } 216 else 217 statchar_buf[0] = 'E'; 218 219 if (n->prop_state == svn_wc_notify_state_conflicted) 220 { 221 nb->prop_conflicts++; 222 add_conflicted_path(nb, n->path); 223 statchar_buf[1] = 'C'; 224 } 225 else if (n->prop_state == svn_wc_notify_state_merged) 226 statchar_buf[1] = 'G'; 227 228 if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local))) 229 goto print_error; 230 break; 231 232 case svn_wc_notify_restore: 233 if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"), 234 path_local))) 235 goto print_error; 236 break; 237 238 case svn_wc_notify_revert: 239 if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"), 240 path_local))) 241 goto print_error; 242 break; 243 244 case svn_wc_notify_failed_revert: 245 if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- " 246 "try updating instead.\n"), 247 path_local))) 248 goto print_error; 249 break; 250 251 case svn_wc_notify_resolved: 252 if ((err = svn_cmdline_printf(pool, 253 _("Resolved conflicted state of '%s'\n"), 254 path_local))) 255 goto print_error; 256 break; 257 258 case svn_wc_notify_add: 259 /* We *should* only get the MIME_TYPE if PATH is a file. If we 260 do get it, and the mime-type is not textual, note that this 261 is a binary addition. */ 262 if (n->mime_type && (svn_mime_type_is_binary(n->mime_type))) 263 { 264 if ((err = svn_cmdline_printf(pool, "A (bin) %s\n", 265 path_local))) 266 goto print_error; 267 } 268 else 269 { 270 if ((err = svn_cmdline_printf(pool, "A %s\n", 271 path_local))) 272 goto print_error; 273 } 274 break; 275 276 case svn_wc_notify_delete: 277 nb->received_some_change = TRUE; 278 if ((err = svn_cmdline_printf(pool, "D %s\n", 279 path_local))) 280 goto print_error; 281 break; 282 283 case svn_wc_notify_patch: 284 { 285 nb->received_some_change = TRUE; 286 if (n->content_state == svn_wc_notify_state_conflicted) 287 { 288 nb->text_conflicts++; 289 add_conflicted_path(nb, n->path); 290 statchar_buf[0] = 'C'; 291 } 292 else if (n->kind == svn_node_file) 293 { 294 if (n->content_state == svn_wc_notify_state_merged) 295 statchar_buf[0] = 'G'; 296 else if (n->content_state == svn_wc_notify_state_changed) 297 statchar_buf[0] = 'U'; 298 } 299 300 if (n->prop_state == svn_wc_notify_state_conflicted) 301 { 302 nb->prop_conflicts++; 303 add_conflicted_path(nb, n->path); 304 statchar_buf[1] = 'C'; 305 } 306 else if (n->prop_state == svn_wc_notify_state_changed) 307 statchar_buf[1] = 'U'; 308 309 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ') 310 { 311 if ((err = svn_cmdline_printf(pool, "%s %s\n", 312 statchar_buf, path_local))) 313 goto print_error; 314 } 315 } 316 break; 317 318 case svn_wc_notify_patch_applied_hunk: 319 nb->received_some_change = TRUE; 320 if (n->hunk_original_start != n->hunk_matched_line) 321 { 322 apr_uint64_t off; 323 const char *s; 324 const char *minus; 325 326 if (n->hunk_matched_line > n->hunk_original_start) 327 { 328 off = n->hunk_matched_line - n->hunk_original_start; 329 minus = ""; 330 } 331 else 332 { 333 off = n->hunk_original_start - n->hunk_matched_line; 334 minus = "-"; 335 } 336 337 /* ### We're creating the localized strings without 338 * ### APR_INT64_T_FMT since it isn't translator-friendly */ 339 if (n->hunk_fuzz) 340 { 341 342 if (n->prop_name) 343 { 344 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " 345 "with offset %s"); 346 347 err = svn_cmdline_printf(pool, 348 apr_pstrcat(pool, s, 349 "%"APR_UINT64_T_FMT 350 " and fuzz %lu (%s)\n", 351 SVN_VA_NULL), 352 n->hunk_original_start, 353 n->hunk_original_length, 354 n->hunk_modified_start, 355 n->hunk_modified_length, 356 minus, off, n->hunk_fuzz, 357 n->prop_name); 358 } 359 else 360 { 361 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " 362 "with offset %s"); 363 364 err = svn_cmdline_printf(pool, 365 apr_pstrcat(pool, s, 366 "%"APR_UINT64_T_FMT 367 " and fuzz %lu\n", 368 SVN_VA_NULL), 369 n->hunk_original_start, 370 n->hunk_original_length, 371 n->hunk_modified_start, 372 n->hunk_modified_length, 373 minus, off, n->hunk_fuzz); 374 } 375 376 if (err) 377 goto print_error; 378 } 379 else 380 { 381 382 if (n->prop_name) 383 { 384 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## " 385 "with offset %s"); 386 err = svn_cmdline_printf(pool, 387 apr_pstrcat(pool, s, 388 "%"APR_UINT64_T_FMT" (%s)\n", 389 SVN_VA_NULL), 390 n->hunk_original_start, 391 n->hunk_original_length, 392 n->hunk_modified_start, 393 n->hunk_modified_length, 394 minus, off, n->prop_name); 395 } 396 else 397 { 398 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " 399 "with offset %s"); 400 err = svn_cmdline_printf(pool, 401 apr_pstrcat(pool, s, 402 "%"APR_UINT64_T_FMT"\n", 403 SVN_VA_NULL), 404 n->hunk_original_start, 405 n->hunk_original_length, 406 n->hunk_modified_start, 407 n->hunk_modified_length, 408 minus, off); 409 } 410 411 if (err) 412 goto print_error; 413 } 414 } 415 else if (n->hunk_fuzz) 416 { 417 if (n->prop_name) 418 err = svn_cmdline_printf(pool, 419 _("> applied hunk ## -%lu,%lu +%lu,%lu ## " 420 "with fuzz %lu (%s)\n"), 421 n->hunk_original_start, 422 n->hunk_original_length, 423 n->hunk_modified_start, 424 n->hunk_modified_length, 425 n->hunk_fuzz, 426 n->prop_name); 427 else 428 err = svn_cmdline_printf(pool, 429 _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ " 430 "with fuzz %lu\n"), 431 n->hunk_original_start, 432 n->hunk_original_length, 433 n->hunk_modified_start, 434 n->hunk_modified_length, 435 n->hunk_fuzz); 436 if (err) 437 goto print_error; 438 439 } 440 break; 441 442 case svn_wc_notify_patch_rejected_hunk: 443 nb->received_some_change = TRUE; 444 445 if (n->prop_name) 446 err = svn_cmdline_printf(pool, 447 _("> rejected hunk " 448 "## -%lu,%lu +%lu,%lu ## (%s)\n"), 449 n->hunk_original_start, 450 n->hunk_original_length, 451 n->hunk_modified_start, 452 n->hunk_modified_length, 453 n->prop_name); 454 else 455 err = svn_cmdline_printf(pool, 456 _("> rejected hunk " 457 "@@ -%lu,%lu +%lu,%lu @@\n"), 458 n->hunk_original_start, 459 n->hunk_original_length, 460 n->hunk_modified_start, 461 n->hunk_modified_length); 462 if (err) 463 goto print_error; 464 break; 465 466 case svn_wc_notify_patch_hunk_already_applied: 467 nb->received_some_change = TRUE; 468 if (n->prop_name) 469 err = svn_cmdline_printf(pool, 470 _("> hunk " 471 "## -%lu,%lu +%lu,%lu ## " 472 "already applied (%s)\n"), 473 n->hunk_original_start, 474 n->hunk_original_length, 475 n->hunk_modified_start, 476 n->hunk_modified_length, 477 n->prop_name); 478 else 479 err = svn_cmdline_printf(pool, 480 _("> hunk " 481 "@@ -%lu,%lu +%lu,%lu @@ " 482 "already applied\n"), 483 n->hunk_original_start, 484 n->hunk_original_length, 485 n->hunk_modified_start, 486 n->hunk_modified_length); 487 if (err) 488 goto print_error; 489 break; 490 491 case svn_wc_notify_update_update: 492 case svn_wc_notify_merge_record_info: 493 { 494 if (n->content_state == svn_wc_notify_state_conflicted) 495 { 496 nb->text_conflicts++; 497 add_conflicted_path(nb, n->path); 498 statchar_buf[0] = 'C'; 499 } 500 else if (n->kind == svn_node_file) 501 { 502 if (n->content_state == svn_wc_notify_state_merged) 503 statchar_buf[0] = 'G'; 504 else if (n->content_state == svn_wc_notify_state_changed) 505 statchar_buf[0] = 'U'; 506 } 507 508 if (n->prop_state == svn_wc_notify_state_conflicted) 509 { 510 nb->prop_conflicts++; 511 add_conflicted_path(nb, n->path); 512 statchar_buf[1] = 'C'; 513 } 514 else if (n->prop_state == svn_wc_notify_state_merged) 515 statchar_buf[1] = 'G'; 516 else if (n->prop_state == svn_wc_notify_state_changed) 517 statchar_buf[1] = 'U'; 518 519 if (n->lock_state == svn_wc_notify_lock_state_unlocked) 520 statchar_buf[2] = 'B'; 521 522 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ') 523 nb->received_some_change = TRUE; 524 525 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ' 526 || statchar_buf[2] != ' ') 527 { 528 if ((err = svn_cmdline_printf(pool, "%s %s\n", 529 statchar_buf, path_local))) 530 goto print_error; 531 } 532 } 533 break; 534 535 case svn_wc_notify_update_external: 536 /* Remember that we're now "inside" an externals definition. */ 537 nb->in_external = TRUE; 538 539 /* Currently this is used for checkouts and switches too. If we 540 want different output, we'll have to add new actions. */ 541 if ((err = svn_cmdline_printf(pool, 542 _("\nFetching external item into '%s':\n"), 543 path_local))) 544 goto print_error; 545 break; 546 547 case svn_wc_notify_failed_external: 548 /* If we are currently inside the handling of an externals 549 definition, then we can simply present n->err as a warning 550 and feel confident that after this, we aren't handling that 551 externals definition any longer. */ 552 if (nb->in_external) 553 { 554 svn_handle_warning2(stderr, n->err, "svn: "); 555 nb->in_external = FALSE; 556 if ((err = svn_cmdline_printf(pool, "\n"))) 557 goto print_error; 558 } 559 /* Otherwise, we'll just print two warnings. Why? Because 560 svn_handle_warning2() only shows the single "best message", 561 but we have two pretty important ones: that the external at 562 '/some/path' didn't pan out, and then the more specific 563 reason why (from n->err). */ 564 else 565 { 566 svn_error_t *warn_err = 567 svn_error_createf(SVN_ERR_BASE, NULL, 568 _("Error handling externals definition for '%s':"), 569 path_local); 570 svn_handle_warning2(stderr, warn_err, "svn: "); 571 svn_error_clear(warn_err); 572 svn_handle_warning2(stderr, n->err, "svn: "); 573 } 574 break; 575 576 case svn_wc_notify_update_started: 577 if (! (nb->in_external || 578 nb->is_checkout || 579 nb->is_export)) 580 { 581 if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"), 582 path_local))) 583 goto print_error; 584 } 585 break; 586 587 case svn_wc_notify_update_completed: 588 { 589 if (SVN_IS_VALID_REVNUM(n->revision)) 590 { 591 if (nb->is_export) 592 { 593 if ((err = svn_cmdline_printf 594 (pool, nb->in_external 595 ? _("Exported external at revision %ld.\n") 596 : _("Exported revision %ld.\n"), 597 n->revision))) 598 goto print_error; 599 } 600 else if (nb->is_checkout) 601 { 602 if ((err = svn_cmdline_printf 603 (pool, nb->in_external 604 ? _("Checked out external at revision %ld.\n") 605 : _("Checked out revision %ld.\n"), 606 n->revision))) 607 goto print_error; 608 } 609 else 610 { 611 if (nb->received_some_change) 612 { 613 nb->received_some_change = FALSE; 614 if ((err = svn_cmdline_printf 615 (pool, nb->in_external 616 ? _("Updated external to revision %ld.\n") 617 : _("Updated to revision %ld.\n"), 618 n->revision))) 619 goto print_error; 620 } 621 else 622 { 623 if ((err = svn_cmdline_printf 624 (pool, nb->in_external 625 ? _("External at revision %ld.\n") 626 : _("At revision %ld.\n"), 627 n->revision))) 628 goto print_error; 629 } 630 } 631 } 632 else /* no revision */ 633 { 634 if (nb->is_export) 635 { 636 if ((err = svn_cmdline_printf 637 (pool, nb->in_external 638 ? _("External export complete.\n") 639 : _("Export complete.\n")))) 640 goto print_error; 641 } 642 else if (nb->is_checkout) 643 { 644 if ((err = svn_cmdline_printf 645 (pool, nb->in_external 646 ? _("External checkout complete.\n") 647 : _("Checkout complete.\n")))) 648 goto print_error; 649 } 650 else 651 { 652 if ((err = svn_cmdline_printf 653 (pool, nb->in_external 654 ? _("External update complete.\n") 655 : _("Update complete.\n")))) 656 goto print_error; 657 } 658 } 659 } 660 661 if (nb->in_external) 662 { 663 nb->in_external = FALSE; 664 if ((err = svn_cmdline_printf(pool, "\n"))) 665 goto print_error; 666 } 667 break; 668 669 case svn_wc_notify_status_external: 670 if ((err = svn_cmdline_printf 671 (pool, _("\nPerforming status on external item at '%s':\n"), 672 path_local))) 673 goto print_error; 674 break; 675 676 case svn_wc_notify_status_completed: 677 if (SVN_IS_VALID_REVNUM(n->revision)) 678 if ((err = svn_cmdline_printf(pool, 679 _("Status against revision: %6ld\n"), 680 n->revision))) 681 goto print_error; 682 break; 683 684 case svn_wc_notify_commit_modified: 685 /* xgettext: Align the %s's on this and the following 4 messages */ 686 if ((err = svn_cmdline_printf(pool, 687 nb->is_wc_to_repos_copy 688 ? _("Sending copy of %s\n") 689 : _("Sending %s\n"), 690 path_local))) 691 goto print_error; 692 break; 693 694 case svn_wc_notify_commit_added: 695 case svn_wc_notify_commit_copied: 696 if (n->mime_type && svn_mime_type_is_binary(n->mime_type)) 697 { 698 if ((err = svn_cmdline_printf(pool, 699 nb->is_wc_to_repos_copy 700 ? _("Adding copy of (bin) %s\n") 701 : _("Adding (bin) %s\n"), 702 path_local))) 703 goto print_error; 704 } 705 else 706 { 707 if ((err = svn_cmdline_printf(pool, 708 nb->is_wc_to_repos_copy 709 ? _("Adding copy of %s\n") 710 : _("Adding %s\n"), 711 path_local))) 712 goto print_error; 713 } 714 break; 715 716 case svn_wc_notify_commit_deleted: 717 if ((err = svn_cmdline_printf(pool, 718 nb->is_wc_to_repos_copy 719 ? _("Deleting copy of %s\n") 720 : _("Deleting %s\n"), 721 path_local))) 722 goto print_error; 723 break; 724 725 case svn_wc_notify_commit_replaced: 726 case svn_wc_notify_commit_copied_replaced: 727 if ((err = svn_cmdline_printf(pool, 728 nb->is_wc_to_repos_copy 729 ? _("Replacing copy of %s\n") 730 : _("Replacing %s\n"), 731 path_local))) 732 goto print_error; 733 break; 734 735 case svn_wc_notify_commit_postfix_txdelta: 736 if (! nb->sent_first_txdelta) 737 { 738 nb->sent_first_txdelta = TRUE; 739 if ((err = svn_cmdline_printf(pool, 740 _("Transmitting file data ")))) 741 goto print_error; 742 } 743 744 if ((err = svn_cmdline_printf(pool, "."))) 745 goto print_error; 746 break; 747 748 case svn_wc_notify_locked: 749 if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"), 750 path_local, n->lock->owner))) 751 goto print_error; 752 break; 753 754 case svn_wc_notify_unlocked: 755 if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"), 756 path_local))) 757 goto print_error; 758 break; 759 760 case svn_wc_notify_failed_lock: 761 case svn_wc_notify_failed_unlock: 762 svn_handle_warning2(stderr, n->err, "svn: "); 763 break; 764 765 case svn_wc_notify_changelist_set: 766 if ((err = svn_cmdline_printf(pool, "A [%s] %s\n", 767 n->changelist_name, path_local))) 768 goto print_error; 769 break; 770 771 case svn_wc_notify_changelist_clear: 772 case svn_wc_notify_changelist_moved: 773 if ((err = svn_cmdline_printf(pool, 774 "D [%s] %s\n", 775 n->changelist_name, path_local))) 776 goto print_error; 777 break; 778 779 case svn_wc_notify_merge_begin: 780 if (n->merge_range == NULL) 781 err = svn_cmdline_printf(pool, 782 _("--- Merging differences between " 783 "repository URLs into '%s':\n"), 784 path_local); 785 else if (n->merge_range->start == n->merge_range->end - 1 786 || n->merge_range->start == n->merge_range->end) 787 err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"), 788 n->merge_range->end, path_local); 789 else if (n->merge_range->start - 1 == n->merge_range->end) 790 err = svn_cmdline_printf(pool, 791 _("--- Reverse-merging r%ld into '%s':\n"), 792 n->merge_range->start, path_local); 793 else if (n->merge_range->start < n->merge_range->end) 794 err = svn_cmdline_printf(pool, 795 _("--- Merging r%ld through r%ld into " 796 "'%s':\n"), 797 n->merge_range->start + 1, 798 n->merge_range->end, path_local); 799 else /* n->merge_range->start > n->merge_range->end - 1 */ 800 err = svn_cmdline_printf(pool, 801 _("--- Reverse-merging r%ld through r%ld " 802 "into '%s':\n"), 803 n->merge_range->start, 804 n->merge_range->end + 1, path_local); 805 if (err) 806 goto print_error; 807 break; 808 809 case svn_wc_notify_merge_record_info_begin: 810 if (!n->merge_range) 811 { 812 err = svn_cmdline_printf(pool, 813 _("--- Recording mergeinfo for merge " 814 "between repository URLs into '%s':\n"), 815 path_local); 816 } 817 else 818 { 819 if (n->merge_range->start == n->merge_range->end - 1 820 || n->merge_range->start == n->merge_range->end) 821 err = svn_cmdline_printf( 822 pool, 823 _("--- Recording mergeinfo for merge of r%ld into '%s':\n"), 824 n->merge_range->end, path_local); 825 else if (n->merge_range->start - 1 == n->merge_range->end) 826 err = svn_cmdline_printf( 827 pool, 828 _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"), 829 n->merge_range->start, path_local); 830 else if (n->merge_range->start < n->merge_range->end) 831 err = svn_cmdline_printf( 832 pool, 833 _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"), 834 n->merge_range->start + 1, n->merge_range->end, path_local); 835 else /* n->merge_range->start > n->merge_range->end - 1 */ 836 err = svn_cmdline_printf( 837 pool, 838 _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"), 839 n->merge_range->start, n->merge_range->end + 1, path_local); 840 } 841 842 if (err) 843 goto print_error; 844 break; 845 846 case svn_wc_notify_merge_elide_info: 847 if ((err = svn_cmdline_printf(pool, 848 _("--- Eliding mergeinfo from '%s':\n"), 849 path_local))) 850 goto print_error; 851 break; 852 853 case svn_wc_notify_foreign_merge_begin: 854 if (n->merge_range == NULL) 855 err = svn_cmdline_printf(pool, 856 _("--- Merging differences between " 857 "foreign repository URLs into '%s':\n"), 858 path_local); 859 else if (n->merge_range->start == n->merge_range->end - 1 860 || n->merge_range->start == n->merge_range->end) 861 err = svn_cmdline_printf(pool, 862 _("--- Merging (from foreign repository) " 863 "r%ld into '%s':\n"), 864 n->merge_range->end, path_local); 865 else if (n->merge_range->start - 1 == n->merge_range->end) 866 err = svn_cmdline_printf(pool, 867 _("--- Reverse-merging (from foreign " 868 "repository) r%ld into '%s':\n"), 869 n->merge_range->start, path_local); 870 else if (n->merge_range->start < n->merge_range->end) 871 err = svn_cmdline_printf(pool, 872 _("--- Merging (from foreign repository) " 873 "r%ld through r%ld into '%s':\n"), 874 n->merge_range->start + 1, 875 n->merge_range->end, path_local); 876 else /* n->merge_range->start > n->merge_range->end - 1 */ 877 err = svn_cmdline_printf(pool, 878 _("--- Reverse-merging (from foreign " 879 "repository) r%ld through r%ld into " 880 "'%s':\n"), 881 n->merge_range->start, 882 n->merge_range->end + 1, path_local); 883 if (err) 884 goto print_error; 885 break; 886 887 case svn_wc_notify_tree_conflict: 888 nb->tree_conflicts++; 889 add_conflicted_path(nb, n->path); 890 if ((err = svn_cmdline_printf(pool, " C %s\n", path_local))) 891 goto print_error; 892 break; 893 894 case svn_wc_notify_update_shadowed_add: 895 nb->received_some_change = TRUE; 896 if ((err = svn_cmdline_printf(pool, " A %s\n", path_local))) 897 goto print_error; 898 break; 899 900 case svn_wc_notify_update_shadowed_update: 901 nb->received_some_change = TRUE; 902 if ((err = svn_cmdline_printf(pool, " U %s\n", path_local))) 903 goto print_error; 904 break; 905 906 case svn_wc_notify_update_shadowed_delete: 907 nb->received_some_change = TRUE; 908 if ((err = svn_cmdline_printf(pool, " D %s\n", path_local))) 909 goto print_error; 910 break; 911 912 case svn_wc_notify_property_modified: 913 case svn_wc_notify_property_added: 914 err = svn_cmdline_printf(pool, 915 _("property '%s' set on '%s'\n"), 916 n->prop_name, path_local); 917 if (err) 918 goto print_error; 919 break; 920 921 case svn_wc_notify_property_deleted: 922 err = svn_cmdline_printf(pool, 923 _("property '%s' deleted from '%s'.\n"), 924 n->prop_name, path_local); 925 if (err) 926 goto print_error; 927 break; 928 929 case svn_wc_notify_property_deleted_nonexistent: 930 err = svn_cmdline_printf(pool, 931 _("Attempting to delete nonexistent " 932 "property '%s' on '%s'\n"), n->prop_name, 933 path_local); 934 if (err) 935 goto print_error; 936 break; 937 938 case svn_wc_notify_revprop_set: 939 err = svn_cmdline_printf(pool, 940 _("property '%s' set on repository revision %ld\n"), 941 n->prop_name, n->revision); 942 if (err) 943 goto print_error; 944 break; 945 946 case svn_wc_notify_revprop_deleted: 947 err = svn_cmdline_printf(pool, 948 _("property '%s' deleted from repository revision %ld\n"), 949 n->prop_name, n->revision); 950 if (err) 951 goto print_error; 952 break; 953 954 case svn_wc_notify_upgraded_path: 955 err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local); 956 if (err) 957 goto print_error; 958 break; 959 960 case svn_wc_notify_url_redirect: 961 err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"), 962 n->url); 963 if (err) 964 goto print_error; 965 break; 966 967 case svn_wc_notify_path_nonexistent: 968 err = svn_cmdline_printf(pool, _("'%s' is not under version control"), 969 path_local); 970 if (err) 971 goto print_error; 972 break; 973 974 case svn_wc_notify_conflict_resolver_starting: 975 /* Once all operations invoke the interactive conflict resolution after 976 * they've completed, we can run svn_cl__print_conflict_stats() here. */ 977 break; 978 979 case svn_wc_notify_conflict_resolver_done: 980 break; 981 982 default: 983 break; 984 } 985 986 if ((err = svn_cmdline_fflush(stdout))) 987 goto print_error; 988 989 return; 990 991 print_error: 992 /* If we had no errors before, print this error to stderr. Else, don't print 993 anything. The user already knows there were some output errors, 994 so there is no point in flooding her with an error per notification. */ 995 if (!nb->had_print_error) 996 { 997 nb->had_print_error = TRUE; 998 /* Issue #3014: 999 * Don't print anything on broken pipes. The pipe was likely 1000 * closed by the process at the other end. We expect that 1001 * process to perform error reporting as necessary. 1002 * 1003 * ### This assumes that there is only one error in a chain for 1004 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */ 1005 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR) 1006 svn_handle_error2(err, stderr, FALSE, "svn: "); 1007 } 1008 svn_error_clear(err); 1009} 1010 1011 1012svn_error_t * 1013svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p, 1014 void **notify_baton_p, 1015 apr_pool_t *pool) 1016{ 1017 struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb)); 1018 1019 nb->received_some_change = FALSE; 1020 nb->sent_first_txdelta = FALSE; 1021 nb->is_checkout = FALSE; 1022 nb->is_export = FALSE; 1023 nb->is_wc_to_repos_copy = FALSE; 1024 nb->in_external = FALSE; 1025 nb->had_print_error = FALSE; 1026 nb->text_conflicts = 0; 1027 nb->prop_conflicts = 0; 1028 nb->tree_conflicts = 0; 1029 nb->skipped_paths = 0; 1030 nb->conflicted_paths = apr_hash_make(pool); 1031 SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool)); 1032 1033 *notify_func_p = notify; 1034 *notify_baton_p = nb; 1035 return SVN_NO_ERROR; 1036} 1037 1038svn_error_t * 1039svn_cl__notifier_mark_export(void *baton) 1040{ 1041 struct notify_baton *nb = baton; 1042 1043 nb->is_export = TRUE; 1044 return SVN_NO_ERROR; 1045} 1046