2 * notify.c: feedback handlers for cmdline client.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
30 #define APR_WANT_STDIO
31 #define APR_WANT_STRFUNC
34 #include "svn_cmdline.h"
35 #include "svn_pools.h"
36 #include "svn_dirent_uri.h"
38 #include "svn_sorts.h"
41 #include "private/svn_subr_private.h"
42 #include "private/svn_dep_compat.h"
44 #include "svn_private_config.h"
47 /* Baton for notify and friends. */
50 svn_boolean_t received_some_change;
51 svn_boolean_t is_checkout;
52 svn_boolean_t is_export;
53 svn_boolean_t is_wc_to_repos_copy;
54 svn_boolean_t sent_first_txdelta;
56 svn_boolean_t had_print_error; /* Used to not keep printing error messages
57 when we've already had one print error. */
59 svn_cl__conflict_stats_t *conflict_stats;
61 /* The cwd, for use in decomposing absolute paths. */
62 const char *path_prefix;
65 /* Conflict stats for operations such as update and merge. */
66 struct svn_cl__conflict_stats_t
68 apr_pool_t *stats_pool;
69 apr_hash_t *text_conflicts, *prop_conflicts, *tree_conflicts;
70 int text_conflicts_resolved, prop_conflicts_resolved, tree_conflicts_resolved;
74 svn_cl__conflict_stats_t *
75 svn_cl__conflict_stats_create(apr_pool_t *pool)
77 svn_cl__conflict_stats_t *conflict_stats
78 = apr_palloc(pool, sizeof(*conflict_stats));
80 conflict_stats->stats_pool = pool;
81 conflict_stats->text_conflicts = apr_hash_make(pool);
82 conflict_stats->prop_conflicts = apr_hash_make(pool);
83 conflict_stats->tree_conflicts = apr_hash_make(pool);
84 conflict_stats->text_conflicts_resolved = 0;
85 conflict_stats->prop_conflicts_resolved = 0;
86 conflict_stats->tree_conflicts_resolved = 0;
87 conflict_stats->skipped_paths = 0;
88 return conflict_stats;
91 /* Add the PATH (as a key, with a meaningless value) into the HASH in NB. */
93 store_path(struct notify_baton *nb, apr_hash_t *hash, const char *path)
95 svn_hash_sets(hash, apr_pstrdup(nb->conflict_stats->stats_pool, path), "");
99 svn_cl__conflict_stats_resolved(svn_cl__conflict_stats_t *conflict_stats,
100 const char *path_local,
101 svn_wc_conflict_kind_t conflict_kind)
103 switch (conflict_kind)
105 case svn_wc_conflict_kind_text:
106 if (svn_hash_gets(conflict_stats->text_conflicts, path_local))
108 svn_hash_sets(conflict_stats->text_conflicts, path_local, NULL);
109 conflict_stats->text_conflicts_resolved++;
112 case svn_wc_conflict_kind_property:
113 if (svn_hash_gets(conflict_stats->prop_conflicts, path_local))
115 svn_hash_sets(conflict_stats->prop_conflicts, path_local, NULL);
116 conflict_stats->prop_conflicts_resolved++;
119 case svn_wc_conflict_kind_tree:
120 if (svn_hash_gets(conflict_stats->tree_conflicts, path_local))
122 svn_hash_sets(conflict_stats->tree_conflicts, path_local, NULL);
123 conflict_stats->tree_conflicts_resolved++;
130 remaining_str(apr_pool_t *pool, int n_remaining)
132 return apr_psprintf(pool, Q_("%d remaining",
139 resolved_str(apr_pool_t *pool, int n_resolved)
141 return apr_psprintf(pool, Q_("and %d already resolved",
142 "and %d already resolved",
148 svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats,
149 apr_pool_t *scratch_pool)
151 int n_text = apr_hash_count(conflict_stats->text_conflicts);
152 int n_prop = apr_hash_count(conflict_stats->prop_conflicts);
153 int n_tree = apr_hash_count(conflict_stats->tree_conflicts);
154 int n_text_r = conflict_stats->text_conflicts_resolved;
155 int n_prop_r = conflict_stats->prop_conflicts_resolved;
156 int n_tree_r = conflict_stats->tree_conflicts_resolved;
158 if (n_text > 0 || n_text_r > 0
159 || n_prop > 0 || n_prop_r > 0
160 || n_tree > 0 || n_tree_r > 0
161 || conflict_stats->skipped_paths > 0)
162 SVN_ERR(svn_cmdline_printf(scratch_pool,
163 _("Summary of conflicts:\n")));
165 if (n_text_r == 0 && n_prop_r == 0 && n_tree_r == 0)
168 SVN_ERR(svn_cmdline_printf(scratch_pool,
169 _(" Text conflicts: %d\n"),
172 SVN_ERR(svn_cmdline_printf(scratch_pool,
173 _(" Property conflicts: %d\n"),
176 SVN_ERR(svn_cmdline_printf(scratch_pool,
177 _(" Tree conflicts: %d\n"),
182 if (n_text > 0 || n_text_r > 0)
183 SVN_ERR(svn_cmdline_printf(scratch_pool,
184 _(" Text conflicts: %s (%s)\n"),
185 remaining_str(scratch_pool, n_text),
186 resolved_str(scratch_pool, n_text_r)));
187 if (n_prop > 0 || n_prop_r > 0)
188 SVN_ERR(svn_cmdline_printf(scratch_pool,
189 _(" Property conflicts: %s (%s)\n"),
190 remaining_str(scratch_pool, n_prop),
191 resolved_str(scratch_pool, n_prop_r)));
192 if (n_tree > 0 || n_tree_r > 0)
193 SVN_ERR(svn_cmdline_printf(scratch_pool,
194 _(" Tree conflicts: %s (%s)\n"),
195 remaining_str(scratch_pool, n_tree),
196 resolved_str(scratch_pool, n_tree_r)));
198 if (conflict_stats->skipped_paths > 0)
199 SVN_ERR(svn_cmdline_printf(scratch_pool,
200 _(" Skipped paths: %d\n"),
201 conflict_stats->skipped_paths));
207 svn_cl__notifier_print_conflict_stats(void *baton, apr_pool_t *scratch_pool)
209 struct notify_baton *nb = baton;
211 SVN_ERR(svn_cl__print_conflict_stats(nb->conflict_stats, scratch_pool));
215 /* The body for notify() function with standard error handling semantic.
216 * Handling of errors implemented at caller side. */
218 notify_body(struct notify_baton *nb,
219 const svn_wc_notify_t *n,
222 char statchar_buf[5] = " ";
223 const char *path_local;
229 /* Skip the path prefix in N, if supplied, or else the path prefix
230 in NB (which was set to the current working directory). */
232 path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path,
235 path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path,
241 case svn_wc_notify_skip:
242 nb->conflict_stats->skipped_paths++;
243 if (n->content_state == svn_wc_notify_state_missing)
245 SVN_ERR(svn_cmdline_printf(pool,
246 _("Skipped missing target: '%s'\n"),
249 else if (n->content_state == svn_wc_notify_state_source_missing)
251 SVN_ERR(svn_cmdline_printf(
253 _("Skipped target: '%s' -- copy-source is missing\n"),
258 SVN_ERR(svn_cmdline_printf(pool, _("Skipped '%s'\n"), path_local));
261 case svn_wc_notify_update_skip_obstruction:
262 nb->conflict_stats->skipped_paths++;
263 SVN_ERR(svn_cmdline_printf(
265 _("Skipped '%s' -- An obstructing working copy was found\n"),
268 case svn_wc_notify_update_skip_working_only:
269 nb->conflict_stats->skipped_paths++;
270 SVN_ERR(svn_cmdline_printf(
271 pool, _("Skipped '%s' -- Has no versioned parent\n"),
274 case svn_wc_notify_update_skip_access_denied:
275 nb->conflict_stats->skipped_paths++;
276 SVN_ERR(svn_cmdline_printf(
277 pool, _("Skipped '%s' -- Access denied\n"),
280 case svn_wc_notify_skip_conflicted:
281 nb->conflict_stats->skipped_paths++;
282 SVN_ERR(svn_cmdline_printf(
283 pool, _("Skipped '%s' -- Node remains in conflict\n"),
286 case svn_wc_notify_update_delete:
287 case svn_wc_notify_exclude:
288 nb->received_some_change = TRUE;
289 SVN_ERR(svn_cmdline_printf(pool, "D %s\n", path_local));
291 case svn_wc_notify_update_broken_lock:
292 SVN_ERR(svn_cmdline_printf(pool, "B %s\n", path_local));
295 case svn_wc_notify_update_external_removed:
296 nb->received_some_change = TRUE;
297 if (n->err && n->err->message)
299 SVN_ERR(svn_cmdline_printf(pool, _("Removed external '%s': %s\n"),
300 path_local, n->err->message));
304 SVN_ERR(svn_cmdline_printf(pool, _("Removed external '%s'\n"),
309 case svn_wc_notify_left_local_modifications:
310 SVN_ERR(svn_cmdline_printf(pool, _("Left local modifications as '%s'\n"),
314 case svn_wc_notify_update_replace:
315 nb->received_some_change = TRUE;
316 SVN_ERR(svn_cmdline_printf(pool, "R %s\n", path_local));
319 case svn_wc_notify_update_add:
320 nb->received_some_change = TRUE;
321 if (n->content_state == svn_wc_notify_state_conflicted)
323 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
324 SVN_ERR(svn_cmdline_printf(pool, "C %s\n", path_local));
328 SVN_ERR(svn_cmdline_printf(pool, "A %s\n", path_local));
332 case svn_wc_notify_exists:
333 nb->received_some_change = TRUE;
334 if (n->content_state == svn_wc_notify_state_conflicted)
336 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
337 statchar_buf[0] = 'C';
340 statchar_buf[0] = 'E';
342 if (n->prop_state == svn_wc_notify_state_conflicted)
344 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
345 statchar_buf[1] = 'C';
347 else if (n->prop_state == svn_wc_notify_state_merged)
348 statchar_buf[1] = 'G';
350 SVN_ERR(svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local));
353 case svn_wc_notify_restore:
354 SVN_ERR(svn_cmdline_printf(pool, _("Restored '%s'\n"),
358 case svn_wc_notify_revert:
359 SVN_ERR(svn_cmdline_printf(pool, _("Reverted '%s'\n"),
363 case svn_wc_notify_failed_revert:
364 SVN_ERR(svn_cmdline_printf(pool, _("Failed to revert '%s' -- "
365 "try updating instead.\n"),
369 case svn_wc_notify_resolved:
370 SVN_ERR(svn_cmdline_printf(pool,
371 _("Resolved conflicted state of '%s'\n"),
375 case svn_wc_notify_add:
376 /* We *should* only get the MIME_TYPE if PATH is a file. If we
377 do get it, and the mime-type is not textual, note that this
378 is a binary addition. */
379 if (n->mime_type && (svn_mime_type_is_binary(n->mime_type)))
381 SVN_ERR(svn_cmdline_printf(pool, "A (bin) %s\n",
386 SVN_ERR(svn_cmdline_printf(pool, "A %s\n",
391 case svn_wc_notify_delete:
392 nb->received_some_change = TRUE;
393 SVN_ERR(svn_cmdline_printf(pool, "D %s\n",
397 case svn_wc_notify_patch:
399 nb->received_some_change = TRUE;
400 if (n->content_state == svn_wc_notify_state_conflicted)
402 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
403 statchar_buf[0] = 'C';
405 else if (n->kind == svn_node_file)
407 if (n->content_state == svn_wc_notify_state_merged)
408 statchar_buf[0] = 'G';
409 else if (n->content_state == svn_wc_notify_state_changed)
410 statchar_buf[0] = 'U';
413 if (n->prop_state == svn_wc_notify_state_conflicted)
415 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
416 statchar_buf[1] = 'C';
418 else if (n->prop_state == svn_wc_notify_state_changed)
419 statchar_buf[1] = 'U';
421 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
423 SVN_ERR(svn_cmdline_printf(pool, "%s %s\n",
424 statchar_buf, path_local));
429 case svn_wc_notify_patch_applied_hunk:
430 nb->received_some_change = TRUE;
431 if (n->hunk_original_start != n->hunk_matched_line)
437 if (n->hunk_matched_line > n->hunk_original_start)
439 /* If we are patching from the start of an empty file,
440 it is nicer to show offset 0 */
441 if (n->hunk_original_start == 0 && n->hunk_matched_line == 1)
442 off = 0; /* No offset, just adding */
444 off = n->hunk_matched_line - n->hunk_original_start;
450 off = n->hunk_original_start - n->hunk_matched_line;
454 /* ### We're creating the localized strings without
455 * ### APR_INT64_T_FMT since it isn't translator-friendly */
461 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
464 SVN_ERR(svn_cmdline_printf(pool,
467 " and fuzz %lu (%s)\n",
469 n->hunk_original_start,
470 n->hunk_original_length,
471 n->hunk_modified_start,
472 n->hunk_modified_length,
473 minus, off, n->hunk_fuzz,
478 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
481 SVN_ERR(svn_cmdline_printf(pool,
486 n->hunk_original_start,
487 n->hunk_original_length,
488 n->hunk_modified_start,
489 n->hunk_modified_length,
490 minus, off, n->hunk_fuzz));
498 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
500 SVN_ERR(svn_cmdline_printf(pool,
502 "%"APR_UINT64_T_FMT" (%s)\n",
504 n->hunk_original_start,
505 n->hunk_original_length,
506 n->hunk_modified_start,
507 n->hunk_modified_length,
508 minus, off, n->prop_name));
512 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
514 SVN_ERR(svn_cmdline_printf(pool,
516 "%"APR_UINT64_T_FMT"\n",
518 n->hunk_original_start,
519 n->hunk_original_length,
520 n->hunk_modified_start,
521 n->hunk_modified_length,
526 else if (n->hunk_fuzz)
529 SVN_ERR(svn_cmdline_printf(pool,
530 _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
531 "with fuzz %lu (%s)\n"),
532 n->hunk_original_start,
533 n->hunk_original_length,
534 n->hunk_modified_start,
535 n->hunk_modified_length,
539 SVN_ERR(svn_cmdline_printf(pool,
540 _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
542 n->hunk_original_start,
543 n->hunk_original_length,
544 n->hunk_modified_start,
545 n->hunk_modified_length,
551 case svn_wc_notify_patch_rejected_hunk:
552 nb->received_some_change = TRUE;
555 SVN_ERR(svn_cmdline_printf(pool,
557 "## -%lu,%lu +%lu,%lu ## (%s)\n"),
558 n->hunk_original_start,
559 n->hunk_original_length,
560 n->hunk_modified_start,
561 n->hunk_modified_length,
564 SVN_ERR(svn_cmdline_printf(pool,
566 "@@ -%lu,%lu +%lu,%lu @@\n"),
567 n->hunk_original_start,
568 n->hunk_original_length,
569 n->hunk_modified_start,
570 n->hunk_modified_length));
573 case svn_wc_notify_patch_hunk_already_applied:
574 nb->received_some_change = TRUE;
576 SVN_ERR(svn_cmdline_printf(pool,
578 "## -%lu,%lu +%lu,%lu ## "
579 "already applied (%s)\n"),
580 n->hunk_original_start,
581 n->hunk_original_length,
582 n->hunk_modified_start,
583 n->hunk_modified_length,
586 SVN_ERR(svn_cmdline_printf(pool,
588 "@@ -%lu,%lu +%lu,%lu @@ "
589 "already applied\n"),
590 n->hunk_original_start,
591 n->hunk_original_length,
592 n->hunk_modified_start,
593 n->hunk_modified_length));
596 case svn_wc_notify_update_update:
597 case svn_wc_notify_merge_record_info:
599 if (n->content_state == svn_wc_notify_state_conflicted)
601 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
602 statchar_buf[0] = 'C';
604 else if (n->kind == svn_node_file)
606 if (n->content_state == svn_wc_notify_state_merged)
607 statchar_buf[0] = 'G';
608 else if (n->content_state == svn_wc_notify_state_changed)
609 statchar_buf[0] = 'U';
612 if (n->prop_state == svn_wc_notify_state_conflicted)
614 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
615 statchar_buf[1] = 'C';
617 else if (n->prop_state == svn_wc_notify_state_merged)
618 statchar_buf[1] = 'G';
619 else if (n->prop_state == svn_wc_notify_state_changed)
620 statchar_buf[1] = 'U';
622 if (n->lock_state == svn_wc_notify_lock_state_unlocked)
623 statchar_buf[2] = 'B';
625 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
626 nb->received_some_change = TRUE;
628 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' '
629 || statchar_buf[2] != ' ')
631 SVN_ERR(svn_cmdline_printf(pool, "%s %s\n",
632 statchar_buf, path_local));
637 case svn_wc_notify_update_external:
638 /* Remember that we're now "inside" an externals definition. */
641 /* Currently this is used for checkouts and switches too. If we
642 want different output, we'll have to add new actions. */
643 SVN_ERR(svn_cmdline_printf(pool,
644 _("\nFetching external item into '%s':\n"),
648 case svn_wc_notify_failed_external:
649 /* If we are currently inside the handling of an externals
650 definition, then we can simply present n->err as a warning
651 and feel confident that after this, we aren't handling that
652 externals definition any longer. */
655 svn_handle_warning2(stderr, n->err, "svn: ");
657 SVN_ERR(svn_cmdline_printf(pool, "\n"));
659 /* Otherwise, we'll just print two warnings. Why? Because
660 svn_handle_warning2() only shows the single "best message",
661 but we have two pretty important ones: that the external at
662 '/some/path' didn't pan out, and then the more specific
663 reason why (from n->err). */
666 svn_error_t *warn_err =
667 svn_error_createf(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL,
668 _("Error handling externals definition for '%s':"),
670 svn_handle_warning2(stderr, warn_err, "svn: ");
671 svn_error_clear(warn_err);
672 svn_handle_warning2(stderr, n->err, "svn: ");
676 case svn_wc_notify_update_started:
677 if (! (nb->in_external ||
681 SVN_ERR(svn_cmdline_printf(pool, _("Updating '%s':\n"),
686 case svn_wc_notify_update_completed:
688 if (SVN_IS_VALID_REVNUM(n->revision))
692 SVN_ERR(svn_cmdline_printf(
693 pool, nb->in_external
694 ? _("Exported external at revision %ld.\n")
695 : _("Exported revision %ld.\n"),
698 else if (nb->is_checkout)
700 SVN_ERR(svn_cmdline_printf(
701 pool, nb->in_external
702 ? _("Checked out external at revision %ld.\n")
703 : _("Checked out revision %ld.\n"),
708 if (nb->received_some_change)
710 nb->received_some_change = FALSE;
711 SVN_ERR(svn_cmdline_printf(
712 pool, nb->in_external
713 ? _("Updated external to revision %ld.\n")
714 : _("Updated to revision %ld.\n"),
719 SVN_ERR(svn_cmdline_printf(
720 pool, nb->in_external
721 ? _("External at revision %ld.\n")
722 : _("At revision %ld.\n"),
727 else /* no revision */
731 SVN_ERR(svn_cmdline_printf(
732 pool, nb->in_external
733 ? _("External export complete.\n")
734 : _("Export complete.\n")));
736 else if (nb->is_checkout)
738 SVN_ERR(svn_cmdline_printf(
739 pool, nb->in_external
740 ? _("External checkout complete.\n")
741 : _("Checkout complete.\n")));
745 SVN_ERR(svn_cmdline_printf(
746 pool, nb->in_external
747 ? _("External update complete.\n")
748 : _("Update complete.\n")));
756 SVN_ERR(svn_cmdline_printf(pool, "\n"));
760 case svn_wc_notify_status_external:
761 SVN_ERR(svn_cmdline_printf(
762 pool, _("\nPerforming status on external item at '%s':\n"),
766 case svn_wc_notify_info_external:
767 SVN_ERR(svn_cmdline_printf(
768 pool, _("\nPerforming info on external item at '%s':\n"),
772 case svn_wc_notify_status_completed:
773 if (SVN_IS_VALID_REVNUM(n->revision))
774 SVN_ERR(svn_cmdline_printf(pool,
775 _("Status against revision: %6ld\n"),
779 case svn_wc_notify_commit_modified:
780 /* xgettext: Align the %s's on this and the following 4 messages */
781 SVN_ERR(svn_cmdline_printf(pool,
782 nb->is_wc_to_repos_copy
783 ? _("Sending copy of %s\n")
788 case svn_wc_notify_commit_added:
789 case svn_wc_notify_commit_copied:
790 if (n->mime_type && svn_mime_type_is_binary(n->mime_type))
792 SVN_ERR(svn_cmdline_printf(pool,
793 nb->is_wc_to_repos_copy
794 ? _("Adding copy of (bin) %s\n")
795 : _("Adding (bin) %s\n"),
800 SVN_ERR(svn_cmdline_printf(pool,
801 nb->is_wc_to_repos_copy
802 ? _("Adding copy of %s\n")
808 case svn_wc_notify_commit_deleted:
809 SVN_ERR(svn_cmdline_printf(pool,
810 nb->is_wc_to_repos_copy
811 ? _("Deleting copy of %s\n")
812 : _("Deleting %s\n"),
816 case svn_wc_notify_commit_replaced:
817 case svn_wc_notify_commit_copied_replaced:
818 SVN_ERR(svn_cmdline_printf(pool,
819 nb->is_wc_to_repos_copy
820 ? _("Replacing copy of %s\n")
821 : _("Replacing %s\n"),
825 case svn_wc_notify_commit_postfix_txdelta:
826 if (! nb->sent_first_txdelta)
828 nb->sent_first_txdelta = TRUE;
829 SVN_ERR(svn_cmdline_printf(pool,
830 _("Transmitting file data ")));
833 SVN_ERR(svn_cmdline_printf(pool, "."));
836 case svn_wc_notify_locked:
837 SVN_ERR(svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
838 path_local, n->lock->owner));
841 case svn_wc_notify_unlocked:
842 SVN_ERR(svn_cmdline_printf(pool, _("'%s' unlocked.\n"),
846 case svn_wc_notify_failed_lock:
847 case svn_wc_notify_failed_unlock:
848 svn_handle_warning2(stderr, n->err, "svn: ");
851 case svn_wc_notify_changelist_set:
852 SVN_ERR(svn_cmdline_printf(pool, "A [%s] %s\n",
853 n->changelist_name, path_local));
856 case svn_wc_notify_changelist_clear:
857 case svn_wc_notify_changelist_moved:
858 SVN_ERR(svn_cmdline_printf(pool,
860 n->changelist_name, path_local));
863 case svn_wc_notify_merge_begin:
864 if (n->merge_range == NULL)
865 SVN_ERR(svn_cmdline_printf(pool,
866 _("--- Merging differences between "
867 "repository URLs into '%s':\n"),
869 else if (n->merge_range->start == n->merge_range->end - 1
870 || n->merge_range->start == n->merge_range->end)
871 SVN_ERR(svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"),
872 n->merge_range->end, path_local));
873 else if (n->merge_range->start - 1 == n->merge_range->end)
874 SVN_ERR(svn_cmdline_printf(pool,
875 _("--- Reverse-merging r%ld into '%s':\n"),
876 n->merge_range->start, path_local));
877 else if (n->merge_range->start < n->merge_range->end)
878 SVN_ERR(svn_cmdline_printf(pool,
879 _("--- Merging r%ld through r%ld into "
881 n->merge_range->start + 1,
882 n->merge_range->end, path_local));
883 else /* n->merge_range->start > n->merge_range->end - 1 */
884 SVN_ERR(svn_cmdline_printf(pool,
885 _("--- Reverse-merging r%ld through r%ld "
887 n->merge_range->start,
888 n->merge_range->end + 1, path_local));
891 case svn_wc_notify_merge_record_info_begin:
894 SVN_ERR(svn_cmdline_printf(pool,
895 _("--- Recording mergeinfo for merge "
896 "between repository URLs into '%s':\n"),
901 if (n->merge_range->start == n->merge_range->end - 1
902 || n->merge_range->start == n->merge_range->end)
903 SVN_ERR(svn_cmdline_printf(
905 _("--- Recording mergeinfo for merge of r%ld into '%s':\n"),
906 n->merge_range->end, path_local));
907 else if (n->merge_range->start - 1 == n->merge_range->end)
908 SVN_ERR(svn_cmdline_printf(
910 _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"),
911 n->merge_range->start, path_local));
912 else if (n->merge_range->start < n->merge_range->end)
913 SVN_ERR(svn_cmdline_printf(
915 _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"),
916 n->merge_range->start + 1, n->merge_range->end, path_local));
917 else /* n->merge_range->start > n->merge_range->end - 1 */
918 SVN_ERR(svn_cmdline_printf(
920 _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"),
921 n->merge_range->start, n->merge_range->end + 1, path_local));
925 case svn_wc_notify_merge_elide_info:
926 SVN_ERR(svn_cmdline_printf(pool,
927 _("--- Eliding mergeinfo from '%s':\n"),
931 case svn_wc_notify_foreign_merge_begin:
932 if (n->merge_range == NULL)
933 SVN_ERR(svn_cmdline_printf(pool,
934 _("--- Merging differences between "
935 "foreign repository URLs into '%s':\n"),
937 else if (n->merge_range->start == n->merge_range->end - 1
938 || n->merge_range->start == n->merge_range->end)
939 SVN_ERR(svn_cmdline_printf(pool,
940 _("--- Merging (from foreign repository) "
941 "r%ld into '%s':\n"),
942 n->merge_range->end, path_local));
943 else if (n->merge_range->start - 1 == n->merge_range->end)
944 SVN_ERR(svn_cmdline_printf(pool,
945 _("--- Reverse-merging (from foreign "
946 "repository) r%ld into '%s':\n"),
947 n->merge_range->start, path_local));
948 else if (n->merge_range->start < n->merge_range->end)
949 SVN_ERR(svn_cmdline_printf(pool,
950 _("--- Merging (from foreign repository) "
951 "r%ld through r%ld into '%s':\n"),
952 n->merge_range->start + 1,
953 n->merge_range->end, path_local));
954 else /* n->merge_range->start > n->merge_range->end - 1 */
955 SVN_ERR(svn_cmdline_printf(pool,
956 _("--- Reverse-merging (from foreign "
957 "repository) r%ld through r%ld into "
959 n->merge_range->start,
960 n->merge_range->end + 1, path_local));
963 case svn_wc_notify_tree_conflict:
964 store_path(nb, nb->conflict_stats->tree_conflicts, path_local);
965 SVN_ERR(svn_cmdline_printf(pool, " C %s\n", path_local));
968 case svn_wc_notify_update_shadowed_add:
969 nb->received_some_change = TRUE;
970 SVN_ERR(svn_cmdline_printf(pool, " A %s\n", path_local));
973 case svn_wc_notify_update_shadowed_update:
974 nb->received_some_change = TRUE;
975 SVN_ERR(svn_cmdline_printf(pool, " U %s\n", path_local));
978 case svn_wc_notify_update_shadowed_delete:
979 nb->received_some_change = TRUE;
980 SVN_ERR(svn_cmdline_printf(pool, " D %s\n", path_local));
983 case svn_wc_notify_property_modified:
984 case svn_wc_notify_property_added:
985 SVN_ERR(svn_cmdline_printf(pool,
986 _("property '%s' set on '%s'\n"),
987 n->prop_name, path_local));
990 case svn_wc_notify_property_deleted:
991 SVN_ERR(svn_cmdline_printf(pool,
992 _("property '%s' deleted from '%s'.\n"),
993 n->prop_name, path_local));
996 case svn_wc_notify_property_deleted_nonexistent:
997 SVN_ERR(svn_cmdline_printf(pool,
998 _("Attempting to delete nonexistent "
999 "property '%s' on '%s'\n"), n->prop_name,
1003 case svn_wc_notify_revprop_set:
1004 SVN_ERR(svn_cmdline_printf(pool,
1005 _("property '%s' set on repository revision %ld\n"),
1006 n->prop_name, n->revision));
1009 case svn_wc_notify_revprop_deleted:
1010 SVN_ERR(svn_cmdline_printf(pool,
1011 _("property '%s' deleted from repository revision %ld\n"),
1012 n->prop_name, n->revision));
1015 case svn_wc_notify_upgraded_path:
1016 SVN_ERR(svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local));
1019 case svn_wc_notify_url_redirect:
1020 SVN_ERR(svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"),
1024 case svn_wc_notify_path_nonexistent:
1025 SVN_ERR(svn_cmdline_printf(pool, "%s\n",
1026 apr_psprintf(pool, _("'%s' is not under version control"),
1030 case svn_wc_notify_conflict_resolver_starting:
1031 /* Once all operations invoke the interactive conflict resolution after
1032 * they've completed, we can run svn_cl__notifier_print_conflict_stats()
1036 case svn_wc_notify_conflict_resolver_done:
1039 case svn_wc_notify_foreign_copy_begin:
1040 if (n->merge_range == NULL)
1042 SVN_ERR(svn_cmdline_printf(
1044 _("--- Copying from foreign repository URL '%s':\n"),
1049 case svn_wc_notify_move_broken:
1050 SVN_ERR(svn_cmdline_printf(pool,
1051 _("Breaking move with source path '%s'\n"),
1055 case svn_wc_notify_cleanup_external:
1056 SVN_ERR(svn_cmdline_printf
1057 (pool, _("Performing cleanup on external item at '%s'.\n"),
1061 case svn_wc_notify_commit_finalizing:
1062 if (nb->sent_first_txdelta)
1064 SVN_ERR(svn_cmdline_printf(pool, _("done\n")));
1066 SVN_ERR(svn_cmdline_printf(pool, _("Committing transaction...\n")));
1073 SVN_ERR(svn_cmdline_fflush(stdout));
1075 return SVN_NO_ERROR;
1078 /* This implements `svn_wc_notify_func2_t'.
1079 * NOTE: This function can't fail, so we just ignore any print errors. */
1081 notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool)
1083 struct notify_baton *nb = baton;
1086 err = notify_body(nb, n, pool);
1088 /* If we had no errors before, print this error to stderr. Else, don't print
1089 anything. The user already knows there were some output errors,
1090 so there is no point in flooding her with an error per notification. */
1091 if (err && !nb->had_print_error)
1093 nb->had_print_error = TRUE;
1095 * Don't print anything on broken pipes. The pipe was likely
1096 * closed by the process at the other end. We expect that
1097 * process to perform error reporting as necessary.
1099 * ### This assumes that there is only one error in a chain for
1100 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
1101 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
1102 svn_handle_error2(err, stderr, FALSE, "svn: ");
1104 svn_error_clear(err);
1108 svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
1109 void **notify_baton_p,
1110 svn_cl__conflict_stats_t *conflict_stats,
1113 struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb));
1115 nb->received_some_change = FALSE;
1116 nb->sent_first_txdelta = FALSE;
1117 nb->is_checkout = FALSE;
1118 nb->is_export = FALSE;
1119 nb->is_wc_to_repos_copy = FALSE;
1120 nb->in_external = 0;
1121 nb->had_print_error = FALSE;
1122 nb->conflict_stats = conflict_stats;
1123 SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
1125 *notify_func_p = notify;
1126 *notify_baton_p = nb;
1127 return SVN_NO_ERROR;
1131 svn_cl__notifier_mark_checkout(void *baton)
1133 struct notify_baton *nb = baton;
1135 nb->is_checkout = TRUE;
1136 return SVN_NO_ERROR;
1140 svn_cl__notifier_mark_export(void *baton)
1142 struct notify_baton *nb = baton;
1144 nb->is_export = TRUE;
1145 return SVN_NO_ERROR;
1149 svn_cl__notifier_mark_wc_to_repos_copy(void *baton)
1151 struct notify_baton *nb = baton;
1153 nb->is_wc_to_repos_copy = TRUE;
1154 return SVN_NO_ERROR;
1158 svn_cl__check_externals_failed_notify_wrapper(void *baton,
1159 const svn_wc_notify_t *n,
1162 struct svn_cl__check_externals_failed_notify_baton *nwb = baton;
1164 if (n->action == svn_wc_notify_failed_external)
1165 nwb->had_externals_error = TRUE;
1167 if (nwb->wrapped_func)
1168 nwb->wrapped_func(nwb->wrapped_baton, n, pool);