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;
55 svn_boolean_t in_external;
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__notifier_print_conflict_stats(void *baton, apr_pool_t *scratch_pool)
150 struct notify_baton *nb = baton;
151 int n_text = apr_hash_count(nb->conflict_stats->text_conflicts);
152 int n_prop = apr_hash_count(nb->conflict_stats->prop_conflicts);
153 int n_tree = apr_hash_count(nb->conflict_stats->tree_conflicts);
154 int n_text_r = nb->conflict_stats->text_conflicts_resolved;
155 int n_prop_r = nb->conflict_stats->prop_conflicts_resolved;
156 int n_tree_r = nb->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 || nb->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 (nb->conflict_stats->skipped_paths > 0)
199 SVN_ERR(svn_cmdline_printf(scratch_pool,
200 _(" Skipped paths: %d\n"),
201 nb->conflict_stats->skipped_paths));
206 /* This implements `svn_wc_notify_func2_t'.
207 * NOTE: This function can't fail, so we just ignore any print errors. */
209 notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool)
211 struct notify_baton *nb = baton;
212 char statchar_buf[5] = " ";
213 const char *path_local;
221 path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path,
223 else /* skip nb->path_prefix, if it's non-null */
224 path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path,
230 case svn_wc_notify_skip:
231 nb->conflict_stats->skipped_paths++;
232 if (n->content_state == svn_wc_notify_state_missing)
234 if ((err = svn_cmdline_printf
235 (pool, _("Skipped missing target: '%s'\n"),
239 else if (n->content_state == svn_wc_notify_state_source_missing)
241 if ((err = svn_cmdline_printf
242 (pool, _("Skipped target: '%s' -- copy-source is missing\n"),
248 if ((err = svn_cmdline_printf
249 (pool, _("Skipped '%s'\n"), path_local)))
253 case svn_wc_notify_update_skip_obstruction:
254 nb->conflict_stats->skipped_paths++;
255 if ((err = svn_cmdline_printf(
256 pool, _("Skipped '%s' -- An obstructing working copy was found\n"),
260 case svn_wc_notify_update_skip_working_only:
261 nb->conflict_stats->skipped_paths++;
262 if ((err = svn_cmdline_printf(
263 pool, _("Skipped '%s' -- Has no versioned parent\n"),
267 case svn_wc_notify_update_skip_access_denied:
268 nb->conflict_stats->skipped_paths++;
269 if ((err = svn_cmdline_printf(
270 pool, _("Skipped '%s' -- Access denied\n"),
274 case svn_wc_notify_skip_conflicted:
275 nb->conflict_stats->skipped_paths++;
276 if ((err = svn_cmdline_printf(
277 pool, _("Skipped '%s' -- Node remains in conflict\n"),
281 case svn_wc_notify_update_delete:
282 case svn_wc_notify_exclude:
283 nb->received_some_change = TRUE;
284 if ((err = svn_cmdline_printf(pool, "D %s\n", path_local)))
287 case svn_wc_notify_update_broken_lock:
288 if ((err = svn_cmdline_printf(pool, "B %s\n", path_local)))
292 case svn_wc_notify_update_external_removed:
293 nb->received_some_change = TRUE;
294 if (n->err && n->err->message)
296 if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n",
297 path_local, n->err->message)))
302 if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n",
308 case svn_wc_notify_left_local_modifications:
309 if ((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 if ((err = svn_cmdline_printf(pool, "R %s\n", path_local)))
320 case svn_wc_notify_update_add:
321 nb->received_some_change = TRUE;
322 if (n->content_state == svn_wc_notify_state_conflicted)
324 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
325 if ((err = svn_cmdline_printf(pool, "C %s\n", path_local)))
330 if ((err = svn_cmdline_printf(pool, "A %s\n", path_local)))
335 case svn_wc_notify_exists:
336 nb->received_some_change = TRUE;
337 if (n->content_state == svn_wc_notify_state_conflicted)
339 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
340 statchar_buf[0] = 'C';
343 statchar_buf[0] = 'E';
345 if (n->prop_state == svn_wc_notify_state_conflicted)
347 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
348 statchar_buf[1] = 'C';
350 else if (n->prop_state == svn_wc_notify_state_merged)
351 statchar_buf[1] = 'G';
353 if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local)))
357 case svn_wc_notify_restore:
358 if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"),
363 case svn_wc_notify_revert:
364 if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"),
369 case svn_wc_notify_failed_revert:
370 if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- "
371 "try updating instead.\n"),
376 case svn_wc_notify_resolved:
377 if ((err = svn_cmdline_printf(pool,
378 _("Resolved conflicted state of '%s'\n"),
383 case svn_wc_notify_add:
384 /* We *should* only get the MIME_TYPE if PATH is a file. If we
385 do get it, and the mime-type is not textual, note that this
386 is a binary addition. */
387 if (n->mime_type && (svn_mime_type_is_binary(n->mime_type)))
389 if ((err = svn_cmdline_printf(pool, "A (bin) %s\n",
395 if ((err = svn_cmdline_printf(pool, "A %s\n",
401 case svn_wc_notify_delete:
402 nb->received_some_change = TRUE;
403 if ((err = svn_cmdline_printf(pool, "D %s\n",
408 case svn_wc_notify_patch:
410 nb->received_some_change = TRUE;
411 if (n->content_state == svn_wc_notify_state_conflicted)
413 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
414 statchar_buf[0] = 'C';
416 else if (n->kind == svn_node_file)
418 if (n->content_state == svn_wc_notify_state_merged)
419 statchar_buf[0] = 'G';
420 else if (n->content_state == svn_wc_notify_state_changed)
421 statchar_buf[0] = 'U';
424 if (n->prop_state == svn_wc_notify_state_conflicted)
426 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
427 statchar_buf[1] = 'C';
429 else if (n->prop_state == svn_wc_notify_state_changed)
430 statchar_buf[1] = 'U';
432 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
434 if ((err = svn_cmdline_printf(pool, "%s %s\n",
435 statchar_buf, path_local)))
441 case svn_wc_notify_patch_applied_hunk:
442 nb->received_some_change = TRUE;
443 if (n->hunk_original_start != n->hunk_matched_line)
449 if (n->hunk_matched_line > n->hunk_original_start)
451 /* If we are patching from the start of an empty file,
452 it is nicer to show offset 0 */
453 if (n->hunk_original_start == 0 && n->hunk_matched_line == 1)
454 off = 0; /* No offset, just adding */
456 off = n->hunk_matched_line - n->hunk_original_start;
462 off = n->hunk_original_start - n->hunk_matched_line;
466 /* ### We're creating the localized strings without
467 * ### APR_INT64_T_FMT since it isn't translator-friendly */
473 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
476 err = svn_cmdline_printf(pool,
479 " and fuzz %lu (%s)\n",
481 n->hunk_original_start,
482 n->hunk_original_length,
483 n->hunk_modified_start,
484 n->hunk_modified_length,
485 minus, off, n->hunk_fuzz,
490 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
493 err = svn_cmdline_printf(pool,
498 n->hunk_original_start,
499 n->hunk_original_length,
500 n->hunk_modified_start,
501 n->hunk_modified_length,
502 minus, off, n->hunk_fuzz);
513 s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
515 err = svn_cmdline_printf(pool,
517 "%"APR_UINT64_T_FMT" (%s)\n",
519 n->hunk_original_start,
520 n->hunk_original_length,
521 n->hunk_modified_start,
522 n->hunk_modified_length,
523 minus, off, n->prop_name);
527 s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
529 err = svn_cmdline_printf(pool,
531 "%"APR_UINT64_T_FMT"\n",
533 n->hunk_original_start,
534 n->hunk_original_length,
535 n->hunk_modified_start,
536 n->hunk_modified_length,
544 else if (n->hunk_fuzz)
547 err = svn_cmdline_printf(pool,
548 _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
549 "with fuzz %lu (%s)\n"),
550 n->hunk_original_start,
551 n->hunk_original_length,
552 n->hunk_modified_start,
553 n->hunk_modified_length,
557 err = svn_cmdline_printf(pool,
558 _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
560 n->hunk_original_start,
561 n->hunk_original_length,
562 n->hunk_modified_start,
563 n->hunk_modified_length,
571 case svn_wc_notify_patch_rejected_hunk:
572 nb->received_some_change = TRUE;
575 err = svn_cmdline_printf(pool,
577 "## -%lu,%lu +%lu,%lu ## (%s)\n"),
578 n->hunk_original_start,
579 n->hunk_original_length,
580 n->hunk_modified_start,
581 n->hunk_modified_length,
584 err = svn_cmdline_printf(pool,
586 "@@ -%lu,%lu +%lu,%lu @@\n"),
587 n->hunk_original_start,
588 n->hunk_original_length,
589 n->hunk_modified_start,
590 n->hunk_modified_length);
595 case svn_wc_notify_patch_hunk_already_applied:
596 nb->received_some_change = TRUE;
598 err = svn_cmdline_printf(pool,
600 "## -%lu,%lu +%lu,%lu ## "
601 "already applied (%s)\n"),
602 n->hunk_original_start,
603 n->hunk_original_length,
604 n->hunk_modified_start,
605 n->hunk_modified_length,
608 err = svn_cmdline_printf(pool,
610 "@@ -%lu,%lu +%lu,%lu @@ "
611 "already applied\n"),
612 n->hunk_original_start,
613 n->hunk_original_length,
614 n->hunk_modified_start,
615 n->hunk_modified_length);
620 case svn_wc_notify_update_update:
621 case svn_wc_notify_merge_record_info:
623 if (n->content_state == svn_wc_notify_state_conflicted)
625 store_path(nb, nb->conflict_stats->text_conflicts, path_local);
626 statchar_buf[0] = 'C';
628 else if (n->kind == svn_node_file)
630 if (n->content_state == svn_wc_notify_state_merged)
631 statchar_buf[0] = 'G';
632 else if (n->content_state == svn_wc_notify_state_changed)
633 statchar_buf[0] = 'U';
636 if (n->prop_state == svn_wc_notify_state_conflicted)
638 store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
639 statchar_buf[1] = 'C';
641 else if (n->prop_state == svn_wc_notify_state_merged)
642 statchar_buf[1] = 'G';
643 else if (n->prop_state == svn_wc_notify_state_changed)
644 statchar_buf[1] = 'U';
646 if (n->lock_state == svn_wc_notify_lock_state_unlocked)
647 statchar_buf[2] = 'B';
649 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
650 nb->received_some_change = TRUE;
652 if (statchar_buf[0] != ' ' || statchar_buf[1] != ' '
653 || statchar_buf[2] != ' ')
655 if ((err = svn_cmdline_printf(pool, "%s %s\n",
656 statchar_buf, path_local)))
662 case svn_wc_notify_update_external:
663 /* Remember that we're now "inside" an externals definition. */
664 nb->in_external = TRUE;
666 /* Currently this is used for checkouts and switches too. If we
667 want different output, we'll have to add new actions. */
668 if ((err = svn_cmdline_printf(pool,
669 _("\nFetching external item into '%s':\n"),
674 case svn_wc_notify_failed_external:
675 /* If we are currently inside the handling of an externals
676 definition, then we can simply present n->err as a warning
677 and feel confident that after this, we aren't handling that
678 externals definition any longer. */
681 svn_handle_warning2(stderr, n->err, "svn: ");
682 nb->in_external = FALSE;
683 if ((err = svn_cmdline_printf(pool, "\n")))
686 /* Otherwise, we'll just print two warnings. Why? Because
687 svn_handle_warning2() only shows the single "best message",
688 but we have two pretty important ones: that the external at
689 '/some/path' didn't pan out, and then the more specific
690 reason why (from n->err). */
693 svn_error_t *warn_err =
694 svn_error_createf(SVN_ERR_BASE, NULL,
695 _("Error handling externals definition for '%s':"),
697 svn_handle_warning2(stderr, warn_err, "svn: ");
698 svn_error_clear(warn_err);
699 svn_handle_warning2(stderr, n->err, "svn: ");
703 case svn_wc_notify_update_started:
704 if (! (nb->in_external ||
708 if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"),
714 case svn_wc_notify_update_completed:
716 if (SVN_IS_VALID_REVNUM(n->revision))
720 if ((err = svn_cmdline_printf
721 (pool, nb->in_external
722 ? _("Exported external at revision %ld.\n")
723 : _("Exported revision %ld.\n"),
727 else if (nb->is_checkout)
729 if ((err = svn_cmdline_printf
730 (pool, nb->in_external
731 ? _("Checked out external at revision %ld.\n")
732 : _("Checked out revision %ld.\n"),
738 if (nb->received_some_change)
740 nb->received_some_change = FALSE;
741 if ((err = svn_cmdline_printf
742 (pool, nb->in_external
743 ? _("Updated external to revision %ld.\n")
744 : _("Updated to revision %ld.\n"),
750 if ((err = svn_cmdline_printf
751 (pool, nb->in_external
752 ? _("External at revision %ld.\n")
753 : _("At revision %ld.\n"),
759 else /* no revision */
763 if ((err = svn_cmdline_printf
764 (pool, nb->in_external
765 ? _("External export complete.\n")
766 : _("Export complete.\n"))))
769 else if (nb->is_checkout)
771 if ((err = svn_cmdline_printf
772 (pool, nb->in_external
773 ? _("External checkout complete.\n")
774 : _("Checkout complete.\n"))))
779 if ((err = svn_cmdline_printf
780 (pool, nb->in_external
781 ? _("External update complete.\n")
782 : _("Update complete.\n"))))
790 nb->in_external = FALSE;
791 if ((err = svn_cmdline_printf(pool, "\n")))
796 case svn_wc_notify_status_external:
797 if ((err = svn_cmdline_printf
798 (pool, _("\nPerforming status on external item at '%s':\n"),
803 case svn_wc_notify_status_completed:
804 if (SVN_IS_VALID_REVNUM(n->revision))
805 if ((err = svn_cmdline_printf(pool,
806 _("Status against revision: %6ld\n"),
811 case svn_wc_notify_commit_modified:
812 /* xgettext: Align the %s's on this and the following 4 messages */
813 if ((err = svn_cmdline_printf(pool,
814 nb->is_wc_to_repos_copy
815 ? _("Sending copy of %s\n")
821 case svn_wc_notify_commit_added:
822 case svn_wc_notify_commit_copied:
823 if (n->mime_type && svn_mime_type_is_binary(n->mime_type))
825 if ((err = svn_cmdline_printf(pool,
826 nb->is_wc_to_repos_copy
827 ? _("Adding copy of (bin) %s\n")
828 : _("Adding (bin) %s\n"),
834 if ((err = svn_cmdline_printf(pool,
835 nb->is_wc_to_repos_copy
836 ? _("Adding copy of %s\n")
843 case svn_wc_notify_commit_deleted:
844 if ((err = svn_cmdline_printf(pool,
845 nb->is_wc_to_repos_copy
846 ? _("Deleting copy of %s\n")
847 : _("Deleting %s\n"),
852 case svn_wc_notify_commit_replaced:
853 case svn_wc_notify_commit_copied_replaced:
854 if ((err = svn_cmdline_printf(pool,
855 nb->is_wc_to_repos_copy
856 ? _("Replacing copy of %s\n")
857 : _("Replacing %s\n"),
862 case svn_wc_notify_commit_postfix_txdelta:
863 if (! nb->sent_first_txdelta)
865 nb->sent_first_txdelta = TRUE;
866 if ((err = svn_cmdline_printf(pool,
867 _("Transmitting file data "))))
871 if ((err = svn_cmdline_printf(pool, ".")))
875 case svn_wc_notify_locked:
876 if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
877 path_local, n->lock->owner)))
881 case svn_wc_notify_unlocked:
882 if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"),
887 case svn_wc_notify_failed_lock:
888 case svn_wc_notify_failed_unlock:
889 svn_handle_warning2(stderr, n->err, "svn: ");
892 case svn_wc_notify_changelist_set:
893 if ((err = svn_cmdline_printf(pool, "A [%s] %s\n",
894 n->changelist_name, path_local)))
898 case svn_wc_notify_changelist_clear:
899 case svn_wc_notify_changelist_moved:
900 if ((err = svn_cmdline_printf(pool,
902 n->changelist_name, path_local)))
906 case svn_wc_notify_merge_begin:
907 if (n->merge_range == NULL)
908 err = svn_cmdline_printf(pool,
909 _("--- Merging differences between "
910 "repository URLs into '%s':\n"),
912 else if (n->merge_range->start == n->merge_range->end - 1
913 || n->merge_range->start == n->merge_range->end)
914 err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"),
915 n->merge_range->end, path_local);
916 else if (n->merge_range->start - 1 == n->merge_range->end)
917 err = svn_cmdline_printf(pool,
918 _("--- Reverse-merging r%ld into '%s':\n"),
919 n->merge_range->start, path_local);
920 else if (n->merge_range->start < n->merge_range->end)
921 err = svn_cmdline_printf(pool,
922 _("--- Merging r%ld through r%ld into "
924 n->merge_range->start + 1,
925 n->merge_range->end, path_local);
926 else /* n->merge_range->start > n->merge_range->end - 1 */
927 err = svn_cmdline_printf(pool,
928 _("--- Reverse-merging r%ld through r%ld "
930 n->merge_range->start,
931 n->merge_range->end + 1, path_local);
936 case svn_wc_notify_merge_record_info_begin:
939 err = svn_cmdline_printf(pool,
940 _("--- Recording mergeinfo for merge "
941 "between repository URLs into '%s':\n"),
946 if (n->merge_range->start == n->merge_range->end - 1
947 || n->merge_range->start == n->merge_range->end)
948 err = svn_cmdline_printf(
950 _("--- Recording mergeinfo for merge of r%ld into '%s':\n"),
951 n->merge_range->end, path_local);
952 else if (n->merge_range->start - 1 == n->merge_range->end)
953 err = svn_cmdline_printf(
955 _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"),
956 n->merge_range->start, path_local);
957 else if (n->merge_range->start < n->merge_range->end)
958 err = svn_cmdline_printf(
960 _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"),
961 n->merge_range->start + 1, n->merge_range->end, path_local);
962 else /* n->merge_range->start > n->merge_range->end - 1 */
963 err = svn_cmdline_printf(
965 _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"),
966 n->merge_range->start, n->merge_range->end + 1, path_local);
973 case svn_wc_notify_merge_elide_info:
974 if ((err = svn_cmdline_printf(pool,
975 _("--- Eliding mergeinfo from '%s':\n"),
980 case svn_wc_notify_foreign_merge_begin:
981 if (n->merge_range == NULL)
982 err = svn_cmdline_printf(pool,
983 _("--- Merging differences between "
984 "foreign repository URLs into '%s':\n"),
986 else if (n->merge_range->start == n->merge_range->end - 1
987 || n->merge_range->start == n->merge_range->end)
988 err = svn_cmdline_printf(pool,
989 _("--- Merging (from foreign repository) "
990 "r%ld into '%s':\n"),
991 n->merge_range->end, path_local);
992 else if (n->merge_range->start - 1 == n->merge_range->end)
993 err = svn_cmdline_printf(pool,
994 _("--- Reverse-merging (from foreign "
995 "repository) r%ld into '%s':\n"),
996 n->merge_range->start, path_local);
997 else if (n->merge_range->start < n->merge_range->end)
998 err = svn_cmdline_printf(pool,
999 _("--- Merging (from foreign repository) "
1000 "r%ld through r%ld into '%s':\n"),
1001 n->merge_range->start + 1,
1002 n->merge_range->end, path_local);
1003 else /* n->merge_range->start > n->merge_range->end - 1 */
1004 err = svn_cmdline_printf(pool,
1005 _("--- Reverse-merging (from foreign "
1006 "repository) r%ld through r%ld into "
1008 n->merge_range->start,
1009 n->merge_range->end + 1, path_local);
1014 case svn_wc_notify_tree_conflict:
1015 store_path(nb, nb->conflict_stats->tree_conflicts, path_local);
1016 if ((err = svn_cmdline_printf(pool, " C %s\n", path_local)))
1020 case svn_wc_notify_update_shadowed_add:
1021 nb->received_some_change = TRUE;
1022 if ((err = svn_cmdline_printf(pool, " A %s\n", path_local)))
1026 case svn_wc_notify_update_shadowed_update:
1027 nb->received_some_change = TRUE;
1028 if ((err = svn_cmdline_printf(pool, " U %s\n", path_local)))
1032 case svn_wc_notify_update_shadowed_delete:
1033 nb->received_some_change = TRUE;
1034 if ((err = svn_cmdline_printf(pool, " D %s\n", path_local)))
1038 case svn_wc_notify_property_modified:
1039 case svn_wc_notify_property_added:
1040 err = svn_cmdline_printf(pool,
1041 _("property '%s' set on '%s'\n"),
1042 n->prop_name, path_local);
1047 case svn_wc_notify_property_deleted:
1048 err = svn_cmdline_printf(pool,
1049 _("property '%s' deleted from '%s'.\n"),
1050 n->prop_name, path_local);
1055 case svn_wc_notify_property_deleted_nonexistent:
1056 err = svn_cmdline_printf(pool,
1057 _("Attempting to delete nonexistent "
1058 "property '%s' on '%s'\n"), n->prop_name,
1064 case svn_wc_notify_revprop_set:
1065 err = svn_cmdline_printf(pool,
1066 _("property '%s' set on repository revision %ld\n"),
1067 n->prop_name, n->revision);
1072 case svn_wc_notify_revprop_deleted:
1073 err = svn_cmdline_printf(pool,
1074 _("property '%s' deleted from repository revision %ld\n"),
1075 n->prop_name, n->revision);
1080 case svn_wc_notify_upgraded_path:
1081 err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local);
1086 case svn_wc_notify_url_redirect:
1087 err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"),
1093 case svn_wc_notify_path_nonexistent:
1094 err = svn_cmdline_printf(pool, "%s\n",
1095 apr_psprintf(pool, _("'%s' is not under version control"),
1101 case svn_wc_notify_conflict_resolver_starting:
1102 /* Once all operations invoke the interactive conflict resolution after
1103 * they've completed, we can run svn_cl__notifier_print_conflict_stats()
1107 case svn_wc_notify_conflict_resolver_done:
1110 case svn_wc_notify_foreign_copy_begin:
1111 if (n->merge_range == NULL)
1113 err = svn_cmdline_printf(
1115 _("--- Copying from foreign repository URL '%s':\n"),
1122 case svn_wc_notify_move_broken:
1123 err = svn_cmdline_printf(pool,
1124 _("Breaking move with source path '%s'\n"),
1134 if ((err = svn_cmdline_fflush(stdout)))
1140 /* If we had no errors before, print this error to stderr. Else, don't print
1141 anything. The user already knows there were some output errors,
1142 so there is no point in flooding her with an error per notification. */
1143 if (!nb->had_print_error)
1145 nb->had_print_error = TRUE;
1147 * Don't print anything on broken pipes. The pipe was likely
1148 * closed by the process at the other end. We expect that
1149 * process to perform error reporting as necessary.
1151 * ### This assumes that there is only one error in a chain for
1152 * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
1153 if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
1154 svn_handle_error2(err, stderr, FALSE, "svn: ");
1156 svn_error_clear(err);
1161 svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
1162 void **notify_baton_p,
1163 svn_cl__conflict_stats_t *conflict_stats,
1166 struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb));
1168 nb->received_some_change = FALSE;
1169 nb->sent_first_txdelta = FALSE;
1170 nb->is_checkout = FALSE;
1171 nb->is_export = FALSE;
1172 nb->is_wc_to_repos_copy = FALSE;
1173 nb->in_external = FALSE;
1174 nb->had_print_error = FALSE;
1175 nb->conflict_stats = conflict_stats;
1176 SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
1178 *notify_func_p = notify;
1179 *notify_baton_p = nb;
1180 return SVN_NO_ERROR;
1184 svn_cl__notifier_mark_checkout(void *baton)
1186 struct notify_baton *nb = baton;
1188 nb->is_checkout = TRUE;
1189 return SVN_NO_ERROR;
1193 svn_cl__notifier_mark_export(void *baton)
1195 struct notify_baton *nb = baton;
1197 nb->is_export = TRUE;
1198 return SVN_NO_ERROR;
1202 svn_cl__notifier_mark_wc_to_repos_copy(void *baton)
1204 struct notify_baton *nb = baton;
1206 nb->is_wc_to_repos_copy = TRUE;
1207 return SVN_NO_ERROR;
1211 svn_cl__check_externals_failed_notify_wrapper(void *baton,
1212 const svn_wc_notify_t *n,
1215 struct svn_cl__check_externals_failed_notify_baton *nwb = baton;
1217 if (n->action == svn_wc_notify_failed_external)
1218 nwb->had_externals_error = TRUE;
1220 if (nwb->wrapped_func)
1221 nwb->wrapped_func(nwb->wrapped_baton, n, pool);