summaryrefslogtreecommitdiffstats
path: root/subversion/svn/status-cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/status-cmd.c')
-rw-r--r--subversion/svn/status-cmd.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/subversion/svn/status-cmd.c b/subversion/svn/status-cmd.c
new file mode 100644
index 0000000..0a73dac
--- /dev/null
+++ b/subversion/svn/status-cmd.c
@@ -0,0 +1,416 @@
+/*
+ * status-cmd.c -- Display status information in current directory
+ *
+ * ====================================================================
+ * 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_string.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_xml.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_cmdline.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_wc_private.h"
+
+
+
+/*** Code. ***/
+
+struct status_baton
+{
+ /* These fields all correspond to the ones in the
+ svn_cl__print_status() interface. */
+ const char *cwd_abspath;
+ svn_boolean_t suppress_externals_placeholders;
+ svn_boolean_t detailed;
+ svn_boolean_t show_last_committed;
+ svn_boolean_t skip_unrecognized;
+ svn_boolean_t repos_locks;
+
+ apr_hash_t *cached_changelists;
+ apr_pool_t *cl_pool; /* where cached changelists are allocated */
+
+ svn_boolean_t had_print_error; /* To avoid printing lots of errors if we get
+ errors while printing to stdout */
+ svn_boolean_t xml_mode;
+
+ /* Conflict stats. */
+ unsigned int text_conflicts;
+ unsigned int prop_conflicts;
+ unsigned int tree_conflicts;
+
+ svn_client_ctx_t *ctx;
+};
+
+
+struct status_cache
+{
+ const char *path;
+ svn_client_status_t *status;
+};
+
+/* Print conflict stats accumulated in status baton SB.
+ * Do temporary allocations in POOL. */
+static svn_error_t *
+print_conflict_stats(struct status_baton *sb, apr_pool_t *pool)
+{
+ if (sb->text_conflicts > 0 || sb->prop_conflicts > 0 ||
+ sb->tree_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf(pool, "%s", _("Summary of conflicts:\n")));
+
+ if (sb->text_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Text conflicts: %u\n"), sb->text_conflicts));
+
+ if (sb->prop_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Property conflicts: %u\n"), sb->prop_conflicts));
+
+ if (sb->tree_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Tree conflicts: %u\n"), sb->tree_conflicts));
+
+ return SVN_NO_ERROR;
+}
+
+/* Prints XML target element with path attribute TARGET, using POOL for
+ temporary allocations. */
+static svn_error_t *
+print_start_target_xml(const char *target, apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target",
+ "path", target, NULL);
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* Finish a target element by optionally printing an against element if
+ * REPOS_REV is a valid revision number, and then printing an target end tag.
+ * Use POOL for temporary allocations. */
+static svn_error_t *
+print_finish_target_xml(svn_revnum_t repos_rev,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
+
+ if (SVN_IS_VALID_REVNUM(repos_rev))
+ {
+ const char *repos_rev_str;
+ repos_rev_str = apr_psprintf(pool, "%ld", repos_rev);
+ svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "against",
+ "revision", repos_rev_str, NULL);
+ }
+
+ svn_xml_make_close_tag(&sb, pool, "target");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* Function which *actually* causes a status structure to be output to
+ the user. Called by both print_status() and svn_cl__status(). */
+static svn_error_t *
+print_status_normal_or_xml(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *pool)
+{
+ struct status_baton *sb = baton;
+
+ if (sb->xml_mode)
+ return svn_cl__print_status_xml(sb->cwd_abspath, path, status,
+ sb->ctx, pool);
+ else
+ return svn_cl__print_status(sb->cwd_abspath, path, status,
+ sb->suppress_externals_placeholders,
+ sb->detailed,
+ sb->show_last_committed,
+ sb->skip_unrecognized,
+ sb->repos_locks,
+ &sb->text_conflicts,
+ &sb->prop_conflicts,
+ &sb->tree_conflicts,
+ sb->ctx,
+ pool);
+}
+
+
+/* A status callback function for printing STATUS for PATH. */
+static svn_error_t *
+print_status(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *pool)
+{
+ struct status_baton *sb = baton;
+ const char *local_abspath = status->local_abspath;
+
+ /* ### The revision information with associates are based on what
+ * ### _read_info() returns. The svn_wc_status_func4_t callback is
+ * ### suppposed to handle the gathering of additional information from the
+ * ### WORKING nodes on its own. Until we've agreed on how the CLI should
+ * ### handle the revision information, we use this appproach to stay compat
+ * ### with our testsuite. */
+ if (status->versioned
+ && !SVN_IS_VALID_REVNUM(status->revision)
+ && !status->copied
+ && (status->node_status == svn_wc_status_deleted
+ || status->node_status == svn_wc_status_replaced))
+ {
+ svn_client_status_t *twks = svn_client_status_dup(status, sb->cl_pool);
+
+ /* Copied is FALSE, so either we have a local addition, or we have
+ a delete that directly shadows a BASE node */
+
+ switch(status->node_status)
+ {
+ case svn_wc_status_replaced:
+ /* Just retrieve the revision below the replacement.
+ The other fields are filled by a copy.
+ (With ! copied, we know we have a BASE node)
+
+ ### Is this really what we want to provide? */
+ SVN_ERR(svn_wc__node_get_pre_ng_status_data(&twks->revision,
+ NULL, NULL, NULL,
+ sb->ctx->wc_ctx,
+ local_abspath,
+ sb->cl_pool, pool));
+ break;
+ case svn_wc_status_deleted:
+ /* Retrieve some data from the original version below the delete */
+ SVN_ERR(svn_wc__node_get_pre_ng_status_data(&twks->revision,
+ &twks->changed_rev,
+ &twks->changed_date,
+ &twks->changed_author,
+ sb->ctx->wc_ctx,
+ local_abspath,
+ sb->cl_pool, pool));
+ break;
+
+ default:
+ /* This space intentionally left blank. */
+ break;
+ }
+
+ status = twks;
+ }
+
+ /* If the path is part of a changelist, then we don't print
+ the item, but instead dup & cache the status structure for later. */
+ if (status->changelist)
+ {
+ /* The hash maps a changelist name to an array of status_cache
+ structures. */
+ apr_array_header_t *path_array;
+ const char *cl_key = apr_pstrdup(sb->cl_pool, status->changelist);
+ struct status_cache *scache = apr_pcalloc(sb->cl_pool, sizeof(*scache));
+ scache->path = apr_pstrdup(sb->cl_pool, path);
+ scache->status = svn_client_status_dup(status, sb->cl_pool);
+
+ path_array =
+ svn_hash_gets(sb->cached_changelists, cl_key);
+ if (path_array == NULL)
+ {
+ path_array = apr_array_make(sb->cl_pool, 1,
+ sizeof(struct status_cache *));
+ svn_hash_sets(sb->cached_changelists, cl_key, path_array);
+ }
+
+ APR_ARRAY_PUSH(path_array, struct status_cache *) = scache;
+ return SVN_NO_ERROR;
+ }
+
+ return print_status_normal_or_xml(baton, path, status, pool);
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__status(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *scratch_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_array_header_t *targets;
+ apr_pool_t *iterpool;
+ apr_hash_t *master_cl_hash = apr_hash_make(scratch_pool);
+ int i;
+ svn_opt_revision_t rev;
+ struct status_baton sb;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ /* We want our -u statuses to be against HEAD. */
+ rev.kind = svn_opt_revision_head;
+
+ sb.had_print_error = FALSE;
+
+ if (opt_state->xml)
+ {
+ /* If output is not incremental, output the XML header and wrap
+ everything in a top-level element. This makes the output in
+ its entirety a well-formed XML document. */
+ if (! opt_state->incremental)
+ SVN_ERR(svn_cl__xml_print_header("status", scratch_pool));
+ }
+ else
+ {
+ if (opt_state->incremental)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'incremental' option only valid in XML "
+ "mode"));
+ }
+
+ SVN_ERR(svn_dirent_get_absolute(&(sb.cwd_abspath), "", scratch_pool));
+ sb.suppress_externals_placeholders = (opt_state->quiet
+ && (! opt_state->verbose));
+ sb.detailed = (opt_state->verbose || opt_state->update);
+ sb.show_last_committed = opt_state->verbose;
+ sb.skip_unrecognized = opt_state->quiet;
+ sb.repos_locks = opt_state->update;
+ sb.xml_mode = opt_state->xml;
+ sb.cached_changelists = master_cl_hash;
+ sb.cl_pool = scratch_pool;
+ sb.text_conflicts = 0;
+ sb.prop_conflicts = 0;
+ sb.tree_conflicts = 0;
+ sb.ctx = ctx;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_revnum_t repos_rev = SVN_INVALID_REVNUM;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ if (opt_state->xml)
+ SVN_ERR(print_start_target_xml(svn_dirent_local_style(target, iterpool),
+ iterpool));
+
+ /* Retrieve a hash of status structures with the information
+ requested by the user. */
+ SVN_ERR(svn_cl__try(svn_client_status5(&repos_rev, ctx, target, &rev,
+ opt_state->depth,
+ opt_state->verbose,
+ opt_state->update,
+ opt_state->no_ignore,
+ opt_state->ignore_externals,
+ FALSE /* depth_as_sticky */,
+ opt_state->changelists,
+ print_status, &sb,
+ iterpool),
+ NULL, opt_state->quiet,
+ /* not versioned: */
+ SVN_ERR_WC_NOT_WORKING_COPY,
+ SVN_ERR_WC_PATH_NOT_FOUND));
+
+ if (opt_state->xml)
+ SVN_ERR(print_finish_target_xml(repos_rev, iterpool));
+ }
+
+ /* If any paths were cached because they were associatied with
+ changelists, we can now display them as grouped changelists. */
+ if (apr_hash_count(master_cl_hash) > 0)
+ {
+ apr_hash_index_t *hi;
+ svn_stringbuf_t *buf;
+
+ if (opt_state->xml)
+ buf = svn_stringbuf_create_empty(scratch_pool);
+
+ for (hi = apr_hash_first(scratch_pool, master_cl_hash); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *changelist_name = svn__apr_hash_index_key(hi);
+ apr_array_header_t *path_array = svn__apr_hash_index_val(hi);
+ int j;
+
+ /* ### TODO: For non-XML output, we shouldn't print the
+ ### leading \n on the first changelist if there were no
+ ### non-changelist entries. */
+ if (opt_state->xml)
+ {
+ svn_stringbuf_setempty(buf);
+ svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
+ "changelist", "name", changelist_name,
+ NULL);
+ SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
+ }
+ else
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("\n--- Changelist '%s':\n"),
+ changelist_name));
+
+ for (j = 0; j < path_array->nelts; j++)
+ {
+ struct status_cache *scache =
+ APR_ARRAY_IDX(path_array, j, struct status_cache *);
+ SVN_ERR(print_status_normal_or_xml(&sb, scache->path,
+ scache->status, scratch_pool));
+ }
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_setempty(buf);
+ svn_xml_make_close_tag(&buf, scratch_pool, "changelist");
+ SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
+ }
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ if (opt_state->xml && (! opt_state->incremental))
+ SVN_ERR(svn_cl__xml_print_footer("status", scratch_pool));
+
+ if (! opt_state->quiet && ! opt_state->xml)
+ SVN_ERR(print_conflict_stats(&sb, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud