1/* 2 * null-blame-cmd.c -- Subversion export command 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/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include "svn_client.h" 31#include "svn_cmdline.h" 32#include "svn_error.h" 33#include "svn_dirent_uri.h" 34#include "svn_path.h" 35#include "svn_pools.h" 36#include "svn_sorts.h" 37#include "cl.h" 38 39#include "svn_private_config.h" 40#include "private/svn_string_private.h" 41#include "private/svn_client_private.h" 42 43struct file_rev_baton { 44 apr_int64_t byte_count; 45 apr_int64_t delta_count; 46 apr_int64_t rev_count; 47}; 48 49/* Implements svn_txdelta_window_handler_t */ 50static svn_error_t * 51delta_handler(svn_txdelta_window_t *window, void *baton) 52{ 53 struct file_rev_baton *frb = baton; 54 55 if (window != NULL) 56 frb->byte_count += window->tview_len; 57 58 return SVN_NO_ERROR; 59} 60 61/* Implementes svn_file_rev_handler_t */ 62static svn_error_t * 63file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, 64 apr_hash_t *rev_props, 65 svn_boolean_t merged_revision, 66 svn_txdelta_window_handler_t *content_delta_handler, 67 void **content_delta_baton, 68 apr_array_header_t *prop_diffs, 69 apr_pool_t *pool) 70{ 71 struct file_rev_baton *frb = baton; 72 73 frb->rev_count++; 74 75 if (content_delta_handler) 76 { 77 *content_delta_handler = delta_handler; 78 *content_delta_baton = baton; 79 frb->delta_count++; 80 } 81 82 return SVN_NO_ERROR; 83} 84 85static svn_error_t * 86bench_null_blame(const char *target, 87 const svn_opt_revision_t *peg_revision, 88 const svn_opt_revision_t *start, 89 const svn_opt_revision_t *end, 90 svn_boolean_t include_merged_revisions, 91 svn_boolean_t quiet, 92 svn_client_ctx_t *ctx, 93 apr_pool_t *pool) 94{ 95 struct file_rev_baton frb = { 0, 0, 0}; 96 svn_ra_session_t *ra_session; 97 svn_revnum_t start_revnum, end_revnum; 98 svn_boolean_t backwards; 99 const char *target_abspath_or_url; 100 101 if (start->kind == svn_opt_revision_unspecified 102 || end->kind == svn_opt_revision_unspecified) 103 return svn_error_create 104 (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); 105 106 if (svn_path_is_url(target)) 107 target_abspath_or_url = target; 108 else 109 SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool)); 110 111 112 /* Get an RA plugin for this filesystem object. */ 113 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, 114 target, NULL, peg_revision, 115 peg_revision, 116 ctx, pool)); 117 118 SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx, 119 target_abspath_or_url, ra_session, 120 start, pool)); 121 122 SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx, 123 target_abspath_or_url, ra_session, 124 end, pool)); 125 126 { 127 svn_client__pathrev_t *loc; 128 svn_opt_revision_t younger_end; 129 younger_end.kind = svn_opt_revision_number; 130 younger_end.value.number = MAX(start_revnum, end_revnum); 131 132 SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session, 133 target, peg_revision, 134 &younger_end, 135 ctx, pool)); 136 137 /* Make the session point to the real URL. */ 138 SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool)); 139 } 140 141 backwards = (start_revnum > end_revnum); 142 143 /* Collect all blame information. 144 We need to ensure that we get one revision before the start_rev, 145 if available so that we can know what was actually changed in the start 146 revision. */ 147 SVN_ERR(svn_ra_get_file_revs2(ra_session, "", 148 backwards ? start_revnum 149 : MAX(0, start_revnum-1), 150 end_revnum, 151 include_merged_revisions, 152 file_rev_handler, &frb, pool)); 153 154 if (!quiet) 155 SVN_ERR(svn_cmdline_printf(pool, 156 _("%15s revisions\n" 157 "%15s deltas\n" 158 "%15s bytes in deltas\n"), 159 svn__ui64toa_sep(frb.rev_count, ',', pool), 160 svn__ui64toa_sep(frb.delta_count, ',', pool), 161 svn__ui64toa_sep(frb.byte_count, ',', pool))); 162 163 return SVN_NO_ERROR; 164} 165 166 167/* This implements the `svn_opt_subcommand_t' interface. */ 168svn_error_t * 169svn_cl__null_blame(apr_getopt_t *os, 170 void *baton, 171 apr_pool_t *pool) 172{ 173 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 174 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 175 apr_pool_t *iterpool; 176 apr_array_header_t *targets; 177 int i; 178 svn_boolean_t end_revision_unspecified = FALSE; 179 svn_boolean_t seen_nonexistent_target = FALSE; 180 181 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 182 opt_state->targets, 183 ctx, FALSE, pool)); 184 185 /* Blame needs a file on which to operate. */ 186 if (! targets->nelts) 187 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); 188 189 if (opt_state->end_revision.kind == svn_opt_revision_unspecified) 190 { 191 if (opt_state->start_revision.kind != svn_opt_revision_unspecified) 192 { 193 /* In the case that -rX was specified, we actually want to set the 194 range to be -r1:X. */ 195 196 opt_state->end_revision = opt_state->start_revision; 197 opt_state->start_revision.kind = svn_opt_revision_number; 198 opt_state->start_revision.value.number = 1; 199 } 200 else 201 end_revision_unspecified = TRUE; 202 } 203 204 if (opt_state->start_revision.kind == svn_opt_revision_unspecified) 205 { 206 opt_state->start_revision.kind = svn_opt_revision_number; 207 opt_state->start_revision.value.number = 1; 208 } 209 210 /* The final conclusion from issue #2431 is that blame info 211 is client output (unlike 'svn cat' which plainly cats the file), 212 so the EOL style should be the platform local one. 213 */ 214 iterpool = svn_pool_create(pool); 215 216 for (i = 0; i < targets->nelts; i++) 217 { 218 svn_error_t *err; 219 const char *target = APR_ARRAY_IDX(targets, i, const char *); 220 const char *parsed_path; 221 svn_opt_revision_t peg_revision; 222 223 svn_pool_clear(iterpool); 224 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); 225 226 /* Check for a peg revision. */ 227 SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target, 228 iterpool)); 229 230 if (end_revision_unspecified) 231 { 232 if (peg_revision.kind != svn_opt_revision_unspecified) 233 opt_state->end_revision = peg_revision; 234 else if (svn_path_is_url(target)) 235 opt_state->end_revision.kind = svn_opt_revision_head; 236 else 237 opt_state->end_revision.kind = svn_opt_revision_working; 238 } 239 240 err = bench_null_blame(parsed_path, 241 &peg_revision, 242 &opt_state->start_revision, 243 &opt_state->end_revision, 244 opt_state->use_merge_history, 245 opt_state->quiet, 246 ctx, 247 iterpool); 248 249 if (err) 250 { 251 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND || 252 err->apr_err == SVN_ERR_ENTRY_NOT_FOUND || 253 err->apr_err == SVN_ERR_FS_NOT_FILE || 254 err->apr_err == SVN_ERR_FS_NOT_FOUND) 255 { 256 svn_handle_warning2(stderr, err, "svn: "); 257 svn_error_clear(err); 258 err = NULL; 259 seen_nonexistent_target = TRUE; 260 } 261 else 262 { 263 return svn_error_trace(err); 264 } 265 } 266 } 267 svn_pool_destroy(iterpool); 268 269 if (seen_nonexistent_target) 270 return svn_error_create( 271 SVN_ERR_ILLEGAL_TARGET, NULL, 272 _("Could not perform blame on all targets because some " 273 "targets don't exist")); 274 else 275 return SVN_NO_ERROR; 276} 277