2 * log-cmd.c -- Display log messages
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 #include <apr_fnmatch.h>
26 #include "svn_client.h"
27 #include "svn_compat.h"
28 #include "svn_dirent_uri.h"
29 #include "svn_string.h"
31 #include "svn_error.h"
32 #include "svn_sorts.h"
35 #include "svn_cmdline.h"
36 #include "svn_props.h"
37 #include "svn_pools.h"
39 #include "private/svn_cmdline_private.h"
40 #include "private/svn_sorts_private.h"
41 #include "private/svn_utf_private.h"
46 #include "svn_private_config.h"
53 /* Display a diff of the subtree TARGET_PATH_OR_URL@TARGET_PEG_REVISION as
54 * it changed in the revision that LOG_ENTRY describes.
56 * Restrict the diff to depth DEPTH. Pass DIFF_EXTENSIONS along to the diff
59 * Write the diff to OUTSTREAM and write any stderr output to ERRSTREAM.
60 * ### How is exit code handled? 0 and 1 -> SVN_NO_ERROR, else an svn error?
61 * ### Should we get rid of ERRSTREAM and use svn_error_t instead?
64 display_diff(const svn_log_entry_t *log_entry,
65 const char *target_path_or_url,
66 const svn_opt_revision_t *target_peg_revision,
68 const char *diff_extensions,
69 svn_stream_t *outstream,
70 svn_stream_t *errstream,
71 svn_client_ctx_t *ctx,
74 apr_array_header_t *diff_options;
75 svn_opt_revision_t start_revision;
76 svn_opt_revision_t end_revision;
78 /* Fall back to "" to get options initialized either way. */
80 diff_options = svn_cstring_split(diff_extensions, " \t\n\r",
85 start_revision.kind = svn_opt_revision_number;
86 start_revision.value.number = log_entry->revision - 1;
87 end_revision.kind = svn_opt_revision_number;
88 end_revision.value.number = log_entry->revision;
90 SVN_ERR(svn_stream_puts(outstream, "\n"));
91 SVN_ERR(svn_client_diff_peg6(diff_options,
94 &start_revision, &end_revision,
97 FALSE /* ignore ancestry */,
98 FALSE /* no diff added */,
99 TRUE /* no diff deleted */,
100 FALSE /* show copies as adds */,
101 FALSE /* ignore content type */,
102 FALSE /* ignore prop diff */,
103 FALSE /* properties only */,
104 FALSE /* use git diff format */,
105 svn_cmdline_output_encoding(pool),
110 SVN_ERR(svn_stream_puts(outstream, _("\n")));
114 /* Return TRUE if STR matches PATTERN. Else, return FALSE. Assumes that
115 * PATTERN is a UTF-8 string prepared for case- and accent-insensitive
116 * comparison via svn_utf__xfrm(). */
118 match(const char *pattern, const char *str, svn_membuf_t *buf)
122 err = svn_utf__xfrm(&str, str, strlen(str), TRUE, TRUE, buf);
125 /* Can't match invalid data. */
126 svn_error_clear(err);
130 return apr_fnmatch(pattern, str, 0) == APR_SUCCESS;
133 /* Return TRUE if SEARCH_PATTERN matches the AUTHOR, DATE, LOG_MESSAGE,
134 * or a path in the set of keys of the CHANGED_PATHS hash. Else, return FALSE.
135 * Any of AUTHOR, DATE, LOG_MESSAGE, and CHANGED_PATHS may be NULL. */
137 match_search_pattern(const char *search_pattern,
140 const char *log_message,
141 apr_hash_t *changed_paths,
145 /* Match any substring containing the pattern, like UNIX 'grep' does. */
146 const char *pattern = apr_psprintf(pool, "*%s*", search_pattern);
148 /* Does the author match the search pattern? */
149 if (author && match(pattern, author, buf))
152 /* Does the date the search pattern? */
153 if (date && match(pattern, date, buf))
156 /* Does the log message the search pattern? */
157 if (log_message && match(pattern, log_message, buf))
162 apr_hash_index_t *hi;
164 /* Does a changed path match the search pattern? */
165 for (hi = apr_hash_first(pool, changed_paths);
167 hi = apr_hash_next(hi))
169 const char *path = apr_hash_this_key(hi);
170 svn_log_changed_path2_t *log_item;
172 if (match(pattern, path, buf))
175 /* Match copy-from paths, too. */
176 log_item = apr_hash_this_val(hi);
177 if (log_item->copyfrom_path
178 && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)
179 && match(pattern, log_item->copyfrom_path, buf))
187 /* Match all search patterns in SEARCH_PATTERNS against AUTHOR, DATE, MESSAGE,
188 * and CHANGED_PATHS. Return TRUE if any pattern matches, else FALSE.
189 * BUF and SCRATCH_POOL are used for temporary allocations. */
191 match_search_patterns(apr_array_header_t *search_patterns,
195 apr_hash_t *changed_paths,
197 apr_pool_t *scratch_pool)
200 svn_boolean_t match = FALSE;
201 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
203 for (i = 0; i < search_patterns->nelts; i++)
205 apr_array_header_t *pattern_group;
208 pattern_group = APR_ARRAY_IDX(search_patterns, i, apr_array_header_t *);
210 /* All patterns within the group must match. */
211 for (j = 0; j < pattern_group->nelts; j++)
215 svn_pool_clear(iterpool);
217 pattern = APR_ARRAY_IDX(pattern_group, j, const char *);
218 match = match_search_pattern(pattern, author, date, message,
219 changed_paths, buf, iterpool);
224 match = (match && j == pattern_group->nelts);
228 svn_pool_destroy(iterpool);
233 /* Implement `svn_log_entry_receiver_t', printing the logs in
234 * a human-readable and machine-parseable format.
236 * BATON is of type `svn_cl__log_receiver_baton'.
238 * First, print a header line. Then if CHANGED_PATHS is non-null,
239 * print all affected paths in a list headed "Changed paths:\n",
240 * immediately following the header line. Then print a newline
241 * followed by the message body, unless BATON->omit_log_message is true.
243 * Here are some examples of the output:
245 * $ svn log -r1847:1846
246 * ------------------------------------------------------------------------
247 * rev 1847: cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
249 * Fix for Issue #694.
251 * * subversion/libsvn_repos/delta.c
252 * (delta_files): Rework the logic in this function to only call
253 * send_text_deltas if there are deltas to send, and within that case,
254 * only use a real delta stream if the caller wants real text deltas.
256 * ------------------------------------------------------------------------
257 * rev 1846: whoever | Wed 1 May 2002 15:23:41 | 1 line
259 * imagine an example log message here
260 * ------------------------------------------------------------------------
264 * $ svn log -r1847:1846 -v
265 * ------------------------------------------------------------------------
266 * rev 1847: cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
268 * M /trunk/subversion/libsvn_repos/delta.c
270 * Fix for Issue #694.
272 * * subversion/libsvn_repos/delta.c
273 * (delta_files): Rework the logic in this function to only call
274 * send_text_deltas if there are deltas to send, and within that case,
275 * only use a real delta stream if the caller wants real text deltas.
277 * ------------------------------------------------------------------------
278 * rev 1846: whoever | Wed 1 May 2002 15:23:41 | 1 line
280 * M /trunk/notes/fs_dumprestore.txt
281 * M /trunk/subversion/libsvn_repos/dump.c
283 * imagine an example log message here
284 * ------------------------------------------------------------------------
288 * $ svn log -r1847:1846 -q
289 * ------------------------------------------------------------------------
290 * rev 1847: cmpilato | Wed 1 May 2002 15:44:26
291 * ------------------------------------------------------------------------
292 * rev 1846: whoever | Wed 1 May 2002 15:23:41
293 * ------------------------------------------------------------------------
297 * $ svn log -r1847:1846 -qv
298 * ------------------------------------------------------------------------
299 * rev 1847: cmpilato | Wed 1 May 2002 15:44:26
301 * M /trunk/subversion/libsvn_repos/delta.c
302 * ------------------------------------------------------------------------
303 * rev 1846: whoever | Wed 1 May 2002 15:23:41
305 * M /trunk/notes/fs_dumprestore.txt
306 * M /trunk/subversion/libsvn_repos/dump.c
307 * ------------------------------------------------------------------------
311 svn_cl__log_entry_receiver(void *baton,
312 svn_log_entry_t *log_entry,
315 svn_cl__log_receiver_baton *lb = baton;
320 if (lb->ctx->cancel_func)
321 SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
323 svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
325 if (log_entry->revision == 0 && message == NULL)
328 if (! SVN_IS_VALID_REVNUM(log_entry->revision))
331 apr_array_pop(lb->merge_stack);
336 /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=807
337 for more on the fallback fuzzy conversions below. */
340 author = _("(no author)");
343 /* Convert date to a format for humans. */
344 SVN_ERR(svn_cl__time_cstring_to_human_cstring(&date, date, pool));
346 date = _("(no date)");
348 if (! lb->omit_log_message && message == NULL)
351 if (lb->search_patterns &&
352 ! match_search_patterns(lb->search_patterns, author, date, message,
353 log_entry->changed_paths2, &lb->buffer, pool))
355 if (log_entry->has_children)
357 if (! lb->merge_stack)
358 lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t));
360 APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
366 SVN_ERR(svn_cmdline_printf(pool,
367 SVN_CL__LOG_SEP_STRING "r%ld | %s | %s",
368 log_entry->revision, author, date));
372 /* Number of lines in the msg. */
373 int lines = svn_cstring_count_newlines(message) + 1;
375 SVN_ERR(svn_cmdline_printf(pool,
376 Q_(" | %d line", " | %d lines", lines),
380 SVN_ERR(svn_cmdline_printf(pool, "\n"));
382 if (log_entry->changed_paths2)
384 apr_array_header_t *sorted_paths;
386 apr_pool_t *iterpool;
388 /* Get an array of sorted hash keys. */
389 sorted_paths = svn_sort__hash(log_entry->changed_paths2,
390 svn_sort_compare_items_as_paths, pool);
392 SVN_ERR(svn_cmdline_printf(pool,
393 _("Changed paths:\n")));
394 iterpool = svn_pool_create(pool);
395 for (i = 0; i < sorted_paths->nelts; i++)
397 svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i,
399 const char *path = item->key;
400 svn_log_changed_path2_t *log_item = item->value;
401 const char *copy_data = "";
403 svn_pool_clear(iterpool);
405 if (lb->ctx->cancel_func)
406 SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
408 if (log_item->copyfrom_path
409 && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
412 = apr_psprintf(iterpool,
414 log_item->copyfrom_path,
415 log_item->copyfrom_rev);
417 SVN_ERR(svn_cmdline_printf(iterpool, " %c %s%s\n",
418 log_item->action, path,
421 svn_pool_destroy(iterpool);
424 if (lb->merge_stack && lb->merge_stack->nelts > 0)
427 apr_pool_t *iterpool;
429 /* Print the result of merge line */
430 if (log_entry->subtractive_merge)
431 SVN_ERR(svn_cmdline_printf(pool, _("Reverse merged via:")));
433 SVN_ERR(svn_cmdline_printf(pool, _("Merged via:")));
434 iterpool = svn_pool_create(pool);
435 for (i = 0; i < lb->merge_stack->nelts; i++)
437 svn_revnum_t rev = APR_ARRAY_IDX(lb->merge_stack, i, svn_revnum_t);
439 svn_pool_clear(iterpool);
440 SVN_ERR(svn_cmdline_printf(iterpool, " r%ld%c", rev,
441 i == lb->merge_stack->nelts - 1 ?
444 svn_pool_destroy(iterpool);
449 /* A blank line always precedes the log message. */
450 SVN_ERR(svn_cmdline_printf(pool, "\n%s\n", message));
453 SVN_ERR(svn_cmdline_fflush(stdout));
454 SVN_ERR(svn_cmdline_fflush(stderr));
456 /* Print a diff if requested. */
459 svn_stream_t *outstream;
460 svn_stream_t *errstream;
462 SVN_ERR(svn_stream_for_stdout(&outstream, pool));
463 SVN_ERR(svn_stream_for_stderr(&errstream, pool));
465 SVN_ERR(display_diff(log_entry,
466 lb->target_path_or_url, &lb->target_peg_revision,
467 lb->depth, lb->diff_extensions,
468 outstream, errstream,
471 SVN_ERR(svn_stream_close(outstream));
472 SVN_ERR(svn_stream_close(errstream));
475 if (log_entry->has_children)
477 if (! lb->merge_stack)
478 lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t));
480 APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
487 /* This implements `svn_log_entry_receiver_t', printing the logs in XML.
489 * BATON is of type `svn_cl__log_receiver_baton'.
491 * Here is an example of the output; note that the "<log>" and
492 * "</log>" tags are not emitted by this function:
494 * $ svn log --xml -r 1648:1649
498 * <author>david</author>
499 * <date>2002-04-06T16:34:51.428043Z</date>
500 * <msg> * packages/rpm/subversion.spec : Now requires apache 2.0.36.
505 * <author>cmpilato</author>
506 * <date>2002-04-06T17:01:28.185136Z</date>
507 * <msg>Fix error handling when the $EDITOR is needed but unavailable. Ah
508 * ... now that's *much* nicer.
510 * * subversion/clients/cmdline/util.c
511 * (svn_cl__edit_externally): Clean up the "no external editor"
513 * (svn_cl__get_log_message): Wrap "no external editor"
514 * errors with helpful hints about the -m and -F options.
516 * * subversion/libsvn_client/commit.c
517 * (svn_client_commit): Actually capture and propagate "no external
518 * editor" errors.</msg>
524 svn_cl__log_entry_receiver_xml(void *baton,
525 svn_log_entry_t *log_entry,
528 svn_cl__log_receiver_baton *lb = baton;
529 /* Collate whole log message into sb before printing. */
530 svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
536 if (lb->ctx->cancel_func)
537 SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
539 svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
541 if (log_entry->revision == 0 && message == NULL)
544 if (! SVN_IS_VALID_REVNUM(log_entry->revision))
546 svn_xml_make_close_tag(&sb, pool, "logentry");
547 SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
549 apr_array_pop(lb->merge_stack);
554 /* Match search pattern before XML-escaping. */
555 if (lb->search_patterns &&
556 ! match_search_patterns(lb->search_patterns, author, date, message,
557 log_entry->changed_paths2, &lb->buffer, pool))
559 if (log_entry->has_children)
561 if (! lb->merge_stack)
562 lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t));
564 APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
571 author = svn_xml_fuzzy_escape(author, pool);
573 date = svn_xml_fuzzy_escape(date, pool);
575 message = svn_xml_fuzzy_escape(message, pool);
577 revstr = apr_psprintf(pool, "%ld", log_entry->revision);
578 /* <logentry revision="xxx"> */
579 if (lb->merge_stack && lb->merge_stack->nelts > 0)
581 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry",
582 "revision", revstr, "reverse-merge",
583 log_entry->subtractive_merge ? "true" : "false",
589 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry",
590 "revision", revstr, SVN_VA_NULL);
593 /* <author>xxx</author> */
594 svn_cl__xml_tagged_cdata(&sb, pool, "author", author);
596 /* Print the full, uncut, date. This is machine output. */
597 /* According to the docs for svn_log_entry_receiver_t, either
598 NULL or the empty string represents no date. Avoid outputting an
599 empty date element. */
600 if (date && date[0] == '\0')
602 /* <date>xxx</date> */
603 svn_cl__xml_tagged_cdata(&sb, pool, "date", date);
605 if (log_entry->changed_paths2)
607 apr_array_header_t *sorted_paths;
611 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths",
614 /* Get an array of sorted hash keys. */
615 sorted_paths = svn_sort__hash(log_entry->changed_paths2,
616 svn_sort_compare_items_as_paths, pool);
618 for (i = 0; i < sorted_paths->nelts; i++)
620 svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i,
622 const char *path = item->key;
623 svn_log_changed_path2_t *log_item = item->value;
626 action[0] = log_item->action;
628 if (log_item->copyfrom_path
629 && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
631 /* <path action="X" copyfrom-path="xxx" copyfrom-rev="xxx"> */
632 revstr = apr_psprintf(pool, "%ld",
633 log_item->copyfrom_rev);
634 svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
636 "copyfrom-path", log_item->copyfrom_path,
637 "copyfrom-rev", revstr,
638 "kind", svn_cl__node_kind_str_xml(
639 log_item->node_kind),
640 "text-mods", svn_tristate__to_word(
641 log_item->text_modified),
642 "prop-mods", svn_tristate__to_word(
643 log_item->props_modified),
648 /* <path action="X"> */
649 svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
651 "kind", svn_cl__node_kind_str_xml(
652 log_item->node_kind),
653 "text-mods", svn_tristate__to_word(
654 log_item->text_modified),
655 "prop-mods", svn_tristate__to_word(
656 log_item->props_modified),
660 svn_xml_escape_cdata_cstring(&sb, path, pool);
661 svn_xml_make_close_tag(&sb, pool, "path");
665 svn_xml_make_close_tag(&sb, pool, "paths");
671 svn_cl__xml_tagged_cdata(&sb, pool, "msg", message);
674 svn_compat_log_revprops_clear(log_entry->revprops);
675 if (log_entry->revprops && apr_hash_count(log_entry->revprops) > 0)
677 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", SVN_VA_NULL);
678 SVN_ERR(svn_cmdline__print_xml_prop_hash(&sb, log_entry->revprops,
679 FALSE, /* name_only */
681 svn_xml_make_close_tag(&sb, pool, "revprops");
684 if (log_entry->has_children)
686 if (! lb->merge_stack)
687 lb->merge_stack = apr_array_make(lb->pool, 1, sizeof(svn_revnum_t));
689 APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
692 svn_xml_make_close_tag(&sb, pool, "logentry");
694 return svn_cl__error_checked_fputs(sb->data, stdout);
698 /* This implements the `svn_opt_subcommand_t' interface. */
700 svn_cl__log(apr_getopt_t *os,
704 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
705 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
706 apr_array_header_t *targets;
707 svn_cl__log_receiver_baton lb;
710 apr_array_header_t *revprops;
714 if (opt_state->all_revprops)
715 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
716 _("'with-all-revprops' option only valid in"
718 if (opt_state->no_revprops)
719 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
720 _("'with-no-revprops' option only valid in"
722 if (opt_state->revprop_table != NULL)
723 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
724 _("'with-revprop' option only valid in"
729 if (opt_state->show_diff)
730 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
731 _("'diff' option is not supported in "
735 if (opt_state->quiet && opt_state->show_diff)
736 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
737 _("'quiet' and 'diff' options are "
738 "mutually exclusive"));
739 if (opt_state->diff.diff_cmd && (! opt_state->show_diff))
740 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
741 _("'diff-cmd' option requires 'diff' "
743 if (opt_state->diff.internal_diff && (! opt_state->show_diff))
744 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
745 _("'internal-diff' option requires "
747 if (opt_state->extensions && (! opt_state->show_diff))
748 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
749 _("'extensions' option requires 'diff' "
752 if (opt_state->depth != svn_depth_unknown && (! opt_state->show_diff))
753 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
754 _("'depth' option requires 'diff' option"));
756 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
760 /* Add "." if user passed 0 arguments */
761 svn_opt_push_implicit_dot_target(targets, pool);
763 /* Determine if they really want a two-revision range. */
764 if (opt_state->used_change_arg)
766 if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1)
768 return svn_error_create
769 (SVN_ERR_CLIENT_BAD_REVISION, NULL,
770 _("-c and -r are mutually exclusive"));
772 for (i = 0; i < opt_state->revision_ranges->nelts; i++)
774 svn_opt_revision_range_t *range;
775 range = APR_ARRAY_IDX(opt_state->revision_ranges, i,
776 svn_opt_revision_range_t *);
777 if (range->start.value.number < range->end.value.number)
778 range->start.value.number++;
780 range->end.value.number++;
784 /* Parse the first target into path-or-url and peg revision. */
785 target = APR_ARRAY_IDX(targets, 0, const char *);
786 SVN_ERR(svn_opt_parse_path(&lb.target_peg_revision, &lb.target_path_or_url,
788 if (lb.target_peg_revision.kind == svn_opt_revision_unspecified)
789 lb.target_peg_revision.kind = (svn_path_is_url(target)
790 ? svn_opt_revision_head
791 : svn_opt_revision_working);
792 APR_ARRAY_IDX(targets, 0, const char *) = lb.target_path_or_url;
794 if (svn_path_is_url(target))
796 for (i = 1; i < targets->nelts; i++)
798 target = APR_ARRAY_IDX(targets, i, const char *);
800 if (svn_path_is_url(target) || target[0] == '/')
801 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
802 _("Only relative paths can be specified"
803 " after a URL for 'svn log', "
804 "but '%s' is not a relative path"),
810 lb.omit_log_message = opt_state->quiet;
811 lb.show_diff = opt_state->show_diff;
812 lb.depth = opt_state->depth == svn_depth_unknown ? svn_depth_infinity
814 lb.diff_extensions = opt_state->extensions;
815 lb.merge_stack = NULL;
816 lb.search_patterns = opt_state->search_patterns;
817 svn_membuf__create(&lb.buffer, 0, pool);
822 /* If output is not incremental, output the XML header and wrap
823 everything in a top-level element. This makes the output in
824 its entirety a well-formed XML document. */
825 if (! opt_state->incremental)
826 SVN_ERR(svn_cl__xml_print_header("log", pool));
828 if (opt_state->all_revprops)
830 else if(opt_state->no_revprops)
832 revprops = apr_array_make(pool, 0, sizeof(char *));
834 else if (opt_state->revprop_table != NULL)
836 apr_hash_index_t *hi;
837 revprops = apr_array_make(pool,
838 apr_hash_count(opt_state->revprop_table),
840 for (hi = apr_hash_first(pool, opt_state->revprop_table);
842 hi = apr_hash_next(hi))
844 const char *property = apr_hash_this_key(hi);
845 svn_string_t *value = apr_hash_this_val(hi);
847 if (value && value->data[0] != '\0')
848 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
849 _("cannot assign with 'with-revprop'"
850 " option (drop the '=')"));
851 APR_ARRAY_PUSH(revprops, const char *) = property;
856 revprops = apr_array_make(pool, 3, sizeof(char *));
857 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
858 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
859 if (!opt_state->quiet)
860 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
862 SVN_ERR(svn_client_log5(targets,
863 &lb.target_peg_revision,
864 opt_state->revision_ranges,
867 opt_state->stop_on_copy,
868 opt_state->use_merge_history,
870 svn_cl__log_entry_receiver_xml,
875 if (! opt_state->incremental)
876 SVN_ERR(svn_cl__xml_print_footer("log", pool));
878 else /* default output format */
880 revprops = apr_array_make(pool, 3, sizeof(char *));
881 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
882 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
883 if (!opt_state->quiet)
884 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
885 SVN_ERR(svn_client_log5(targets,
886 &lb.target_peg_revision,
887 opt_state->revision_ranges,
890 opt_state->stop_on_copy,
891 opt_state->use_merge_history,
893 svn_cl__log_entry_receiver,
898 if (! opt_state->incremental)
899 SVN_ERR(svn_cmdline_printf(pool, SVN_CL__LOG_SEP_STRING));