Author: julianfoad
Date: Thu Mar 15 20:43:30 2018
New Revision: 1826864

URL: http://svn.apache.org/viewvc?rev=1826864&view=rev
Log:
Viewspec: Add an experimental viewspec output command.

This reports the current layout of the working copy, in terms of switched
URLs, mixed revisions, and mixed depths.

The current CLI command is:

  svn info --viewspec [PATH]

The current output is in the form of a series of 'svn' commands which can
be used as command-line input to recreate the layout, although the behaviour
when doing so is not optimal.

Patch by: rhuijben
(tweaked by me)

* subversion/include/svn_client.h
  (svn_client_layout_func_t,
   svn_client_layout_list): New.

* subversion/libsvn_client/layout.c
  New file.

* subversion/svn/cl.h
  (svn_cl__opt_state_t): Add a 'viewspec' option.

* subversion/svn/info-cmd.c
  (layout_list_baton_t,
   layout_func,
   cl_layout_list): New.
  (svn_cl__info): Call it.

* subversion/svn/svn.c
  (svn_cl__longopt_t,
   svn_cl__options): Add a 'viewspec' option.
  (svn_cl__cmd_table): Let the 'info' command take the option.
  (sub_main): Parse the option.

Added:
    subversion/trunk/subversion/libsvn_client/layout.c   (with props)
Modified:
    subversion/trunk/subversion/include/svn_client.h
    subversion/trunk/subversion/svn/cl.h
    subversion/trunk/subversion/svn/info-cmd.c
    subversion/trunk/subversion/svn/svn.c

