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. */