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"


Reply via email to