Modified: subversion/trunk/subversion/include/svn_client.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1826864&r1=1826863&r2=1826864&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Thu Mar 15 20:43:30 2018
@@ -1547,6 +1547,34 @@ svn_client_switch(svn_revnum_t *result_r
 
 /** @} */
 
+/** Callback for svn_client_layout_list()
+ */
+typedef svn_error_t * (*svn_client_layout_func_t)(
+                            void *layout_baton,
+                            const char *local_abspath,
+                            const char *repos_root_url,
+                            svn_boolean_t not_present,
+                            svn_boolean_t url_changed,
+                            const char *url,
+                            svn_boolean_t revision_changed,
+                            svn_revnum_t revision,
+                            svn_boolean_t depth_changed,
+                            svn_depth_t depth,
+                            apr_pool_t *scratch_pool);
+
+/**
+ * Describe the layout of the working copy below @a local_abspath to
+ * the callback @a layout.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_layout_list(const char *local_abspath,
+                       svn_client_layout_func_t layout,
+                       void *layout_baton,
+                       svn_client_ctx_t *ctx,
+                       apr_pool_t *scratch_pool);
+
+
 /**
  * @defgroup Add Begin versioning files/directories in a working copy.
  *

Added: subversion/trunk/subversion/libsvn_client/layout.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/layout.c?rev=1826864&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_client/layout.c (added)
+++ subversion/trunk/subversion/libsvn_client/layout.c Thu Mar 15 20:43:30 2018
@@ -0,0 +1,290 @@
+/*
+* layout.c:  code to list and update the working copy layout
+*
+* ====================================================================
+*    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_hash.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "client.h"
+
+#include "svn_private_config.h"
+#include "private/svn_wc_private.h"
+
+struct layout_item_t
+{
+  const char *local_abspath;
+  const char *url;
+  svn_revnum_t revision;
+  svn_depth_t depth;
+  struct layout_item_t *ancestor;
+  apr_pool_t *pool;
+};
+
+struct client_layout_baton_t
+{
+  const char *root_abspath;
+  svn_wc_context_t *wc_ctx;
+  const char *repos_root_url;
+
+  struct layout_item_t *stack;
+  apr_pool_t *root_pool;
+
+  svn_client_layout_func_t layout;
+  void *layout_baton;
+};
+
+
+static svn_error_t *
+layout_set_path(void *report_baton,
+                const char *path,
+                svn_revnum_t revision,
+                svn_depth_t depth,
+                svn_boolean_t start_empty,
+                const char *lock_token,
+                apr_pool_t *pool)
+{
+  struct client_layout_baton_t *lb = report_baton;
+  const char *local_abspath = svn_dirent_join(lb->root_abspath, path, pool);
+  struct layout_item_t *it;
+  apr_pool_t *item_pool;
+  svn_depth_t expected_depth;
+
+  while (lb->stack
+          && !svn_dirent_is_ancestor(lb->stack->local_abspath, local_abspath))
+    {
+      it = lb->stack;
+      lb->stack = it->ancestor;
+      svn_pool_destroy(it->pool);
+    }
+
+  item_pool = svn_pool_create(lb->stack ? lb->stack->pool
+                                        : lb->root_pool);
+
+  it = apr_pcalloc(item_pool, sizeof(*it));
+  it->pool = item_pool;
+  it->local_abspath = apr_pstrdup(item_pool, local_abspath);
+  it->depth = depth;
+  it->revision = revision;
+  if (lb->stack)
+    {
+      it->url = svn_path_url_add_component2(
+                     lb->stack->url,
+                     svn_dirent_skip_ancestor(lb->stack->local_abspath,
+                                              local_abspath),
+                     item_pool);
+    }
+  else
+    {
+      const char *repos_relpath, *repos_root_url;
+
+      SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath,
+                                    &repos_root_url, NULL, NULL,
+                                    lb->wc_ctx, local_abspath,
+                                    FALSE /* ignore_enoent */,
+                                    pool, pool));
+
+      lb->repos_root_url = apr_pstrdup(lb->root_pool, repos_root_url);
+      it->url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+                                            item_pool);
+    }
+  it->ancestor = lb->stack;
+  lb->stack = it;
+
+  if (!it->ancestor)
+    expected_depth = depth;
+  else if (it->ancestor->depth == svn_depth_infinity)
+    expected_depth = svn_depth_infinity;
+  else
+    expected_depth = svn_depth_empty;
+
+  return svn_error_trace(lb->layout(lb->layout_baton,
+                                    it->local_abspath,
+                                    lb->repos_root_url,
+                                    FALSE /* not-present */,
+                                    FALSE /* url changed */,
+                                    it->url,
+                                    it->ancestor
+                                      ? it->ancestor->revision != it->revision
+                                      : FALSE,
+                                    it->revision,
+                                    (depth != expected_depth),
+                                    it->depth,
+                                    pool));
+}
+
+static svn_error_t *
+layout_link_path(void *report_baton,
+                 const char *path,
+                 const char *url,
+                 svn_revnum_t revision,
+                 svn_depth_t depth,
+                 svn_boolean_t start_empty,
+                 const char *lock_token,
+                 apr_pool_t *pool)
+{
+  struct client_layout_baton_t *lb = report_baton;
+  const char *local_abspath = svn_dirent_join(lb->root_abspath, path, pool);
+  struct layout_item_t *it;
+  apr_pool_t *item_pool;
+  svn_depth_t expected_depth;
+
+  SVN_ERR_ASSERT(lb->stack); /* Always below root entry */
+
+  while (!svn_dirent_is_ancestor(lb->stack->local_abspath, local_abspath))
+    {
+      it = lb->stack;
+      lb->stack = it->ancestor;
+      svn_pool_destroy(it->pool);
+    }
+
+  item_pool = svn_pool_create(lb->stack ? lb->stack->pool
+                                        : lb->root_pool);
+
+  it = apr_pcalloc(item_pool, sizeof(*it));
+  it->pool = item_pool;
+  it->local_abspath = apr_pstrdup(item_pool, local_abspath);
+  it->depth = depth;
+  it->revision = revision;
+  it->url = apr_pstrdup(item_pool, url);
+
+  it->ancestor = lb->stack;
+  lb->stack = it;
+
+  if (it->ancestor->depth == svn_depth_infinity)
+    expected_depth = svn_depth_infinity;
+  else
+    expected_depth = svn_depth_empty;
+
+  return svn_error_trace(lb->layout(lb->layout_baton,
+                                    it->local_abspath,
+                                    lb->repos_root_url,
+                                    FALSE /* not-present */,
+                                    TRUE /* url changed */,
+                                    it->url,
+                                    it->ancestor
+                                      ? it->ancestor->revision != it->revision
+                                      : FALSE,
+                                    it->revision,
+                                    (depth != expected_depth),
+                                    it->depth,
+                                    pool));
+}
+
+static svn_error_t *
+layout_delete_path(void *report_baton,
+                   const char *path,
+                   apr_pool_t *pool)
+{
+  struct client_layout_baton_t *lb = report_baton;
+  const char *local_abspath = svn_dirent_join(lb->root_abspath, path, pool);
+  struct layout_item_t *it;
+
+  SVN_ERR_ASSERT(lb->stack); /* Always below root entry */
+
+  while (!svn_dirent_is_ancestor(lb->stack->local_abspath, local_abspath))
+    {
+      it = lb->stack;
+      lb->stack = it->ancestor;
+      svn_pool_destroy(it->pool);
+    }
+
+  return svn_error_trace(lb->layout(lb->layout_baton,
+                                    local_abspath,
+                                    lb->repos_root_url,
+                                    TRUE /* not-present */,
+                                    FALSE /* url changed */,
+                                    NULL /* no-url */,
+                                    FALSE /* revision changed */,
+                                    SVN_INVALID_REVNUM,
+                                    FALSE /* depth changed */,
+                                    svn_depth_unknown,
+                                    pool));
+}
+
+static svn_error_t *
+layout_finish_report(void *report_baton,
+                     apr_pool_t *pool)
+{
+  /*struct client_layout_baton_t *lb = report_baton;*/
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+layout_abort_report(void *report_baton,
+                     apr_pool_t *pool)
+{
+  /*struct client_layout_baton_t *lb = report_baton;*/
+  return SVN_NO_ERROR;
+}
+
+static const svn_ra_reporter3_t layout_reporter =
+{
+  layout_set_path,
+  layout_delete_path,
+  layout_link_path,
+  layout_finish_report,
+  layout_abort_report
+};
+
+svn_error_t *
+svn_client_layout_list(const char *local_abspath,
+                       svn_client_layout_func_t layout,
+                       void *layout_baton,
+                       svn_client_ctx_t *ctx,
+                       apr_pool_t *scratch_pool)
+{
+  struct client_layout_baton_t lb;
+
+  lb.root_abspath = local_abspath;
+  lb.root_pool = scratch_pool;
+  lb.wc_ctx = ctx->wc_ctx;
+  lb.repos_root_url = NULL; /* Filled in later */
+  lb.stack = NULL;
+
+  lb.layout = layout;
+  lb.layout_baton = layout_baton;
+
+  /* Drive the reporter structure, describing the revisions within
+     LOCAL_ABSPATH.  When this calls reporter->finish_report, the
+     reporter will drive the update_editor. */
+  SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath,
+                                  &layout_reporter, &lb,
+                                  FALSE /* restore_files */,
+                                  svn_depth_infinity,
+                                  TRUE /* honor_depth_exclude */,
+                                  FALSE /* depth_compatibility_trick */,
+                                  FALSE /* use_commit_times */,
+                                  ctx->cancel_func, ctx->cancel_baton,
+                                  ctx->notify_func2, ctx->notify_baton2,
+                                  scratch_pool));
+  return SVN_NO_ERROR;
+}

