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 #define APR_WANT_STRFUNC
25 #define APR_WANT_STDIO
28 #include "svn_cmdline.h"
29 #include "svn_compat.h"
31 #include "svn_props.h"
35 #include "svn_private_config.h"
36 #include "private/svn_string_private.h"
41 /* Baton for log_entry_receiver() and log_entry_receiver_xml(). */
42 struct log_receiver_baton
45 svn_client_ctx_t *ctx;
47 /* Level of merge revision nesting */
48 apr_size_t merge_depth;
50 /* collect counters? */
53 /* total revision counters */
54 apr_int64_t revisions;
56 apr_int64_t message_lines;
58 /* part that came from merges */
60 apr_int64_t merged_revs;
61 apr_int64_t merged_changes;
62 apr_int64_t merged_message_lines;
66 /* Implement `svn_log_entry_receiver_t', printing the logs in
67 * a human-readable and machine-parseable format.
69 * BATON is of type `struct log_receiver_baton'.
72 log_entry_receiver(void *baton,
73 svn_log_entry_t *log_entry,
76 struct log_receiver_baton *lb = baton;
81 if (lb->ctx->cancel_func)
82 SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
84 if (! SVN_IS_VALID_REVNUM(log_entry->revision))
90 /* if we don't want counters, we are done */
94 /* extract the message and do all the other counting */
95 svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
96 if (log_entry->revision == 0 && message == NULL)
105 int count = svn_cstring_count_newlines(message) + 1;
106 lb->message_lines += count;
108 lb->merged_message_lines += count;
111 if (log_entry->changed_paths2)
113 unsigned count = apr_hash_count(log_entry->changed_paths2);
114 lb->changes += count;
116 lb->merged_changes += count;
119 if (log_entry->has_children)
128 /* This implements the `svn_opt_subcommand_t' interface. */
130 svn_cl__null_log(apr_getopt_t *os,
134 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
135 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
136 apr_array_header_t *targets;
137 struct log_receiver_baton lb = { 0 };
140 apr_array_header_t *revprops;
141 svn_opt_revision_t target_peg_revision;
142 const char *target_path_or_url;
144 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
148 /* Add "." if user passed 0 arguments */
149 svn_opt_push_implicit_dot_target(targets, pool);
151 /* Determine if they really want a two-revision range. */
152 if (opt_state->used_change_arg)
154 if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1)
156 return svn_error_create
157 (SVN_ERR_CLIENT_BAD_REVISION, NULL,
158 _("-c and -r are mutually exclusive"));
160 for (i = 0; i < opt_state->revision_ranges->nelts; i++)
162 svn_opt_revision_range_t *range;
163 range = APR_ARRAY_IDX(opt_state->revision_ranges, i,
164 svn_opt_revision_range_t *);
165 if (range->start.value.number < range->end.value.number)
166 range->start.value.number++;
168 range->end.value.number++;
172 /* Parse the first target into path-or-url and peg revision. */
173 target = APR_ARRAY_IDX(targets, 0, const char *);
174 SVN_ERR(svn_opt_parse_path(&target_peg_revision, &target_path_or_url,
176 if (target_peg_revision.kind == svn_opt_revision_unspecified)
177 target_peg_revision.kind = (svn_path_is_url(target)
178 ? svn_opt_revision_head
179 : svn_opt_revision_working);
180 APR_ARRAY_IDX(targets, 0, const char *) = target_path_or_url;
182 if (svn_path_is_url(target))
184 for (i = 1; i < targets->nelts; i++)
186 target = APR_ARRAY_IDX(targets, i, const char *);
188 if (svn_path_is_url(target) || target[0] == '/')
189 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
190 _("Only relative paths can be specified"
191 " after a URL for 'svnbench log', "
192 "but '%s' is not a relative path"),
198 lb.quiet = opt_state->quiet;
200 revprops = apr_array_make(pool, 3, sizeof(char *));
201 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
202 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
203 if (!opt_state->quiet)
204 APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
205 SVN_ERR(svn_client_log5(targets,
206 &target_peg_revision,
207 opt_state->revision_ranges,
210 opt_state->stop_on_copy,
211 opt_state->use_merge_history,
218 if (!opt_state->quiet)
220 if (opt_state->use_merge_history)
221 SVN_ERR(svn_cmdline_printf(pool,
222 _("%15s revisions, %15s merged in %s merges\n"
223 "%15s msg lines, %15s in merged revisions\n"
224 "%15s changes, %15s in merged revisions\n"),
225 svn__ui64toa_sep(lb.revisions, ',', pool),
226 svn__ui64toa_sep(lb.merged_revs, ',', pool),
227 svn__ui64toa_sep(lb.merges, ',', pool),
228 svn__ui64toa_sep(lb.message_lines, ',', pool),
229 svn__ui64toa_sep(lb.merged_message_lines, ',', pool),
230 svn__ui64toa_sep(lb.changes, ',', pool),
231 svn__ui64toa_sep(lb.merged_changes, ',', pool)));
233 SVN_ERR(svn_cmdline_printf(pool,
237 svn__ui64toa_sep(lb.revisions, ',', pool),
238 svn__ui64toa_sep(lb.message_lines, ',', pool),
239 svn__ui64toa_sep(lb.changes, ',', pool)));