1/* 2 * log-cmd.c -- Display log messages 3 * 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 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 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 20 * under the License. 21 * ==================================================================== 22 */ 23 24#define APR_WANT_STRFUNC 25#define APR_WANT_STDIO 26#include <apr_want.h> 27 28#include "svn_cmdline.h" 29#include "svn_compat.h" 30#include "svn_path.h" 31#include "svn_props.h" 32 33#include "cl.h" 34 35#include "svn_private_config.h" 36#include "private/svn_string_private.h" 37 38 39/*** Code. ***/ 40 41/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */ 42struct log_receiver_baton 43{ 44 /* Client context. */ 45 svn_client_ctx_t *ctx; 46 47 /* Level of merge revision nesting */ 48 apr_size_t merge_depth; 49 50 /* collect counters? */ 51 svn_boolean_t quiet; 52 53 /* total revision counters */ 54 apr_int64_t revisions; 55 apr_int64_t changes; 56 apr_int64_t message_lines; 57 58 /* part that came from merges */ 59 apr_int64_t merges; 60 apr_int64_t merged_revs; 61 apr_int64_t merged_changes; 62 apr_int64_t merged_message_lines; 63}; 64 65 66/* Implement `svn_log_entry_receiver_t', printing the logs in 67 * a human-readable and machine-parseable format. 68 * 69 * BATON is of type `struct log_receiver_baton'. 70 */ 71static svn_error_t * 72log_entry_receiver(void *baton, 73 svn_log_entry_t *log_entry, 74 apr_pool_t *pool) 75{ 76 struct log_receiver_baton *lb = baton; 77 const char *author; 78 const char *date; 79 const char *message; 80 81 if (lb->ctx->cancel_func) 82 SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton)); 83 84 if (! SVN_IS_VALID_REVNUM(log_entry->revision)) 85 { 86 lb->merge_depth--; 87 return SVN_NO_ERROR; 88 } 89 90 /* if we don't want counters, we are done */ 91 if (lb->quiet) 92 return SVN_NO_ERROR; 93 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) 97 return SVN_NO_ERROR; 98 99 lb->revisions++; 100 if (lb->merge_depth) 101 lb->merged_revs++; 102 103 if (message != NULL) 104 { 105 int count = svn_cstring_count_newlines(message) + 1; 106 lb->message_lines += count; 107 if (lb->merge_depth) 108 lb->merged_message_lines += count; 109 } 110 111 if (log_entry->changed_paths2) 112 { 113 unsigned count = apr_hash_count(log_entry->changed_paths2); 114 lb->changes += count; 115 if (lb->merge_depth) 116 lb->merged_changes += count; 117 } 118 119 if (log_entry->has_children) 120 { 121 lb->merge_depth++; 122 lb->merges++; 123 } 124 125 return SVN_NO_ERROR; 126} 127 128/* This implements the `svn_opt_subcommand_t' interface. */ 129svn_error_t * 130svn_cl__null_log(apr_getopt_t *os, 131 void *baton, 132 apr_pool_t *pool) 133{ 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 }; 138 const char *target; 139 int i; 140 apr_array_header_t *revprops; 141 svn_opt_revision_t target_peg_revision; 142 const char *target_path_or_url; 143 144 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 145 opt_state->targets, 146 ctx, FALSE, pool)); 147 148 /* Add "." if user passed 0 arguments */ 149 svn_opt_push_implicit_dot_target(targets, pool); 150 151 /* Determine if they really want a two-revision range. */ 152 if (opt_state->used_change_arg) 153 { 154 if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1) 155 { 156 return svn_error_create 157 (SVN_ERR_CLIENT_BAD_REVISION, NULL, 158 _("-c and -r are mutually exclusive")); 159 } 160 for (i = 0; i < opt_state->revision_ranges->nelts; i++) 161 { 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++; 167 else 168 range->end.value.number++; 169 } 170 } 171 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, 175 target, pool)); 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; 181 182 if (svn_path_is_url(target)) 183 { 184 for (i = 1; i < targets->nelts; i++) 185 { 186 target = APR_ARRAY_IDX(targets, i, const char *); 187 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"), 193 target); 194 } 195 } 196 197 lb.ctx = ctx; 198 lb.quiet = opt_state->quiet; 199 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, 208 opt_state->limit, 209 opt_state->verbose, 210 opt_state->stop_on_copy, 211 opt_state->use_merge_history, 212 revprops, 213 log_entry_receiver, 214 &lb, 215 ctx, 216 pool)); 217 218 if (!opt_state->quiet) 219 { 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))); 232 else 233 SVN_ERR(svn_cmdline_printf(pool, 234 _("%15s revisions\n" 235 "%15s msg lines\n" 236 "%15s changes\n"), 237 svn__ui64toa_sep(lb.revisions, ',', pool), 238 svn__ui64toa_sep(lb.message_lines, ',', pool), 239 svn__ui64toa_sep(lb.changes, ',', pool))); 240 } 241 242 return SVN_NO_ERROR; 243} 244