Propchange: subversion/trunk/subversion/libsvn_client/layout.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/svn/cl.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1826864&r1=1826863&r2=1826864&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Thu Mar 15 20:43:30 2018
@@ -257,6 +257,7 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t adds_as_modification; /* update 'add vs add' no tree conflict 
*/
   svn_boolean_t vacuum_pristines; /* remove unreferenced pristines */
   svn_boolean_t drop;             /* drop shelf after successful unshelve */
+  svn_boolean_t viewspec;
 } svn_cl__opt_state_t;
 
 /* Conflict stats for operations such as update and merge. */

Modified: subversion/trunk/subversion/svn/info-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/info-cmd.c?rev=1826864&r1=1826863&r2=1826864&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/info-cmd.c (original)
+++ subversion/trunk/subversion/svn/info-cmd.c Thu Mar 15 20:43:30 2018
@@ -45,6 +45,131 @@
 
 /*** Code. ***/
 
+struct layout_list_baton_t
+{
+  svn_boolean_t checkout;
+  const char *target;
+  const char *target_abspath;
+};
+
+/* Implements svn_client_layout_func_t */
+static svn_error_t *
+layout_func(void *layout_baton,
+            const char *local_abspath,
+            const char *repos_root_url,
+            svn_boolean_t not_present,
+            svn_boolean_t url_changed,
+            const char *url,
+            svn_boolean_t revision_changed,
+            svn_revnum_t revision,
+            svn_boolean_t depth_changed,
+            svn_depth_t depth,
+            apr_pool_t *scratch_pool)
+{
+  struct layout_list_baton_t *llb = layout_baton;
+  const char *relpath = svn_dirent_skip_ancestor(llb->target_abspath, 
local_abspath);
+
+  if (llb->checkout)
+    {
+      SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                 "svn checkout %s@%lu %s",
+                                 url, revision, llb->target));
+      if (depth != svn_depth_infinity)
+        SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                   " --depth %s", svn_depth_to_word(depth)));
+      llb->checkout = FALSE;
+    }
+  else if (depth == svn_depth_exclude)
+    {
+      SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                 "svn update --set-depth exclude %s",
+                                 svn_dirent_join(llb->target, relpath,
+                                                 scratch_pool)));
+    }
+  else if (not_present)
+    {
+      /* Easiest way to create a not present node: update to r0 */
+      SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                 "svn update -r 0 %s",
+                                 svn_dirent_join(llb->target, relpath,
+                                                 scratch_pool)));
+    }
+  else if (!url_changed && revision_changed)
+    {
+      /* Easiest way to create a not present node: update to r0 */
+      SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                 "svn update -r %lu %s",
+                                 revision,
+                                 svn_dirent_join(llb->target, relpath,
+                                                 scratch_pool)));
+
+      if (depth_changed)
+        SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                   " --set-depth %s",
+                                   svn_depth_to_word(depth)));
+    }
+  else if (url_changed)
+    {
+      /* Easiest way to create a not present node: update to r0 */
+      SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                 "svn switch ^/%s@%lu %s",
+                                 svn_uri_skip_ancestor(repos_root_url,
+                                                       url, scratch_pool),
+                                 revision,
+                                 svn_dirent_join(llb->target, relpath,
+                                                 scratch_pool)));
+      if (depth_changed)
+        SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                   " --set-depth %s",
+                                   svn_depth_to_word(depth)));
+    }
+  else if (depth_changed)
+    {
+        SVN_ERR(svn_cmdline_printf(scratch_pool,
+                                   "svn update --set-depth %s %s",
+                                   svn_depth_to_word(depth),
+                                   svn_dirent_join(llb->target, relpath,
+                                                   scratch_pool)));
+    }
+  else
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+cl_layout_list(apr_array_header_t *targets,
+               void *baton,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *scratch_pool)
+{
+  const char *list_path, *list_abspath;
+  struct layout_list_baton_t llb;
+
+  /* Add "." if user passed 0 arguments */
+  svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+  if (targets->nelts > 1)
+    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+  list_path = APR_ARRAY_IDX(targets, 0, const char *);
+
+  SVN_ERR(svn_cl__check_target_is_local_path(list_path));
+
+  SVN_ERR(svn_dirent_get_absolute(&list_abspath, list_path,
+                                  scratch_pool));
+
+  llb.checkout = TRUE;
+  llb.target = list_path;
+  llb.target_abspath = list_abspath;
+
+  return svn_error_trace(svn_client_layout_list(list_abspath,
+                                                layout_func, &llb,
+                                                ctx, scratch_pool));
+}
+
 static svn_error_t *
 svn_cl__info_print_time(apr_time_t atime,
                         const char *desc,
@@ -918,6 +1043,9 @@ svn_cl__info(apr_getopt_t *os,
                                                       opt_state->targets,
                                                       ctx, FALSE, pool));
 
+  if (opt_state->viewspec)
+    return svn_error_trace(cl_layout_list(targets, baton, ctx, pool));
+
   /* Add "." if user passed 0 arguments. */
   svn_opt_push_implicit_dot_target(targets, pool);
 

Modified: subversion/trunk/subversion/svn/svn.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/svn.c?rev=1826864&r1=1826863&r2=1826864&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/svn.c (original)
+++ subversion/trunk/subversion/svn/svn.c Thu Mar 15 20:43:30 2018
@@ -147,6 +147,7 @@ typedef enum svn_cl__longopt_t {
   opt_adds_as_modification,
   opt_vacuum_pristines,
   opt_drop,
+  opt_viewspec,
 } svn_cl__longopt_t;
 
 
@@ -475,6 +476,9 @@ const apr_getopt_option_t svn_cl__option
   {"drop", opt_drop, 0,
                        N_("drop shelf after successful unshelve")},
 
+  {"viewspec", opt_viewspec, 0,
+                       N_("print the working copy layout")},
+
   /* Long-opt Aliases
    *
    * These have NULL desriptions, but an option code that matches some
@@ -813,9 +817,13 @@ const svn_opt_subcommand_desc3_t svn_cl_
      "\n"), N_(
      "  With --show-item, print only the value of one item of information\n"
      "  about TARGET.\n"
+     "\n"), N_(
+     "  EXPERIMENTAL:\n"
+     "  With --viewspec, print the working copy layout.\n"
     )},
     {'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml,
-     opt_changelist, opt_include_externals, opt_show_item, opt_no_newline}
+     opt_changelist, opt_include_externals, opt_show_item, opt_no_newline,
+     opt_viewspec}
   },
 
   { "list", svn_cl__list, {"ls"},
@@ -2732,6 +2740,9 @@ sub_main(int *exit_code, int argc, const
       case opt_vacuum_pristines:
         opt_state.vacuum_pristines = TRUE;
         break;
+      case opt_viewspec:
+        opt_state.viewspec = TRUE;
+        break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */


Reply via email to