Author: rhuijben Date: Wed Apr 15 14:37:04 2015 New Revision: 1673785 URL: http://svn.apache.org/r1673785 Log: Introduce a 'null-blame' command in svnbench.
Suggested by: jcorvel * subversion/include/private/svn_client_private.h (svn_client__get_revision_number): Declare function here. * subversion/libsvn_client/client.h (svn_client__get_revision_number): ... instead of here. * subversion/svnbench/cl.h (svn_cl__null_blame): New function. * subversion/svnbench/null-blame-cmd.c New file. * subversion/svnbench/svnbench.c (svn_cl__cmd_table): Declare null-bench. Added: subversion/trunk/subversion/svnbench/null-blame-cmd.c (with props) Modified: subversion/trunk/subversion/include/private/svn_client_private.h subversion/trunk/subversion/libsvn_client/client.h subversion/trunk/subversion/svnbench/cl.h subversion/trunk/subversion/svnbench/svnbench.c Modified: subversion/trunk/subversion/include/private/svn_client_private.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1673785&r1=1673784&r2=1673785&view=diff ============================================================================== --- subversion/trunk/subversion/include/private/svn_client_private.h (original) +++ subversion/trunk/subversion/include/private/svn_client_private.h Wed Apr 15 14:37:04 2015 @@ -40,6 +40,51 @@ extern "C" { #endif /* __cplusplus */ +/* Set *REVNUM to the revision number identified by REVISION. + + If REVISION->kind is svn_opt_revision_number, just use + REVISION->value.number, ignoring LOCAL_ABSPATH and RA_SESSION. + + Else if REVISION->kind is svn_opt_revision_committed, + svn_opt_revision_previous, or svn_opt_revision_base, or + svn_opt_revision_working, then the revision can be identified + purely based on the working copy's administrative information for + LOCAL_ABSPATH, so RA_SESSION is ignored. If LOCAL_ABSPATH is not + under revision control, return SVN_ERR_UNVERSIONED_RESOURCE, or if + LOCAL_ABSPATH is null, return SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED. + + Else if REVISION->kind is svn_opt_revision_date or + svn_opt_revision_head, then RA_SESSION is used to retrieve the + revision from the repository (using REVISION->value.date in the + former case), and LOCAL_ABSPATH is ignored. If RA_SESSION is null, + return SVN_ERR_CLIENT_RA_ACCESS_REQUIRED. + + Else if REVISION->kind is svn_opt_revision_unspecified, set + *REVNUM to SVN_INVALID_REVNUM. + + If YOUNGEST_REV is non-NULL, it is an in/out parameter. If + *YOUNGEST_REV is valid, use it as the youngest revision in the + repository (regardless of reality) -- don't bother to lookup the + true value for HEAD, and don't return any value in *REVNUM greater + than *YOUNGEST_REV. If *YOUNGEST_REV is not valid, and a HEAD + lookup is required to populate *REVNUM, then also populate + *YOUNGEST_REV with the result. This is useful for making multiple + serialized calls to this function with a basically static view of + the repository, avoiding race conditions which could occur between + multiple invocations with HEAD lookup requests. + + Else return SVN_ERR_CLIENT_BAD_REVISION. + + Use SCRATCH_POOL for any temporary allocation. */ +svn_error_t * +svn_client__get_revision_number(svn_revnum_t *revnum, + svn_revnum_t *youngest_rev, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_ra_session_t *ra_session, + const svn_opt_revision_t *revision, + apr_pool_t *scratch_pool); + /* Return true if KIND is a revision kind that is dependent on the working * copy. Otherwise, return false. */ #define SVN_CLIENT__REVKIND_NEEDS_WC(kind) \ Modified: subversion/trunk/subversion/libsvn_client/client.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1673785&r1=1673784&r2=1673785&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/client.h (original) +++ subversion/trunk/subversion/libsvn_client/client.h Wed Apr 15 14:37:04 2015 @@ -73,52 +73,6 @@ typedef struct svn_client__private_ctx_t svn_client__private_ctx_t * svn_client__get_private_ctx(svn_client_ctx_t *ctx); - -/* Set *REVNUM to the revision number identified by REVISION. - - If REVISION->kind is svn_opt_revision_number, just use - REVISION->value.number, ignoring LOCAL_ABSPATH and RA_SESSION. - - Else if REVISION->kind is svn_opt_revision_committed, - svn_opt_revision_previous, or svn_opt_revision_base, or - svn_opt_revision_working, then the revision can be identified - purely based on the working copy's administrative information for - LOCAL_ABSPATH, so RA_SESSION is ignored. If LOCAL_ABSPATH is not - under revision control, return SVN_ERR_UNVERSIONED_RESOURCE, or if - LOCAL_ABSPATH is null, return SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED. - - Else if REVISION->kind is svn_opt_revision_date or - svn_opt_revision_head, then RA_SESSION is used to retrieve the - revision from the repository (using REVISION->value.date in the - former case), and LOCAL_ABSPATH is ignored. If RA_SESSION is null, - return SVN_ERR_CLIENT_RA_ACCESS_REQUIRED. - - Else if REVISION->kind is svn_opt_revision_unspecified, set - *REVNUM to SVN_INVALID_REVNUM. - - If YOUNGEST_REV is non-NULL, it is an in/out parameter. If - *YOUNGEST_REV is valid, use it as the youngest revision in the - repository (regardless of reality) -- don't bother to lookup the - true value for HEAD, and don't return any value in *REVNUM greater - than *YOUNGEST_REV. If *YOUNGEST_REV is not valid, and a HEAD - lookup is required to populate *REVNUM, then also populate - *YOUNGEST_REV with the result. This is useful for making multiple - serialized calls to this function with a basically static view of - the repository, avoiding race conditions which could occur between - multiple invocations with HEAD lookup requests. - - Else return SVN_ERR_CLIENT_BAD_REVISION. - - Use SCRATCH_POOL for any temporary allocation. */ -svn_error_t * -svn_client__get_revision_number(svn_revnum_t *revnum, - svn_revnum_t *youngest_rev, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - svn_ra_session_t *ra_session, - const svn_opt_revision_t *revision, - apr_pool_t *scratch_pool); - /* Set *ORIGINAL_REPOS_RELPATH and *ORIGINAL_REVISION to the original location that served as the source of the copy from which PATH_OR_URL at REVISION was created, or NULL and SVN_INVALID_REVNUM (respectively) if PATH_OR_URL at Modified: subversion/trunk/subversion/svnbench/cl.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnbench/cl.h?rev=1673785&r1=1673784&r2=1673785&view=diff ============================================================================== --- subversion/trunk/subversion/svnbench/cl.h (original) +++ subversion/trunk/subversion/svnbench/cl.h Wed Apr 15 14:37:04 2015 @@ -109,6 +109,7 @@ typedef struct svn_cl__cmd_baton_t /* Declare all the command procedures */ svn_opt_subcommand_t svn_cl__help, + svn_cl__null_blame, svn_cl__null_export, svn_cl__null_list, svn_cl__null_log, Added: subversion/trunk/subversion/svnbench/null-blame-cmd.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnbench/null-blame-cmd.c?rev=1673785&view=auto ============================================================================== --- subversion/trunk/subversion/svnbench/null-blame-cmd.c (added) +++ subversion/trunk/subversion/svnbench/null-blame-cmd.c Wed Apr 15 14:37:04 2015 @@ -0,0 +1,284 @@ +/* + * null-blame-cmd.c -- Subversion export command + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include "svn_client.h" +#include "svn_cmdline.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_pools.h" +#include "svn_sorts.h" +#include "cl.h" + +#include "svn_private_config.h" +#include "private/svn_string_private.h" +#include "private/svn_client_private.h" + +struct file_rev_baton { + apr_int64_t byte_count; + apr_int64_t delta_count; + apr_int64_t rev_count; +}; + +/* Implements svn_txdelta_window_handler_t */ +static svn_error_t * +delta_handler(svn_txdelta_window_t *window, void *baton) +{ + struct file_rev_baton *frb = baton; + + if (window != NULL) + frb->byte_count += window->tview_len; + + return SVN_NO_ERROR; +} + +/* Implementes svn_file_rev_handler_t */ +static svn_error_t * +file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, + apr_hash_t *rev_props, + svn_boolean_t merged_revision, + svn_txdelta_window_handler_t *content_delta_handler, + void **content_delta_baton, + apr_array_header_t *prop_diffs, + apr_pool_t *pool) +{ + struct file_rev_baton *frb = baton; + + frb->rev_count++; + + if (content_delta_handler) + { + *content_delta_handler = delta_handler; + *content_delta_baton = baton; + frb->delta_count++; + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +bench_null_blame(const char *target, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *start, + const svn_opt_revision_t *end, + svn_boolean_t include_merged_revisions, + svn_boolean_t quiet, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + struct file_rev_baton frb = { 0, 0, 0}; + svn_ra_session_t *ra_session; + svn_revnum_t start_revnum, end_revnum; + svn_boolean_t backwards; + const char *target_abspath_or_url; + + if (start->kind == svn_opt_revision_unspecified + || end->kind == svn_opt_revision_unspecified) + return svn_error_create + (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); + + if (svn_path_is_url(target)) + target_abspath_or_url = target; + else + SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool)); + + + /* Get an RA plugin for this filesystem object. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, + target, NULL, peg_revision, + peg_revision, + ctx, pool)); + + SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx, + target_abspath_or_url, ra_session, + start, pool)); + + SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx, + target_abspath_or_url, ra_session, + end, pool)); + + { + svn_client__pathrev_t *loc; + svn_opt_revision_t younger_end; + younger_end.kind = svn_opt_revision_number; + younger_end.value.number = MAX(start_revnum, end_revnum); + + SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session, + target, peg_revision, + &younger_end, + ctx, pool)); + + /* Make the session point to the real URL. */ + SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool)); + } + + backwards = (start_revnum > end_revnum); + + /* Collect all blame information. + We need to ensure that we get one revision before the start_rev, + if available so that we can know what was actually changed in the start + revision. */ + SVN_ERR(svn_ra_get_file_revs2(ra_session, "", + backwards ? start_revnum + : MAX(0, start_revnum-1), + end_revnum, + include_merged_revisions, + file_rev_handler, &frb, pool)); + + if (!quiet) + SVN_ERR(svn_cmdline_printf(pool, + _("%15s revisions\n" + "%15s deltas\n" + "%15s bytes in deltas\n"), + svn__ui64toa_sep(frb.rev_count, ',', pool), + svn__ui64toa_sep(frb.delta_count, ',', pool), + svn__ui64toa_sep(frb.byte_count, ',', pool))); + + return SVN_NO_ERROR; +} + + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__null_blame(apr_getopt_t *os, + void *baton, + apr_pool_t *pool) +{ + svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; + svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; + apr_pool_t *iterpool; + apr_array_header_t *targets; + int i; + svn_boolean_t end_revision_unspecified = FALSE; + svn_diff_file_options_t *diff_options = svn_diff_file_options_create(pool); + svn_boolean_t seen_nonexistent_target = FALSE; + + SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, + opt_state->targets, + ctx, FALSE, pool)); + + /* Blame needs a file on which to operate. */ + if (! targets->nelts) + return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); + + if (opt_state->end_revision.kind == svn_opt_revision_unspecified) + { + if (opt_state->start_revision.kind != svn_opt_revision_unspecified) + { + /* In the case that -rX was specified, we actually want to set the + range to be -r1:X. */ + + opt_state->end_revision = opt_state->start_revision; + opt_state->start_revision.kind = svn_opt_revision_number; + opt_state->start_revision.value.number = 1; + } + else + end_revision_unspecified = TRUE; + } + + if (opt_state->start_revision.kind == svn_opt_revision_unspecified) + { + opt_state->start_revision.kind = svn_opt_revision_number; + opt_state->start_revision.value.number = 1; + } + + /* The final conclusion from issue #2431 is that blame info + is client output (unlike 'svn cat' which plainly cats the file), + so the EOL style should be the platform local one. + */ + iterpool = svn_pool_create(pool); + + if (opt_state->extensions) + { + apr_array_header_t *opts; + opts = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool); + SVN_ERR(svn_diff_file_options_parse(diff_options, opts, pool)); + } + + for (i = 0; i < targets->nelts; i++) + { + svn_error_t *err; + const char *target = APR_ARRAY_IDX(targets, i, const char *); + const char *parsed_path; + svn_opt_revision_t peg_revision; + + svn_pool_clear(iterpool); + SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); + + /* Check for a peg revision. */ + SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target, + iterpool)); + + if (end_revision_unspecified) + { + if (peg_revision.kind != svn_opt_revision_unspecified) + opt_state->end_revision = peg_revision; + else if (svn_path_is_url(target)) + opt_state->end_revision.kind = svn_opt_revision_head; + else + opt_state->end_revision.kind = svn_opt_revision_working; + } + + err = bench_null_blame(parsed_path, + &peg_revision, + &opt_state->start_revision, + &opt_state->end_revision, + opt_state->use_merge_history, + opt_state->quiet, + ctx, + iterpool); + + if (err) + { + if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND || + err->apr_err == SVN_ERR_ENTRY_NOT_FOUND || + err->apr_err == SVN_ERR_FS_NOT_FILE || + err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_handle_warning2(stderr, err, "svn: "); + svn_error_clear(err); + err = NULL; + seen_nonexistent_target = TRUE; + } + else + { + return svn_error_trace(err); + } + } + } + svn_pool_destroy(iterpool); + + if (seen_nonexistent_target) + return svn_error_create( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("Could not perform blame on all targets because some " + "targets don't exist")); + else + return SVN_NO_ERROR; +} Propchange: subversion/trunk/subversion/svnbench/null-blame-cmd.c ------------------------------------------------------------------------------ svn:eol-style = native Modified: subversion/trunk/subversion/svnbench/svnbench.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnbench/svnbench.c?rev=1673785&r1=1673784&r2=1673785&view=diff ============================================================================== --- subversion/trunk/subversion/svnbench/svnbench.c (original) +++ subversion/trunk/subversion/svnbench/svnbench.c Wed Apr 15 14:37:04 2015 @@ -219,6 +219,26 @@ const svn_opt_subcommand_desc2_t svn_cl_ {0} }, /* This command is also invoked if we see option "--help", "-h" or "-?". */ + { "null-blame", svn_cl__null_blame, {0}, N_ + ("Fetch all versions of a file in a batch.\n" + "usage: null-blame [-rM:N] TARGET[@REV]...\n" + "\n" + " With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the last revision at or before rN that changed or added the line,\n" + " looking back no further than rM.\n" + "\n" + " With a reverse revision range '-r M:N' where M > N,\n" + " annotate each line that is present in revision N of the file, with\n" + " the next revision after rN that changed or deleted the line,\n" + " looking forward no further than rM.\n" + "\n" + " If specified, REV determines in which revision the target is first\n" + " looked up.\n" + "\n" + " Write the annotated result to standard output.\n"), + {'r', 'g'} }, + { "null-export", svn_cl__null_export, {0}, N_ ("Create an unversioned copy of a tree.\n" "usage: null-export [-r REV] URL[@PEGREV]\n"