summaryrefslogtreecommitdiff
path: root/subversion/svn
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 14:29:52 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 14:29:52 +0100
commitf1bdf13786f0752c0846cf36f0d91e4fc6747929 (patch)
tree4223b2035bf2240d681a53822808b3c7f687b905 /subversion/svn
downloadsubversion-tarball-f1bdf13786f0752c0846cf36f0d91e4fc6747929.tar.gz
Tarball conversion
Diffstat (limited to 'subversion/svn')
-rw-r--r--subversion/svn/add-cmd.c112
-rw-r--r--subversion/svn/blame-cmd.c416
-rw-r--r--subversion/svn/cat-cmd.c118
-rw-r--r--subversion/svn/changelist-cmd.c149
-rw-r--r--subversion/svn/checkout-cmd.c173
-rw-r--r--subversion/svn/cl.h840
-rw-r--r--subversion/svn/cleanup-cmd.c104
-rw-r--r--subversion/svn/client_errors.h95
-rw-r--r--subversion/svn/commit-cmd.c184
-rw-r--r--subversion/svn/conflict-callbacks.c757
-rw-r--r--subversion/svn/copy-cmd.c140
-rw-r--r--subversion/svn/delete-cmd.c95
-rw-r--r--subversion/svn/diff-cmd.c439
-rw-r--r--subversion/svn/export-cmd.c122
-rw-r--r--subversion/svn/help-cmd.c93
-rw-r--r--subversion/svn/import-cmd.c130
-rw-r--r--subversion/svn/info-cmd.c635
-rw-r--r--subversion/svn/list-cmd.c328
-rw-r--r--subversion/svn/lock-cmd.c110
-rw-r--r--subversion/svn/log-cmd.c706
-rw-r--r--subversion/svn/main.c2709
-rw-r--r--subversion/svn/merge-cmd.c383
-rw-r--r--subversion/svn/mergeinfo-cmd.c135
-rw-r--r--subversion/svn/mkdir-cmd.c104
-rw-r--r--subversion/svn/move-cmd.c101
-rw-r--r--subversion/svn/notify.c1083
-rw-r--r--subversion/svn/patch-cmd.c98
-rw-r--r--subversion/svn/propdel-cmd.c103
-rw-r--r--subversion/svn/propedit-cmd.c351
-rw-r--r--subversion/svn/propget-cmd.c358
-rw-r--r--subversion/svn/proplist-cmd.c249
-rw-r--r--subversion/svn/props.c224
-rw-r--r--subversion/svn/propset-cmd.c183
-rw-r--r--subversion/svn/relocate-cmd.c120
-rw-r--r--subversion/svn/resolve-cmd.c119
-rw-r--r--subversion/svn/resolved-cmd.c91
-rw-r--r--subversion/svn/revert-cmd.c81
-rw-r--r--subversion/svn/schema/blame.rnc42
-rw-r--r--subversion/svn/schema/common.rnc77
-rw-r--r--subversion/svn/schema/diff.rnc39
-rw-r--r--subversion/svn/schema/info.rnc124
-rw-r--r--subversion/svn/schema/list.rnc45
-rw-r--r--subversion/svn/schema/log.rnc55
-rw-r--r--subversion/svn/schema/props.rnc36
-rw-r--r--subversion/svn/schema/status.rnc88
-rw-r--r--subversion/svn/status-cmd.c408
-rw-r--r--subversion/svn/status.c444
-rw-r--r--subversion/svn/svn.147
-rw-r--r--subversion/svn/switch-cmd.c187
-rw-r--r--subversion/svn/tree-conflicts.c196
-rw-r--r--subversion/svn/tree-conflicts.h68
-rw-r--r--subversion/svn/unlock-cmd.c68
-rw-r--r--subversion/svn/update-cmd.c181
-rw-r--r--subversion/svn/upgrade-cmd.c78
-rw-r--r--subversion/svn/util.c1421
55 files changed, 15842 insertions, 0 deletions
diff --git a/subversion/svn/add-cmd.c b/subversion/svn/add-cmd.c
new file mode 100644
index 0000000..7b79ee1
--- /dev/null
+++ b/subversion/svn/add-cmd.c
@@ -0,0 +1,112 @@
+/*
+ * add-cmd.c -- Subversion add/unadd commands
+ *
+ * ====================================================================
+ * 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. ***/
+#define APR_WANT_STDIO
+#include <apr_want.h>
+
+#include "svn_path.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__add(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_array_header_t *targets;
+ int i;
+ apr_pool_t *iterpool;
+ apr_array_header_t *errors = apr_array_make(pool, 0, sizeof(apr_status_t));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_infinity;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ iterpool = svn_pool_create(pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ SVN_ERR(svn_cl__try
+ (svn_client_add4(target,
+ opt_state->depth,
+ opt_state->force, opt_state->no_ignore,
+ opt_state->parents, ctx, iterpool),
+ errors, opt_state->quiet,
+ SVN_ERR_ENTRY_EXISTS,
+ SVN_ERR_WC_PATH_NOT_FOUND,
+ SVN_NO_ERROR));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ if (errors->nelts > 0)
+ {
+ svn_error_t *err;
+
+ err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
+ for (i = 0; i < errors->nelts; i++)
+ {
+ apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
+ if (status == SVN_ERR_WC_PATH_NOT_FOUND)
+ err = svn_error_quick_wrap(err,
+ _("Could not add all targets because "
+ "some targets don't exist"));
+ else if (status == SVN_ERR_ENTRY_EXISTS)
+ err = svn_error_quick_wrap(err,
+ _("Could not add all targets because "
+ "some targets are already versioned"));
+ }
+
+ return svn_error_trace(err);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/blame-cmd.c b/subversion/svn/blame-cmd.c
new file mode 100644
index 0000000..e198178
--- /dev/null
+++ b/subversion/svn/blame-cmd.c
@@ -0,0 +1,416 @@
+/*
+ * blame-cmd.c -- Display blame information
+ *
+ * ====================================================================
+ * 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_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_cmdline.h"
+#include "svn_xml.h"
+#include "svn_time.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+typedef struct blame_baton_t
+{
+ svn_cl__opt_state_t *opt_state;
+ svn_stream_t *out;
+ svn_stringbuf_t *sbuf;
+} blame_baton_t;
+
+
+/*** Code. ***/
+
+/* This implements the svn_client_blame_receiver3_t interface, printing
+ XML to stdout. */
+static svn_error_t *
+blame_receiver_xml(void *baton,
+ svn_revnum_t start_revnum,
+ svn_revnum_t end_revnum,
+ apr_int64_t line_no,
+ svn_revnum_t revision,
+ apr_hash_t *rev_props,
+ svn_revnum_t merged_revision,
+ apr_hash_t *merged_rev_props,
+ const char *merged_path,
+ const char *line,
+ svn_boolean_t local_change,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state =
+ ((blame_baton_t *) baton)->opt_state;
+ svn_stringbuf_t *sb = ((blame_baton_t *) baton)->sbuf;
+
+ /* "<entry ...>" */
+ /* line_no is 0-based, but the rest of the world is probably Pascal
+ programmers, so we make them happy and output 1-based line numbers. */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "line-number",
+ apr_psprintf(pool, "%" APR_INT64_T_FMT,
+ line_no + 1),
+ NULL);
+
+ if (SVN_IS_VALID_REVNUM(revision))
+ svn_cl__print_xml_commit(&sb, revision,
+ svn_prop_get_value(rev_props,
+ SVN_PROP_REVISION_AUTHOR),
+ svn_prop_get_value(rev_props,
+ SVN_PROP_REVISION_DATE),
+ pool);
+
+ if (opt_state->use_merge_history && SVN_IS_VALID_REVNUM(merged_revision))
+ {
+ /* "<merged>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "merged",
+ "path", merged_path, NULL);
+
+ svn_cl__print_xml_commit(&sb, merged_revision,
+ svn_prop_get_value(merged_rev_props,
+ SVN_PROP_REVISION_AUTHOR),
+ svn_prop_get_value(merged_rev_props,
+ SVN_PROP_REVISION_DATE),
+ pool);
+
+ /* "</merged>" */
+ svn_xml_make_close_tag(&sb, pool, "merged");
+
+ }
+
+ /* "</entry>" */
+ svn_xml_make_close_tag(&sb, pool, "entry");
+
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ svn_stringbuf_setempty(sb);
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+print_line_info(svn_stream_t *out,
+ svn_revnum_t revision,
+ const char *author,
+ const char *date,
+ const char *path,
+ svn_boolean_t verbose,
+ svn_revnum_t end_revnum,
+ apr_pool_t *pool)
+{
+ const char *time_utf8;
+ const char *time_stdout;
+ const char *rev_str;
+ int rev_maxlength;
+
+ /* The standard column width for the revision number is 6 characters.
+ If the revision number can potentially be larger (i.e. if the end_revnum
+ is larger than 1000000), we increase the column width as needed. */
+ rev_maxlength = 6;
+ while (end_revnum >= 1000000)
+ {
+ rev_maxlength++;
+ end_revnum = end_revnum / 10;
+ }
+ rev_str = SVN_IS_VALID_REVNUM(revision)
+ ? apr_psprintf(pool, "%*ld", rev_maxlength, revision)
+ : apr_psprintf(pool, "%*s", rev_maxlength, "-");
+
+ if (verbose)
+ {
+ if (date)
+ {
+ SVN_ERR(svn_cl__time_cstring_to_human_cstring(&time_utf8,
+ date, pool));
+ SVN_ERR(svn_cmdline_cstring_from_utf8(&time_stdout, time_utf8,
+ pool));
+ }
+ else
+ {
+ /* ### This is a 44 characters long string. It assumes the current
+ format of svn_time_to_human_cstring and also 3 letter
+ abbreviations for the month and weekday names. Else, the
+ line contents will be misaligned. */
+ time_stdout = " -";
+ }
+
+ SVN_ERR(svn_stream_printf(out, pool, "%s %10s %s ", rev_str,
+ author ? author : " -",
+ time_stdout));
+
+ if (path)
+ SVN_ERR(svn_stream_printf(out, pool, "%-14s ", path));
+ }
+ else
+ {
+ return svn_stream_printf(out, pool, "%s %10.10s ", rev_str,
+ author ? author : " -");
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the svn_client_blame_receiver3_t interface. */
+static svn_error_t *
+blame_receiver(void *baton,
+ svn_revnum_t start_revnum,
+ svn_revnum_t end_revnum,
+ apr_int64_t line_no,
+ svn_revnum_t revision,
+ apr_hash_t *rev_props,
+ svn_revnum_t merged_revision,
+ apr_hash_t *merged_rev_props,
+ const char *merged_path,
+ const char *line,
+ svn_boolean_t local_change,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state =
+ ((blame_baton_t *) baton)->opt_state;
+ svn_stream_t *out = ((blame_baton_t *)baton)->out;
+ svn_boolean_t use_merged = FALSE;
+
+ if (opt_state->use_merge_history)
+ {
+ /* Choose which revision to use. If they aren't equal, prefer the
+ earliest revision. Since we do a forward blame, we want to the first
+ revision which put the line in its current state, so we use the
+ earliest revision. If we ever switch to a backward blame algorithm,
+ we may need to adjust this. */
+ if (merged_revision < revision)
+ {
+ SVN_ERR(svn_stream_printf(out, pool, "G "));
+ use_merged = TRUE;
+ }
+ else
+ SVN_ERR(svn_stream_printf(out, pool, " "));
+ }
+
+ if (use_merged)
+ SVN_ERR(print_line_info(out, merged_revision,
+ svn_prop_get_value(merged_rev_props,
+ SVN_PROP_REVISION_AUTHOR),
+ svn_prop_get_value(merged_rev_props,
+ SVN_PROP_REVISION_DATE),
+ merged_path, opt_state->verbose, end_revnum,
+ pool));
+ else
+ SVN_ERR(print_line_info(out, revision,
+ svn_prop_get_value(rev_props,
+ SVN_PROP_REVISION_AUTHOR),
+ svn_prop_get_value(rev_props,
+ SVN_PROP_REVISION_DATE),
+ NULL, opt_state->verbose, end_revnum,
+ pool));
+
+ return svn_stream_printf(out, pool, "%s%s", line, APR_EOL_STR);
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__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 *subpool;
+ apr_array_header_t *targets;
+ blame_baton_t bl;
+ 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.
+ */
+ if (! opt_state->xml)
+ SVN_ERR(svn_stream_for_stdout(&bl.out, pool));
+ else
+ bl.sbuf = svn_stringbuf_create("", pool);
+
+ bl.opt_state = opt_state;
+
+ subpool = 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));
+ }
+
+ if (opt_state->xml)
+ {
+ if (opt_state->verbose)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'verbose' option invalid in XML mode"));
+
+ /* 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("blame", pool));
+ }
+ else
+ {
+ if (opt_state->incremental)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'incremental' option only valid in XML "
+ "mode"));
+ }
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ svn_error_t *err;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+ svn_client_blame_receiver3_t receiver;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Check for a peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ subpool));
+
+ 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;
+ }
+
+ if (opt_state->xml)
+ {
+ /* "<target ...>" */
+ /* We don't output this tag immediately, which avoids creating
+ a target element if this path is skipped. */
+ const char *outpath = truepath;
+ if (! svn_path_is_url(target))
+ outpath = svn_dirent_local_style(truepath, subpool);
+ svn_xml_make_open_tag(&bl.sbuf, pool, svn_xml_normal, "target",
+ "path", outpath, NULL);
+
+ receiver = blame_receiver_xml;
+ }
+ else
+ receiver = blame_receiver;
+
+ err = svn_client_blame5(truepath,
+ &peg_revision,
+ &opt_state->start_revision,
+ &opt_state->end_revision,
+ diff_options,
+ opt_state->force,
+ opt_state->use_merge_history,
+ receiver,
+ &bl,
+ ctx,
+ subpool);
+
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CLIENT_IS_BINARY_FILE)
+ {
+ svn_error_clear(err);
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Skipping binary file: '%s'\n"),
+ target));
+ }
+ else if (err->apr_err == SVN_ERR_WC_PATH_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);
+ }
+ }
+ else if (opt_state->xml)
+ {
+ /* "</target>" */
+ svn_xml_make_close_tag(&(bl.sbuf), pool, "target");
+ SVN_ERR(svn_cl__error_checked_fputs(bl.sbuf->data, stdout));
+ }
+
+ if (opt_state->xml)
+ svn_stringbuf_setempty(bl.sbuf);
+ }
+ svn_pool_destroy(subpool);
+ if (opt_state->xml && ! opt_state->incremental)
+ SVN_ERR(svn_cl__xml_print_footer("blame", pool));
+
+ 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;
+}
diff --git a/subversion/svn/cat-cmd.c b/subversion/svn/cat-cmd.c
new file mode 100644
index 0000000..7e28a81
--- /dev/null
+++ b/subversion/svn/cat-cmd.c
@@ -0,0 +1,118 @@
+/*
+ * cat-cmd.c -- Print the content of a file or URL.
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_opt.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__cat(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_array_header_t *targets;
+ int i;
+ svn_stream_t *out;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ apr_array_header_t *errors = apr_array_make(pool, 0, sizeof(apr_status_t));
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Cat cannot operate on an implicit '.' so a filename is required */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_stream_for_stdout(&out, pool));
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Get peg revisions. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ subpool));
+
+ SVN_ERR(svn_cl__try(svn_client_cat2(out, truepath, &peg_revision,
+ &(opt_state->start_revision),
+ ctx, subpool),
+ errors, opt_state->quiet,
+ SVN_ERR_UNVERSIONED_RESOURCE,
+ SVN_ERR_ENTRY_NOT_FOUND,
+ SVN_ERR_CLIENT_IS_DIRECTORY,
+ SVN_ERR_FS_NOT_FOUND,
+ SVN_NO_ERROR));
+ }
+ svn_pool_destroy(subpool);
+
+ if (errors->nelts > 0)
+ {
+ err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
+
+ for (i = 0; i < errors->nelts; i++)
+ {
+ apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
+
+ if (status == SVN_ERR_ENTRY_NOT_FOUND ||
+ status == SVN_ERR_FS_NOT_FOUND)
+ err = svn_error_quick_wrap(err,
+ _("Could not cat all targets because "
+ "some targets don't exist"));
+ else if (status == SVN_ERR_UNVERSIONED_RESOURCE)
+ err = svn_error_quick_wrap(err,
+ _("Could not cat all targets because "
+ "some targets are not versioned"));
+ else if (status == SVN_ERR_CLIENT_IS_DIRECTORY)
+ err = svn_error_quick_wrap(err,
+ _("Could not cat all targets because "
+ "some targets are directories"));
+ }
+
+ return svn_error_trace(err);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/changelist-cmd.c b/subversion/svn/changelist-cmd.c
new file mode 100644
index 0000000..46347b6
--- /dev/null
+++ b/subversion/svn/changelist-cmd.c
@@ -0,0 +1,149 @@
+/*
+ * changelist-cmd.c -- Associate (or deassociate) a wc path with a changelist.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "svn_utf.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__changelist(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const char *changelist_name = NULL;
+ 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;
+ svn_depth_t depth = opt_state->depth;
+ apr_array_header_t *errors = apr_array_make(pool, 0, sizeof(apr_status_t));
+
+ /* If we're not removing changelists, then our first argument should
+ be the name of a changelist. */
+
+ if (! opt_state->remove)
+ {
+ apr_array_header_t *args;
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
+ changelist_name = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&changelist_name,
+ changelist_name, pool));
+ }
+
+ /* Parse the remaining arguments as paths. */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Changelist has no implicit dot-target `.', so don't you put that
+ code here! */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ if (opt_state->quiet)
+ /* FIXME: This is required because svn_client_create_context()
+ always initializes ctx->notify_func2 to a wrapper function
+ which calls ctx->notify_func() if it isn't NULL. In other
+ words, typically, ctx->notify_func2 is never NULL. This isn't
+ usually a problem, but the changelist logic generates
+ svn_error_t's as part of its notification.
+
+ So, svn_wc_set_changelist() checks its notify_func (our
+ ctx->notify_func2) for NULL-ness, and seeing non-NULL-ness,
+ generates a notificaton object and svn_error_t to describe some
+ problem. It passes that off to its notify_func (our
+ ctx->notify_func2) which drops the notification on the floor
+ (because it wraps a NULL ctx->notify_func). But svn_error_t's
+ dropped on the floor cause SEGFAULTs at pool cleanup time --
+ they need instead to be cleared.
+
+ SOOOooo... we set our ctx->notify_func2 to NULL so the WC code
+ doesn't even generate the errors. */
+ ctx->notify_func2 = NULL;
+
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_empty;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ if (changelist_name)
+ {
+ SVN_ERR(svn_cl__try(
+ svn_client_add_to_changelist(targets, changelist_name,
+ depth, opt_state->changelists,
+ ctx, pool),
+ errors, opt_state->quiet,
+ SVN_ERR_UNVERSIONED_RESOURCE,
+ SVN_ERR_WC_PATH_NOT_FOUND,
+ SVN_NO_ERROR));
+ }
+ else
+ {
+ SVN_ERR(svn_cl__try(
+ svn_client_remove_from_changelists(targets, depth,
+ opt_state->changelists,
+ ctx, pool),
+ errors, opt_state->quiet,
+ SVN_ERR_UNVERSIONED_RESOURCE,
+ SVN_ERR_WC_PATH_NOT_FOUND,
+ SVN_NO_ERROR));
+ }
+
+ if (errors->nelts > 0)
+ {
+ int i;
+ svn_error_t *err;
+
+ err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
+ for (i = 0; i < errors->nelts; i++)
+ {
+ apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
+
+ if (status == SVN_ERR_WC_PATH_NOT_FOUND)
+ err = svn_error_quick_wrap(err,
+ _("Could not set changelists on "
+ "all targets because some targets "
+ "don't exist"));
+ else if (status == SVN_ERR_UNVERSIONED_RESOURCE)
+ err = svn_error_quick_wrap(err,
+ _("Could not set changelists on "
+ "all targets because some targets "
+ "are not versioned"));
+ }
+
+ return svn_error_trace(err);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/checkout-cmd.c b/subversion/svn/checkout-cmd.c
new file mode 100644
index 0000000..6c192a0
--- /dev/null
+++ b/subversion/svn/checkout-cmd.c
@@ -0,0 +1,173 @@
+/*
+ * checkout-cmd.c -- Subversion checkout 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_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/*
+ This is what it does
+
+ - case 1: one URL
+ $ svn co http://host/repos/module
+ checkout into ./module/
+
+ - case 2: one URL and explicit path
+ $ svn co http://host/repos/module path
+ checkout into ./path/
+
+ - case 3: multiple URLs
+ $ svn co http://host1/repos1/module1 http://host2/repos2/module2
+ checkout into ./module1/ and ./module2/
+
+ - case 4: multiple URLs and explicit path
+ $ svn co http://host1/repos1/module1 http://host2/repos2/module2 path
+ checkout into ./path/module1/ and ./path/module2/
+
+ Is this the same as CVS? Does it matter if it is not?
+*/
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__checkout(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 *subpool;
+ apr_array_header_t *targets;
+ const char *last_target, *local_dir;
+ int i;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
+
+ /* Determine LOCAL_DIR (case 1: URL basename; 2,4: specified; 3: "")
+ * and leave TARGETS holding just the source URLs. */
+ last_target = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
+ if (svn_path_is_url(last_target))
+ {
+ if (targets->nelts == 1)
+ {
+ svn_opt_revision_t pegrev;
+
+ /* Use the URL basename, discarding any peg revision. */
+ SVN_ERR(svn_opt_parse_path(&pegrev, &local_dir, last_target, pool));
+ local_dir = svn_uri_basename(local_dir, pool);
+ }
+ else
+ {
+ local_dir = "";
+ }
+ }
+ else
+ {
+ if (targets->nelts == 1)
+ /* What? They gave us one target, and it wasn't a URL. */
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, NULL);
+
+ apr_array_pop(targets);
+ local_dir = last_target;
+ }
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__notifier_mark_checkout(ctx->notify_baton2));
+
+ subpool = svn_pool_create(pool);
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *repos_url = APR_ARRAY_IDX(targets, i, const char *);
+ const char *target_dir;
+ const char *true_url;
+ svn_opt_revision_t revision = opt_state->start_revision;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Validate the REPOS_URL */
+ if (! svn_path_is_url(repos_url))
+ return svn_error_createf
+ (SVN_ERR_BAD_URL, NULL,
+ _("'%s' does not appear to be a URL"), repos_url);
+
+ /* Get a possible peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &true_url, repos_url,
+ subpool));
+
+ /* Use sub-directory of destination if checking-out multiple URLs */
+ if (targets->nelts == 1)
+ {
+ target_dir = local_dir;
+ }
+ else
+ {
+ target_dir = svn_dirent_join(local_dir,
+ svn_uri_basename(true_url, subpool),
+ subpool);
+ }
+
+ /* Checkout doesn't accept an unspecified revision, so default to
+ the peg revision, or to HEAD if there wasn't a peg. */
+ if (revision.kind == svn_opt_revision_unspecified)
+ {
+ if (peg_revision.kind != svn_opt_revision_unspecified)
+ revision = peg_revision;
+ else
+ revision.kind = svn_opt_revision_head;
+ }
+
+ SVN_ERR(svn_client_checkout3
+ (NULL, true_url, target_dir,
+ &peg_revision,
+ &revision,
+ opt_state->depth,
+ opt_state->ignore_externals,
+ opt_state->force,
+ ctx, subpool));
+ }
+ svn_pool_destroy(subpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/cl.h b/subversion/svn/cl.h
new file mode 100644
index 0000000..2d4d341
--- /dev/null
+++ b/subversion/svn/cl.h
@@ -0,0 +1,840 @@
+/*
+ * cl.h: shared stuff in the command line program
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+#ifndef SVN_CL_H
+#define SVN_CL_H
+
+/*** Includes. ***/
+#include <apr_tables.h>
+#include <apr_getopt.h>
+
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_opt.h"
+#include "svn_auth.h"
+#include "svn_cmdline.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*** Option processing ***/
+
+/* --accept actions */
+typedef enum svn_cl__accept_t
+{
+ /* invalid accept action */
+ svn_cl__accept_invalid = -2,
+
+ /* unspecified accept action */
+ svn_cl__accept_unspecified = -1,
+
+ /* Leave conflicts alone, for later resolution. */
+ svn_cl__accept_postpone,
+
+ /* Resolve the conflict with the pre-conflict base file. */
+ svn_cl__accept_base,
+
+ /* Resolve the conflict with the current working file. */
+ svn_cl__accept_working,
+
+ /* Resolve the conflicted hunks by choosing the corresponding text
+ from the pre-conflict working copy file.
+
+ Note: this is a placeholder, not actually implemented in 1.5. */
+ svn_cl__accept_mine_conflict,
+
+ /* Resolve the conflicted hunks by choosing the corresponding text
+ from the post-conflict base copy file.
+
+ Note: this is a placeholder, not actually implemented in 1.5. */
+ svn_cl__accept_theirs_conflict,
+
+ /* Resolve the conflict by taking the entire pre-conflict working
+ copy file. */
+ svn_cl__accept_mine_full,
+
+ /* Resolve the conflict by taking the entire post-conflict base file. */
+ svn_cl__accept_theirs_full,
+
+ /* Launch user's editor and resolve conflict with edited file. */
+ svn_cl__accept_edit,
+
+ /* Launch user's resolver and resolve conflict with edited file. */
+ svn_cl__accept_launch
+
+} svn_cl__accept_t;
+
+/* --accept action user input words */
+#define SVN_CL__ACCEPT_POSTPONE "postpone"
+#define SVN_CL__ACCEPT_BASE "base"
+#define SVN_CL__ACCEPT_WORKING "working"
+#define SVN_CL__ACCEPT_MINE_CONFLICT "mine-conflict"
+#define SVN_CL__ACCEPT_THEIRS_CONFLICT "theirs-conflict"
+#define SVN_CL__ACCEPT_MINE_FULL "mine-full"
+#define SVN_CL__ACCEPT_THEIRS_FULL "theirs-full"
+#define SVN_CL__ACCEPT_EDIT "edit"
+#define SVN_CL__ACCEPT_LAUNCH "launch"
+
+/* Return the svn_cl__accept_t value corresponding to WORD, using exact
+ * case-sensitive string comparison. Return svn_cl__accept_invalid if WORD
+ * is empty or is not one of the known values. */
+svn_cl__accept_t
+svn_cl__accept_from_word(const char *word);
+
+
+/*** Mergeinfo flavors. ***/
+
+/* --show-revs values */
+typedef enum svn_cl__show_revs_t {
+ svn_cl__show_revs_invalid = -1,
+ svn_cl__show_revs_merged,
+ svn_cl__show_revs_eligible
+} svn_cl__show_revs_t;
+
+/* --show-revs user input words */
+#define SVN_CL__SHOW_REVS_MERGED "merged"
+#define SVN_CL__SHOW_REVS_ELIGIBLE "eligible"
+
+/* Return svn_cl__show_revs_t value corresponding to word. */
+svn_cl__show_revs_t
+svn_cl__show_revs_from_word(const char *word);
+
+
+/*** Command dispatch. ***/
+
+/* Hold results of option processing that are shared by multiple
+ commands. */
+typedef struct svn_cl__opt_state_t
+{
+ /* An array of svn_opt_revision_range_t *'s representing revisions
+ ranges indicated on the command-line via the -r and -c options.
+ For each range in the list, if only one revision was provided
+ (-rN), its 'end' member remains 'svn_opt_revision_unspecified'.
+ This array always has at least one element, even if that is a
+ null range in which both ends are 'svn_opt_revision_unspecified'. */
+ apr_array_header_t *revision_ranges;
+
+ /* These are simply a copy of the range start and end values present
+ in the first item of the revision_ranges list. */
+ svn_opt_revision_t start_revision;
+ svn_opt_revision_t end_revision;
+
+ /* Flag which is only set if the '-c' option was used. */
+ svn_boolean_t used_change_arg;
+
+ /* Flag which is only set if the '-r' option was used. */
+ svn_boolean_t used_revision_arg;
+
+ /* Max number of log messages to get back from svn_client_log2. */
+ int limit;
+
+ /* After option processing is done, reflects the switch actually
+ given on the command line, or svn_depth_unknown if none. */
+ svn_depth_t depth;
+
+ /* Was --no-unlock specified? */
+ svn_boolean_t no_unlock;
+
+ const char *message; /* log message */
+ const char *ancestor_path; /* ### todo: who sets this? */
+ svn_boolean_t force; /* be more forceful, as in "svn rm -f ..." */
+ svn_boolean_t force_log; /* force validity of a suspect log msg file */
+ svn_boolean_t incremental; /* yield output suitable for concatenation */
+ svn_boolean_t quiet; /* sssh...avoid unnecessary output */
+ svn_boolean_t non_interactive; /* do no interactive prompting */
+ svn_boolean_t version; /* print version information */
+ svn_boolean_t verbose; /* be verbose */
+ svn_boolean_t update; /* contact the server for the full story */
+ svn_boolean_t strict; /* do strictly what was requested */
+ svn_stringbuf_t *filedata; /* contents of file used as option data */
+ const char *encoding; /* the locale/encoding of the data*/
+ svn_boolean_t help; /* print usage message */
+ const char *auth_username; /* auth username */ /* UTF-8! */
+ const char *auth_password; /* auth password */ /* UTF-8! */
+ const char *extensions; /* subprocess extension args */ /* UTF-8! */
+ apr_array_header_t *targets; /* target list from file */ /* UTF-8! */
+ svn_boolean_t xml; /* output in xml, e.g., "svn log --xml" */
+ svn_boolean_t no_ignore; /* disregard default ignores & svn:ignore's */
+ svn_boolean_t no_auth_cache; /* do not cache authentication information */
+ svn_boolean_t no_diff_deleted; /* do not show diffs for deleted files */
+ svn_boolean_t show_copies_as_adds; /* do not diff copies with their source */
+ svn_boolean_t notice_ancestry; /* notice ancestry for diff-y operations */
+ svn_boolean_t ignore_ancestry; /* ignore ancestry for merge-y operations */
+ svn_boolean_t ignore_externals;/* ignore externals definitions */
+ svn_boolean_t stop_on_copy; /* don't cross copies during processing */
+ svn_boolean_t dry_run; /* try operation but make no changes */
+ svn_boolean_t revprop; /* operate on a revision property */
+ const char *diff_cmd; /* the external diff command to use */
+ const char *merge_cmd; /* the external merge command to use */
+ const char *editor_cmd; /* the external editor command to use */
+ svn_boolean_t record_only; /* whether to record mergeinfo */
+ const char *old_target; /* diff target */
+ const char *new_target; /* diff target */
+ svn_boolean_t relocate; /* rewrite urls (svn switch) */
+ const char *config_dir; /* over-riding configuration directory */
+ apr_array_header_t *config_options; /* over-riding configuration options */
+ svn_boolean_t autoprops; /* enable automatic properties */
+ svn_boolean_t no_autoprops; /* disable automatic properties */
+ const char *native_eol; /* override system standard eol marker */
+ svn_boolean_t summarize; /* create a summary of a diff */
+ svn_boolean_t remove; /* deassociate a changelist */
+ apr_array_header_t *changelists; /* changelist filters */
+ const char *changelist; /* operate on this changelist
+ THIS IS TEMPORARY (LAST OF CHANGELISTS) */
+ svn_boolean_t keep_changelists;/* don't remove changelists after commit */
+ svn_boolean_t keep_local; /* delete path only from repository */
+ svn_boolean_t all_revprops; /* retrieve all revprops */
+ svn_boolean_t no_revprops; /* retrieve no revprops */
+ apr_hash_t *revprop_table; /* table of revision properties to get/set */
+ svn_boolean_t parents; /* create intermediate directories */
+ svn_boolean_t use_merge_history; /* use/display extra merge information */
+ svn_cl__accept_t accept_which; /* how to handle conflicts */
+ svn_cl__show_revs_t show_revs; /* mergeinfo flavor */
+ svn_depth_t set_depth; /* new sticky ambient depth value */
+ svn_boolean_t reintegrate; /* use "reintegrate" merge-source heuristic */
+ svn_boolean_t trust_server_cert; /* trust server SSL certs that would
+ otherwise be rejected as "untrusted" */
+ int strip; /* number of leading path components to strip */
+ svn_boolean_t ignore_keywords; /* do not expand keywords */
+ svn_boolean_t reverse_diff; /* reverse a diff (e.g. when patching) */
+ svn_boolean_t ignore_whitespace; /* don't account for whitespace when
+ patching */
+ svn_boolean_t show_diff; /* produce diff output (maps to --diff) */
+ svn_boolean_t internal_diff; /* override diff_cmd in config file */
+ svn_boolean_t use_git_diff_format; /* Use git's extended diff format */
+ svn_boolean_t allow_mixed_rev; /* Allow operation on mixed-revision WC */
+} svn_cl__opt_state_t;
+
+
+typedef struct svn_cl__cmd_baton_t
+{
+ svn_cl__opt_state_t *opt_state;
+ svn_client_ctx_t *ctx;
+} svn_cl__cmd_baton_t;
+
+
+/* Declare all the command procedures */
+svn_opt_subcommand_t
+ svn_cl__add,
+ svn_cl__blame,
+ svn_cl__cat,
+ svn_cl__changelist,
+ svn_cl__checkout,
+ svn_cl__cleanup,
+ svn_cl__commit,
+ svn_cl__copy,
+ svn_cl__delete,
+ svn_cl__diff,
+ svn_cl__export,
+ svn_cl__help,
+ svn_cl__import,
+ svn_cl__info,
+ svn_cl__lock,
+ svn_cl__log,
+ svn_cl__list,
+ svn_cl__merge,
+ svn_cl__mergeinfo,
+ svn_cl__mkdir,
+ svn_cl__move,
+ svn_cl__patch,
+ svn_cl__propdel,
+ svn_cl__propedit,
+ svn_cl__propget,
+ svn_cl__proplist,
+ svn_cl__propset,
+ svn_cl__relocate,
+ svn_cl__revert,
+ svn_cl__resolve,
+ svn_cl__resolved,
+ svn_cl__status,
+ svn_cl__switch,
+ svn_cl__unlock,
+ svn_cl__update,
+ svn_cl__upgrade;
+
+
+/* See definition in main.c for documentation. */
+extern const svn_opt_subcommand_desc2_t svn_cl__cmd_table[];
+
+/* See definition in main.c for documentation. */
+extern const int svn_cl__global_options[];
+
+/* See definition in main.c for documentation. */
+extern const apr_getopt_option_t svn_cl__options[];
+
+
+/* A helper for the many subcommands that wish to merely warn when
+ * invoked on an unversioned, nonexistent, or otherwise innocuously
+ * errorful resource. Meant to be wrapped with SVN_ERR().
+ *
+ * If ERR is null, return SVN_NO_ERROR.
+ *
+ * Else if ERR->apr_err is one of the error codes supplied in varargs,
+ * then handle ERR as a warning (unless QUIET is true), clear ERR, and
+ * return SVN_NO_ERROR, and push the value of ERR->apr_err into the
+ * ERRORS_SEEN array, if ERRORS_SEEN is not NULL.
+ *
+ * Else return ERR.
+ *
+ * Typically, error codes like SVN_ERR_UNVERSIONED_RESOURCE,
+ * SVN_ERR_ENTRY_NOT_FOUND, etc, are supplied in varargs. Don't
+ * forget to terminate the argument list with SVN_NO_ERROR.
+ */
+svn_error_t *
+svn_cl__try(svn_error_t *err,
+ apr_array_header_t *errors_seen,
+ svn_boolean_t quiet,
+ ...);
+
+
+/* Our cancellation callback. */
+svn_error_t *
+svn_cl__check_cancel(void *baton);
+
+
+
+/* Various conflict-resolution callbacks. */
+
+typedef struct svn_cl__conflict_baton_t {
+ svn_cl__accept_t accept_which;
+ apr_hash_t *config;
+ const char *editor_cmd;
+ svn_boolean_t external_failed;
+ svn_cmdline_prompt_baton_t *pb;
+} svn_cl__conflict_baton_t;
+
+/* Create and return a conflict baton, allocated from POOL, with the values
+ ACCEPT_WHICH, CONFIG, EDITOR_CMD and PB placed in the same-named fields
+ of the baton, and its 'external_failed' field initialised to FALSE. */
+svn_cl__conflict_baton_t *
+svn_cl__conflict_baton_make(svn_cl__accept_t accept_which,
+ apr_hash_t *config,
+ const char *editor_cmd,
+ svn_cmdline_prompt_baton_t *pb,
+ apr_pool_t *pool);
+
+/* A conflict-resolution callback which prompts the user to choose
+ one of the 3 fulltexts, edit the merged file on the spot, or just
+ skip the conflict (to be resolved later).
+ Implements @c svn_wc_conflict_resolver_func_t. */
+svn_error_t *
+svn_cl__conflict_handler(svn_wc_conflict_result_t **result,
+ const svn_wc_conflict_description_t *desc,
+ void *baton,
+ apr_pool_t *pool);
+
+
+
+/*** Command-line output functions -- printing to the user. ***/
+
+/* Print out commit information found in COMMIT_INFO to the console.
+ * POOL is used for temporay allocations.
+ * COMMIT_INFO should not be NULL.
+ *
+ * This function implements svn_commit_callback2_t.
+ */
+svn_error_t *
+svn_cl__print_commit_info(const svn_commit_info_t *commit_info,
+ void *baton,
+ apr_pool_t *pool);
+
+
+/* Convert the date in DATA to a human-readable UTF-8-encoded string
+ * *HUMAN_CSTRING, or set the latter to "(invalid date)" if DATA is not
+ * a valid date. DATA should be as expected by svn_time_from_cstring().
+ *
+ * Do all allocations in POOL.
+ */
+svn_error_t *
+svn_cl__time_cstring_to_human_cstring(const char **human_cstring,
+ const char *data,
+ apr_pool_t *pool);
+
+
+/* Print STATUS for PATH to stdout for human consumption. Prints in
+ abbreviated format by default, or DETAILED format if flag is set.
+
+ When DETAILED is set, use SHOW_LAST_COMMITTED to toggle display of
+ the last-committed-revision and last-committed-author.
+
+ If SKIP_UNRECOGNIZED is TRUE, this function will not print out
+ unversioned items found in the working copy.
+
+ When DETAILED is set, and REPOS_LOCKS is set, treat missing repository locks
+ as broken WC locks.
+
+ Increment *TEXT_CONFLICTS, *PROP_CONFLICTS, or *TREE_CONFLICTS if
+ a conflict was encountered.
+ */
+svn_error_t *
+svn_cl__print_status(const char *path,
+ const svn_client_status_t *status,
+ svn_boolean_t detailed,
+ svn_boolean_t show_last_committed,
+ svn_boolean_t skip_unrecognized,
+ svn_boolean_t repos_locks,
+ unsigned int *text_conflicts,
+ unsigned int *prop_conflicts,
+ unsigned int *tree_conflicts,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+
+/* Print STATUS for PATH in XML to stdout. Use POOL for temporary
+ allocations. */
+svn_error_t *
+svn_cl__print_status_xml(const char *path,
+ const svn_client_status_t *status,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+
+/* Print to stdout a hash that maps property names (char *) to property
+ values (svn_string_t *). The names are assumed to be in UTF-8 format;
+ the values are either in UTF-8 (the special Subversion props) or
+ plain binary values.
+
+ If OUT is not NULL, then write to it rather than stdout.
+
+ If NAMES_ONLY is true, print just names, else print names and
+ values. */
+svn_error_t *
+svn_cl__print_prop_hash(svn_stream_t *out,
+ apr_hash_t *prop_hash,
+ svn_boolean_t names_only,
+ apr_pool_t *pool);
+
+/* Same as svn_cl__print_prop_hash(), only output xml to *OUTSTR. If *OUTSTR is
+ NULL, allocate it first from POOL, otherwise append to it. */
+svn_error_t *
+svn_cl__print_xml_prop_hash(svn_stringbuf_t **outstr,
+ apr_hash_t *prop_hash,
+ svn_boolean_t names_only,
+ apr_pool_t *pool);
+
+/* Output a commit xml element to *OUTSTR. If *OUTSTR is NULL, allocate it
+ first from POOL, otherwise append to it. If AUTHOR or DATE is
+ NULL, it will be omitted. */
+void
+svn_cl__print_xml_commit(svn_stringbuf_t **outstr,
+ svn_revnum_t revision,
+ const char *author,
+ const char *date,
+ apr_pool_t *pool);
+
+/* Output an XML "<lock>" element describing LOCK to *OUTSTR. If *OUTSTR is
+ NULL, allocate it first from POOL, otherwise append to it. */
+void
+svn_cl__print_xml_lock(svn_stringbuf_t **outstr,
+ const svn_lock_t *lock,
+ apr_pool_t *pool);
+
+/* Do the following things that are commonly required before accessing revision
+ properties. Ensure that REVISION is specified explicitly and is not
+ relative to a working-copy item. Ensure that exactly one target is
+ specified in TARGETS. Set *URL to the URL of the target. Return an
+ appropriate error if any of those checks or operations fail. Use CTX for
+ accessing the working copy
+ */
+svn_error_t *
+svn_cl__revprop_prepare(const svn_opt_revision_t *revision,
+ const apr_array_header_t *targets,
+ const char **URL,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+/* Search for a text editor command in standard environment variables,
+ and invoke it to edit CONTENTS (using a temporary file created in
+ directory BASE_DIR). Return the new contents in *EDITED_CONTENTS,
+ or set *EDITED_CONTENTS to NULL if no edit was performed.
+
+ If EDITOR_CMD is not NULL, it is the name of the external editor
+ command to use, overriding anything else that might determine the
+ editor.
+
+ If TMPFILE_LEFT is NULL, the temporary file will be destroyed.
+ Else, the file will be left on disk, and its path returned in
+ *TMPFILE_LEFT.
+
+ CONFIG is a hash of svn_config_t * items keyed on a configuration
+ category (SVN_CONFIG_CATEGORY_CONFIG et al), and may be NULL.
+
+ If AS_TEXT is TRUE, recode CONTENTS and convert to native eol-style before
+ editing and back again afterwards. In this case, ENCODING determines the
+ encoding used during editing. If non-NULL, use the named encoding, else
+ use the system encoding. If AS_TEXT is FALSE, don't do any translation.
+ In that case, ENCODING is ignored.
+
+ Use POOL for all allocations. Use PREFIX as the prefix for the
+ temporary file used by the editor.
+
+ If return error, *EDITED_CONTENTS is not touched. */
+svn_error_t *
+svn_cl__edit_string_externally(svn_string_t **edited_contents,
+ const char **tmpfile_left,
+ const char *editor_cmd,
+ const char *base_dir,
+ const svn_string_t *contents,
+ const char *prefix,
+ apr_hash_t *config,
+ svn_boolean_t as_text,
+ const char *encoding,
+ apr_pool_t *pool);
+
+
+/* Search for a text editor command in standard environment variables,
+ and invoke it to edit PATH. Use POOL for all allocations.
+
+ If EDITOR_CMD is not NULL, it is the name of the external editor
+ command to use, overriding anything else that might determine the
+ editor.
+
+ CONFIG is a hash of svn_config_t * items keyed on a configuration
+ category (SVN_CONFIG_CATEGORY_CONFIG et al), and may be NULL. */
+svn_error_t *
+svn_cl__edit_file_externally(const char *path,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ apr_pool_t *pool);
+
+/* Search for a merge tool command in environment variables,
+ and use it to perform the merge of the four given files.
+ WC_PATH is the path of the file that is in conflict, relative
+ to the merge target.
+ Use POOL for all allocations.
+
+ CONFIG is a hash of svn_config_t * items keyed on a configuration
+ category (SVN_CONFIG_CATEGORY_CONFIG et al), and may be NULL.
+
+ Upon success, set *REMAINS_IN_CONFLICT to indicate whether the
+ merge result contains conflict markers.
+ */
+svn_error_t *
+svn_cl__merge_file_externally(const char *base_path,
+ const char *their_path,
+ const char *my_path,
+ const char *merged_path,
+ const char *wc_path,
+ apr_hash_t *config,
+ svn_boolean_t *remains_in_conflict,
+ apr_pool_t *pool);
+
+
+
+/*** Notification functions to display results on the terminal. */
+
+/* Set *NOTIFY_FUNC_P and *NOTIFY_BATON_P to a notifier/baton for all
+ * operations, allocated in POOL.
+ *
+ * If don't want a summary line at the end of notifications, set
+ * SUPPRESS_FINAL_LINE.
+ */
+svn_error_t *
+svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
+ void **notify_baton_p,
+ svn_boolean_t suppress_final_line,
+ apr_pool_t *pool);
+
+/* Make the notifier for use with BATON print the appropriate summary
+ * line at the end of the output.
+ */
+svn_error_t *
+svn_cl__notifier_mark_checkout(void *baton);
+
+/* Make the notifier for use with BATON print the appropriate summary
+ * line at the end of the output.
+ */
+svn_error_t *
+svn_cl__notifier_mark_export(void *baton);
+
+/* Make the notifier for use with BATON print the appropriate notifications
+ * for a wc to repository copy
+ */
+svn_error_t *
+svn_cl__notifier_mark_wc_to_repos_copy(void *baton);
+
+/* Baton for use with svn_cl__check_externals_failed_notify_wrapper(). */
+struct svn_cl__check_externals_failed_notify_baton
+{
+ svn_wc_notify_func2_t wrapped_func; /* The "real" notify_func2. */
+ void *wrapped_baton; /* The "real" notify_func2 baton. */
+ svn_boolean_t had_externals_error; /* Did something fail in an external? */
+};
+
+/* Notification function wrapper (implements `svn_wc_notify_func2_t').
+ Use with an svn_cl__check_externals_failed_notify_baton BATON. */
+void
+svn_cl__check_externals_failed_notify_wrapper(void *baton,
+ const svn_wc_notify_t *n,
+ apr_pool_t *pool);
+
+/* Print conflict stats accumulated in NOTIFY_BATON.
+ * Return any error encountered during printing.
+ * Do all allocations in POOL.*/
+svn_error_t *
+svn_cl__print_conflict_stats(void *notify_baton, apr_pool_t *pool);
+
+
+/*** Log message callback stuffs. ***/
+
+/* Allocate in POOL a baton for use with svn_cl__get_log_message().
+
+ OPT_STATE is the set of command-line options given.
+
+ BASE_DIR is a directory in which to create temporary files if an
+ external editor is used to edit the log message. If BASE_DIR is
+ NULL, the current working directory (`.') will be used, and
+ therefore the user must have the proper permissions on that
+ directory. ### todo: What *should* happen in the NULL case is that
+ we ask APR to tell us where a suitable tmp directory is (like, /tmp
+ on Unix and C:\Windows\Temp on Win32 or something), and use it.
+ But APR doesn't yet have that capability.
+
+ CONFIG is a client configuration hash of svn_config_t * items keyed
+ on config categories, and may be NULL.
+
+ NOTE: While the baton itself will be allocated from POOL, the items
+ add to it are added by reference, not duped into POOL!*/
+svn_error_t *
+svn_cl__make_log_msg_baton(void **baton,
+ svn_cl__opt_state_t *opt_state,
+ const char *base_dir,
+ apr_hash_t *config,
+ apr_pool_t *pool);
+
+/* A function of type svn_client_get_commit_log3_t. */
+svn_error_t *
+svn_cl__get_log_message(const char **log_msg,
+ const char **tmp_file,
+ const apr_array_header_t *commit_items,
+ void *baton,
+ apr_pool_t *pool);
+
+/* Handle the cleanup of a log message, using the data in the
+ LOG_MSG_BATON, in the face of COMMIT_ERR. This may mean removing a
+ temporary file left by an external editor, or it may be a complete
+ no-op. COMMIT_ERR may be NULL to indicate to indicate that the
+ function should act as though no commit error occurred. Use POOL
+ for temporary allocations.
+
+ All error returns from this function are guaranteed to at least
+ include COMMIT_ERR, and perhaps additional errors attached to the
+ end of COMMIT_ERR's chain. */
+svn_error_t *
+svn_cl__cleanup_log_msg(void *log_msg_baton,
+ svn_error_t *commit_err,
+ apr_pool_t *pool);
+
+/* Add a message about --force if appropriate */
+svn_error_t *
+svn_cl__may_need_force(svn_error_t *err);
+
+/* Write the STRING to the stdio STREAM, returning an error if it fails.
+
+ This function is equal to svn_cmdline_fputs() minus the utf8->local
+ encoding translation. */
+svn_error_t *
+svn_cl__error_checked_fputs(const char *string, FILE* stream);
+
+/* If STRING is non-null, append it, wrapped in a simple XML CDATA element
+ named TAGNAME, to the string SB. Use POOL for temporary allocations. */
+void
+svn_cl__xml_tagged_cdata(svn_stringbuf_t **sb,
+ apr_pool_t *pool,
+ const char *tagname,
+ const char *string);
+
+/* Print the XML prolog and document root element start-tag to stdout, using
+ TAGNAME as the root element name. Use POOL for temporary allocations. */
+svn_error_t *
+svn_cl__xml_print_header(const char *tagname, apr_pool_t *pool);
+
+/* Print the XML document root element end-tag to stdout, using TAGNAME as the
+ root element name. Use POOL for temporary allocations. */
+svn_error_t *
+svn_cl__xml_print_footer(const char *tagname, apr_pool_t *pool);
+
+
+/* For use in XML output, return a non-localised string representation
+ * of KIND, being "none" or "dir" or "file" or, in any other case,
+ * the empty string. */
+const char *
+svn_cl__node_kind_str_xml(svn_node_kind_t kind);
+
+/* Return a (possibly localised) string representation of KIND, being "none" or
+ "dir" or "file" or, in any other case, the empty string. */
+const char *
+svn_cl__node_kind_str_human_readable(svn_node_kind_t kind);
+
+
+/** Provides an XML name for a given OPERATION.
+ * Note: POOL is currently not used.
+ */
+const char *
+svn_cl__operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool);
+
+/** Return a possibly localized human readable string for
+ * a given OPERATION.
+ * Note: POOL is currently not used.
+ */
+const char *
+svn_cl__operation_str_human_readable(svn_wc_operation_t operation,
+ apr_pool_t *pool);
+
+
+/* If PROPNAME is one of the svn: properties with a boolean value, and
+ * PROPVAL looks like an attempt to turn the property off (i.e., it's
+ * "off", "no", "false", or ""), then print a warning to the user that
+ * setting the property to this value might not do what they expect.
+ * Perform temporary allocations in POOL.
+ */
+void
+svn_cl__check_boolean_prop_val(const char *propname,
+ const char *propval,
+ apr_pool_t *pool);
+
+/* De-streamifying wrapper around svn_client_get_changelists(), which
+ is called for each target in TARGETS to populate *PATHS (a list of
+ paths assigned to one of the CHANGELISTS.
+ If all targets are to be included, may set *PATHS to TARGETS without
+ reallocating. */
+svn_error_t *
+svn_cl__changelist_paths(apr_array_header_t **paths,
+ const apr_array_header_t *changelists,
+ const apr_array_header_t *targets,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Like svn_client_args_to_target_array() but, if the only error is that some
+ * arguments are reserved file names, then print warning messages for those
+ * targets, store the rest of the targets in TARGETS_P and return success. */
+svn_error_t *
+svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets_p,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t keep_dest_origpath_on_truepath_collision,
+ apr_pool_t *pool);
+
+/* Return a string allocated in POOL that is a copy of STR but with each
+ * line prefixed with INDENT. A line is all characters up to the first
+ * CR-LF, LF-CR, CR or LF, or the end of STR if sooner. */
+const char *
+svn_cl__indent_string(const char *str,
+ const char *indent,
+ apr_pool_t *pool);
+
+
+/* Return a string showing NODE's kind, URL and revision, to the extent that
+ * that information is available in NODE. If NODE itself is NULL, this prints
+ * just a 'none' node kind.
+ * WC_REPOS_ROOT_URL should reflect the target working copy's repository
+ * root URL. If NODE is from that same URL, the printed URL is abbreviated
+ * to caret notation (^/). WC_REPOS_ROOT_URL may be NULL, in which case
+ * this function tries to print the conflicted node's complete URL. */
+const char *
+svn_cl__node_description(const svn_wc_conflict_version_t *node,
+ const char *wc_repos_root_URL,
+ apr_pool_t *pool);
+
+/* Return, in @a *true_targets_p, a copy of @a targets with peg revision
+ * specifiers snipped off the end of each element.
+ *
+ * ### JAF TODO: This function is not good because it does not allow the
+ * ### caller to detect if an invalid peg revision was specified.
+ * ###
+ * ### Callers should never have a need to silently *discard* all peg
+ * ### revisions, even if they are doing this *after* saving any peg
+ * ### revisions that might be of interest on certain arguments: I don't
+ * ### think it can ever be correct to silently ignore a peg revision that
+ * ### is specified, whether it makes semantic sense or not.
+ * ###
+ * ### Instead, callers should parse all the arguments and silently
+ * ### ignore an *empty* peg revision part (just an "@", which can be
+ * ### used to escape an earlier "@" in the argument) on any argument,
+ * ### even an argument on which a peg revision does not make sense,
+ * ### but should not silently ignore a non-empty peg when it does not
+ * ### make sense.
+ * ###
+ * ### Something like:
+ * ### For each (URL-like?) argument that doesn't accept a peg rev:
+ * ### Parse into peg-rev and true-path parts;
+ * ### If (peg rev != unspecified)
+ * ### Error("This arg doesn't accept a peg rev.").
+ * ### Use the true-path part.
+ *
+ * This function is useful for subcommands for which peg revisions
+ * do not make any sense. Such subcommands still need to allow peg
+ * revisions to be specified on the command line so that users of
+ * the command line client can consistently escape '@' characters
+ * in filenames by appending an '@' character, regardless of the
+ * subcommand being used.
+ *
+ * If a peg revision is present but cannot be parsed, an error is thrown.
+ * The user has likely forgotten to escape an '@' character in a filename.
+ *
+ * It is safe to pass the address of @a targets as @a true_targets_p.
+ *
+ * Do all allocations in @a pool. */
+svn_error_t *
+svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p,
+ const apr_array_header_t *targets,
+ apr_pool_t *pool);
+
+/* Return an error if TARGETS contains a mixture of URLs and paths; otherwise
+ * return SVN_NO_ERROR. */
+svn_error_t *
+svn_cl__assert_homogeneous_target_type(const apr_array_header_t *targets);
+
+/* Return an error if TARGETS contains a URL; otherwise return SVN_NO_ERROR. */
+svn_error_t *
+svn_cl__check_targets_are_local_paths(const apr_array_header_t *targets);
+
+/* Return an error if TARGET is a URL; otherwise return SVN_NO_ERROR. */
+svn_error_t *
+svn_cl__check_target_is_local_path(const char *target);
+
+/* Return a copy of PATH, converted to the local path style, skipping
+ * PARENT_PATH if it is non-null and is a parent of or equal to PATH.
+ *
+ * This function assumes PARENT_PATH and PATH are both absolute "dirents"
+ * or both relative "dirents". */
+const char *
+svn_cl__local_style_skip_ancestor(const char *parent_path,
+ const char *path,
+ apr_pool_t *pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_CL_H */
diff --git a/subversion/svn/cleanup-cmd.c b/subversion/svn/cleanup-cmd.c
new file mode 100644
index 0000000..64fa400
--- /dev/null
+++ b/subversion/svn/cleanup-cmd.c
@@ -0,0 +1,104 @@
+/*
+ * cleanup-cmd.c -- Subversion cleanup 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_path.h"
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__cleanup(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_array_header_t *targets;
+ apr_pool_t *subpool;
+ int i;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ subpool = svn_pool_create(pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_error_t *err;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ err = svn_client_cleanup(target, ctx, subpool);
+ if (err && err->apr_err == SVN_ERR_WC_LOCKED)
+ {
+ const char *target_abspath;
+ svn_error_t *err2 = svn_dirent_get_absolute(&target_abspath,
+ target, subpool);
+ if (err2)
+ {
+ err = svn_error_compose_create(err, err2);
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ err2 = svn_client_get_wc_root(&wcroot_abspath, target_abspath,
+ ctx, subpool, subpool);
+ if (err2)
+ err = svn_error_compose_create(err, err2);
+ else
+ err = svn_error_createf(SVN_ERR_WC_LOCKED, err,
+ _("Working copy locked; try running "
+ "'svn cleanup' on the root of the "
+ "working copy ('%s') instead."),
+ svn_dirent_local_style(wcroot_abspath,
+ subpool));
+ }
+ }
+ SVN_ERR(err);
+ }
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/client_errors.h b/subversion/svn/client_errors.h
new file mode 100644
index 0000000..5041e25
--- /dev/null
+++ b/subversion/svn/client_errors.h
@@ -0,0 +1,95 @@
+/*
+ * client_errors.h: error codes this command line client features
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+#ifndef SVN_CLIENT_ERRORS_H
+#define SVN_CLIENT_ERRORS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * This error defining system is copied from and explained in
+ * ../../include/svn_error_codes.h
+ */
+
+/* Process this file if we're building an error array, or if we have
+ not defined the enumerated constants yet. */
+#if defined(SVN_ERROR_BUILD_ARRAY) || !defined(SVN_CMDLINE_ERROR_ENUM_DEFINED)
+
+#if defined(SVN_ERROR_BUILD_ARRAY)
+
+#define SVN_ERROR_START \
+ static const err_defn error_table[] = { \
+ { SVN_ERR_CDMLINE__WARNING, "Warning" },
+#define SVN_ERRDEF(n, s) { n, s },
+#define SVN_ERROR_END { 0, NULL } };
+
+#elif !defined(SVN_CMDLINE_ERROR_ENUM_DEFINED)
+
+#define SVN_ERROR_START \
+ typedef enum svn_client_errno_t { \
+ SVN_ERR_CDMLINE__WARNING = SVN_ERR_LAST + 1,
+#define SVN_ERRDEF(n, s) n,
+#define SVN_ERROR_END SVN_ERR_CMDLINE__ERR_LAST } svn_client_errno_t;
+
+#define SVN_CMDLINE_ERROR_ENUM_DEFINED
+
+#endif
+
+/* Define custom command line client error numbers */
+
+SVN_ERROR_START
+
+ /* BEGIN Client errors */
+
+SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_WRITE,
+ "Failed writing to temporary file.")
+
+ SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_STAT,
+ "Failed getting info about temporary file.")
+
+ SVN_ERRDEF(SVN_ERR_CMDLINE__TMPFILE_OPEN,
+ "Failed opening temporary file.")
+
+ /* END Client errors */
+
+
+SVN_ERROR_END
+
+#undef SVN_ERROR_START
+#undef SVN_ERRDEF
+#undef SVN_ERROR_END
+
+#endif /* SVN_ERROR_BUILD_ARRAY || !SVN_CMDLINE_ERROR_ENUM_DEFINED */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_CLIENT_ERRORS_H */
diff --git a/subversion/svn/commit-cmd.c b/subversion/svn/commit-cmd.c
new file mode 100644
index 0000000..e227f04
--- /dev/null
+++ b/subversion/svn/commit-cmd.c
@@ -0,0 +1,184 @@
+/*
+ * commit-cmd.c -- Check changes into the repository.
+ *
+ * ====================================================================
+ * 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 <apr_general.h>
+
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_path.h"
+#include "svn_dirent_uri.h"
+#include "svn_error.h"
+#include "svn_config.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+/* Wrapper notify_func2 function and baton for warning about
+ reduced-depth commits of copied directories. */
+struct copy_warning_notify_baton
+{
+ svn_wc_notify_func2_t wrapped_func;
+ void *wrapped_baton;
+ svn_depth_t depth;
+ svn_boolean_t warned;
+};
+
+static void
+copy_warning_notify_func(void *baton,
+ const svn_wc_notify_t *notify,
+ apr_pool_t *pool)
+{
+ struct copy_warning_notify_baton *b = baton;
+
+ /* Call the wrapped notification system (if any). */
+ if (b->wrapped_func)
+ b->wrapped_func(b->wrapped_baton, notify, pool);
+
+ /* If we're being notified about a copy of a directory when our
+ commit depth is less-than-infinite, and we've not already warned
+ about this situation, then warn about it (and remember that we
+ now have.) */
+ if ((! b->warned)
+ && (b->depth < svn_depth_infinity)
+ && (notify->kind == svn_node_dir)
+ && ((notify->action == svn_wc_notify_commit_copied) ||
+ (notify->action == svn_wc_notify_commit_copied_replaced)))
+ {
+ svn_error_t *err;
+ err = svn_cmdline_printf(pool,
+ _("svn: The depth of this commit is '%s', "
+ "but copies are always performed "
+ "recursively in the repository.\n"),
+ svn_depth_to_word(b->depth));
+ /* ### FIXME: Try to return this error showhow? */
+ svn_error_clear(err);
+
+ /* We'll only warn once. */
+ b->warned = TRUE;
+ }
+}
+
+
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__commit(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ 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_array_header_t *condensed_targets;
+ const char *base_dir;
+ svn_config_t *cfg;
+ svn_boolean_t no_unlock = FALSE;
+ struct copy_warning_notify_baton cwnb;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ SVN_ERR_W(svn_cl__check_targets_are_local_paths(targets),
+ _("Commit targets must be local paths"));
+
+ /* Add "." if user passed 0 arguments. */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ /* Condense the targets (like commit does)... */
+ SVN_ERR(svn_dirent_condense_targets(&base_dir, &condensed_targets, targets,
+ TRUE, pool, pool));
+
+ if ((! condensed_targets) || (! condensed_targets->nelts))
+ {
+ const char *parent_dir, *base_name;
+
+ SVN_ERR(svn_wc_get_actual_target2(&parent_dir, &base_name, ctx->wc_ctx,
+ base_dir, pool, pool));
+ if (*base_name)
+ base_dir = apr_pstrdup(pool, parent_dir);
+ }
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_infinity;
+
+ cfg = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
+ APR_HASH_KEY_STRING);
+ if (cfg)
+ SVN_ERR(svn_config_get_bool(cfg, &no_unlock,
+ SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_NO_UNLOCK, FALSE));
+
+ /* We're creating a new log message baton because we can use our base_dir
+ to store the temp file, instead of the current working directory. The
+ client might not have write access to their working directory, but they
+ better have write access to the directory they're committing. */
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
+ opt_state, base_dir,
+ ctx->config, pool));
+
+ /* Copies are done server-side, and cheaply, which means they're
+ effectively always done with infinite depth. This is a potential
+ cause of confusion for users trying to commit copied subtrees in
+ part by restricting the commit's depth. See issues #3699 and #3752. */
+ if (opt_state->depth < svn_depth_infinity)
+ {
+ cwnb.wrapped_func = ctx->notify_func2;
+ cwnb.wrapped_baton = ctx->notify_baton2;
+ cwnb.depth = opt_state->depth;
+ cwnb.warned = FALSE;
+ ctx->notify_func2 = copy_warning_notify_func;
+ ctx->notify_baton2 = &cwnb;
+ }
+
+ /* Commit. */
+ err = svn_client_commit5(targets,
+ opt_state->depth,
+ no_unlock,
+ opt_state->keep_changelists,
+ TRUE /* commit_as_operations */,
+ opt_state->changelists,
+ opt_state->revprop_table,
+ (opt_state->quiet
+ ? NULL : svn_cl__print_commit_info),
+ NULL,
+ ctx,
+ pool);
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/conflict-callbacks.c b/subversion/svn/conflict-callbacks.c
new file mode 100644
index 0000000..a158576
--- /dev/null
+++ b/subversion/svn/conflict-callbacks.c
@@ -0,0 +1,757 @@
+/*
+ * conflict-callbacks.c: conflict resolution callbacks specific to the
+ * commandline client.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#include <apr_xlate.h> /* for APR_LOCALE_CHARSET */
+
+#define APR_WANT_STDIO
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_types.h"
+#include "svn_pools.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+
+svn_cl__conflict_baton_t *
+svn_cl__conflict_baton_make(svn_cl__accept_t accept_which,
+ apr_hash_t *config,
+ const char *editor_cmd,
+ svn_cmdline_prompt_baton_t *pb,
+ apr_pool_t *pool)
+{
+ svn_cl__conflict_baton_t *b = apr_palloc(pool, sizeof(*b));
+ b->accept_which = accept_which;
+ b->config = config;
+ b->editor_cmd = editor_cmd;
+ b->external_failed = FALSE;
+ b->pb = pb;
+ return b;
+}
+
+svn_cl__accept_t
+svn_cl__accept_from_word(const char *word)
+{
+ /* Shorthand options are consistent with svn_cl__conflict_handler(). */
+ if (strcmp(word, SVN_CL__ACCEPT_POSTPONE) == 0
+ || strcmp(word, "p") == 0 || strcmp(word, ":-P") == 0)
+ return svn_cl__accept_postpone;
+ if (strcmp(word, SVN_CL__ACCEPT_BASE) == 0)
+ /* ### shorthand? */
+ return svn_cl__accept_base;
+ if (strcmp(word, SVN_CL__ACCEPT_WORKING) == 0)
+ /* ### shorthand? */
+ return svn_cl__accept_working;
+ if (strcmp(word, SVN_CL__ACCEPT_MINE_CONFLICT) == 0
+ || strcmp(word, "mc") == 0 || strcmp(word, "X-)") == 0)
+ return svn_cl__accept_mine_conflict;
+ if (strcmp(word, SVN_CL__ACCEPT_THEIRS_CONFLICT) == 0
+ || strcmp(word, "tc") == 0 || strcmp(word, "X-(") == 0)
+ return svn_cl__accept_theirs_conflict;
+ if (strcmp(word, SVN_CL__ACCEPT_MINE_FULL) == 0
+ || strcmp(word, "mf") == 0 || strcmp(word, ":-)") == 0)
+ return svn_cl__accept_mine_full;
+ if (strcmp(word, SVN_CL__ACCEPT_THEIRS_FULL) == 0
+ || strcmp(word, "tf") == 0 || strcmp(word, ":-(") == 0)
+ return svn_cl__accept_theirs_full;
+ if (strcmp(word, SVN_CL__ACCEPT_EDIT) == 0
+ || strcmp(word, "e") == 0 || strcmp(word, ":-E") == 0)
+ return svn_cl__accept_edit;
+ if (strcmp(word, SVN_CL__ACCEPT_LAUNCH) == 0
+ || strcmp(word, "l") == 0 || strcmp(word, ":-l") == 0)
+ return svn_cl__accept_launch;
+ /* word is an invalid action. */
+ return svn_cl__accept_invalid;
+}
+
+
+/* Print on stdout a diff between the 'base' and 'merged' files, if both of
+ * those are available, else between 'their' and 'my' files, of DESC. */
+static svn_error_t *
+show_diff(const svn_wc_conflict_description_t *desc,
+ apr_pool_t *pool)
+{
+ const char *path1, *path2;
+ svn_diff_t *diff;
+ svn_stream_t *output;
+ svn_diff_file_options_t *options;
+
+ if (desc->merged_file && desc->base_file)
+ {
+ /* Show the conflict markers to the user */
+ path1 = desc->base_file;
+ path2 = desc->merged_file;
+ }
+ else
+ {
+ /* There's no base file, but we can show the
+ difference between mine and theirs. */
+ path1 = desc->their_file;
+ path2 = desc->my_file;
+ }
+
+ options = svn_diff_file_options_create(pool);
+ options->ignore_eol_style = TRUE;
+ SVN_ERR(svn_stream_for_stdout(&output, pool));
+ SVN_ERR(svn_diff_file_diff_2(&diff, path1, path2,
+ options, pool));
+ return svn_diff_file_output_unified3(output, diff,
+ path1, path2,
+ NULL, NULL,
+ APR_LOCALE_CHARSET,
+ NULL, FALSE,
+ pool);
+}
+
+
+/* Print on stdout just the conflict hunks of a diff among the 'base', 'their'
+ * and 'my' files of DESC. */
+static svn_error_t *
+show_conflicts(const svn_wc_conflict_description_t *desc,
+ apr_pool_t *pool)
+{
+ svn_diff_t *diff;
+ svn_stream_t *output;
+ svn_diff_file_options_t *options;
+
+ options = svn_diff_file_options_create(pool);
+ options->ignore_eol_style = TRUE;
+ SVN_ERR(svn_stream_for_stdout(&output, pool));
+ SVN_ERR(svn_diff_file_diff3_2(&diff,
+ desc->base_file,
+ desc->my_file,
+ desc->their_file,
+ options, pool));
+ /* ### Consider putting the markers/labels from
+ ### svn_wc__merge_internal in the conflict description. */
+ return svn_diff_file_output_merge2(output, diff,
+ desc->base_file,
+ desc->my_file,
+ desc->their_file,
+ _("||||||| ORIGINAL"),
+ _("<<<<<<< MINE (select with 'mc')"),
+ _(">>>>>>> THEIRS (select with 'tc')"),
+ "=======",
+ svn_diff_conflict_display_only_conflicts,
+ pool);
+}
+
+
+/* Run an external editor, passing it the 'merged' file in DESC, or, if the
+ * 'merged' file is null, return an error. The tool to use is determined by
+ * B->editor_cmd, B->config and environment variables; see
+ * svn_cl__edit_file_externally() for details.
+ *
+ * If the tool runs, set *PERFORMED_EDIT to true; if a tool is not
+ * configured or cannot run, do not touch *PERFORMED_EDIT, report the error
+ * on stderr, and return SVN_NO_ERROR; if any other error is encountered,
+ * return that error. */
+static svn_error_t *
+open_editor(svn_boolean_t *performed_edit,
+ const svn_wc_conflict_description_t *desc,
+ svn_cl__conflict_baton_t *b,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ if (desc->merged_file)
+ {
+ err = svn_cl__edit_file_externally(desc->merged_file, b->editor_cmd,
+ b->config, pool);
+ if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR))
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("No editor found.")));
+ svn_error_clear(err);
+ }
+ else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("Error running editor.")));
+ svn_error_clear(err);
+ }
+ else if (err)
+ return svn_error_trace(err);
+ else
+ *performed_edit = TRUE;
+ }
+ else
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+ _("Invalid option; there's no "
+ "merged version to edit.\n\n")));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Run an external merge tool, passing it the 'base', 'their', 'my' and
+ * 'merged' files in DESC. The tool to use is determined by B->config and
+ * environment variables; see svn_cl__merge_file_externally() for details.
+ *
+ * If the tool runs, set *PERFORMED_EDIT to true; if a tool is not
+ * configured or cannot run, do not touch *PERFORMED_EDIT, report the error
+ * on stderr, and return SVN_NO_ERROR; if any other error is encountered,
+ * return that error. */
+static svn_error_t *
+launch_resolver(svn_boolean_t *performed_edit,
+ const svn_wc_conflict_description_t *desc,
+ svn_cl__conflict_baton_t *b,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ err = svn_cl__merge_file_externally(desc->base_file, desc->their_file,
+ desc->my_file, desc->merged_file,
+ desc->path, b->config, NULL, pool);
+ if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("No merge tool found.\n")));
+ svn_error_clear(err);
+ }
+ else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("Error running merge tool.")));
+ svn_error_clear(err);
+ }
+ else if (err)
+ return svn_error_trace(err);
+ else if (performed_edit)
+ *performed_edit = TRUE;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Implement svn_wc_conflict_resolver_func_t; resolves based on
+ --accept option if given, else by prompting. */
+svn_error_t *
+svn_cl__conflict_handler(svn_wc_conflict_result_t **result,
+ const svn_wc_conflict_description_t *desc,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__conflict_baton_t *b = baton;
+ svn_error_t *err;
+ apr_pool_t *subpool;
+
+ /* Start out assuming we're going to postpone the conflict. */
+ *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
+ NULL, pool);
+
+ switch (b->accept_which)
+ {
+ case svn_cl__accept_invalid:
+ case svn_cl__accept_unspecified:
+ /* No (or no valid) --accept option, fall through to prompting. */
+ break;
+ case svn_cl__accept_postpone:
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_base:
+ (*result)->choice = svn_wc_conflict_choose_base;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_working:
+ (*result)->choice = svn_wc_conflict_choose_merged;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_mine_conflict:
+ (*result)->choice = svn_wc_conflict_choose_mine_conflict;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_theirs_conflict:
+ (*result)->choice = svn_wc_conflict_choose_theirs_conflict;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_mine_full:
+ (*result)->choice = svn_wc_conflict_choose_mine_full;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_theirs_full:
+ (*result)->choice = svn_wc_conflict_choose_theirs_full;
+ return SVN_NO_ERROR;
+ case svn_cl__accept_edit:
+ if (desc->merged_file)
+ {
+ if (b->external_failed)
+ {
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ return SVN_NO_ERROR;
+ }
+
+ err = svn_cl__edit_file_externally(desc->merged_file,
+ b->editor_cmd, b->config, pool);
+ if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR))
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("No editor found;"
+ " leaving all conflicts.")));
+ svn_error_clear(err);
+ b->external_failed = TRUE;
+ }
+ else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("Error running editor;"
+ " leaving all conflicts.")));
+ svn_error_clear(err);
+ b->external_failed = TRUE;
+ }
+ else if (err)
+ return svn_error_trace(err);
+ (*result)->choice = svn_wc_conflict_choose_merged;
+ return SVN_NO_ERROR;
+ }
+ /* else, fall through to prompting. */
+ break;
+ case svn_cl__accept_launch:
+ if (desc->base_file && desc->their_file
+ && desc->my_file && desc->merged_file)
+ {
+ svn_boolean_t remains_in_conflict;
+
+ if (b->external_failed)
+ {
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ return SVN_NO_ERROR;
+ }
+
+ err = svn_cl__merge_file_externally(desc->base_file,
+ desc->their_file,
+ desc->my_file,
+ desc->merged_file,
+ desc->path,
+ b->config,
+ &remains_in_conflict,
+ pool);
+ if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("No merge tool found;"
+ " leaving all conflicts.")));
+ b->external_failed = TRUE;
+ return svn_error_trace(err);
+ }
+ else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n",
+ err->message ? err->message :
+ _("Error running merge tool;"
+ " leaving all conflicts.")));
+ b->external_failed = TRUE;
+ return svn_error_trace(err);
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ if (remains_in_conflict)
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ else
+ (*result)->choice = svn_wc_conflict_choose_merged;
+ return SVN_NO_ERROR;
+ }
+ /* else, fall through to prompting. */
+ break;
+ }
+
+ /* We're in interactive mode and either the user gave no --accept
+ option or the option did not apply; let's prompt. */
+ subpool = svn_pool_create(pool);
+
+ /* Handle the most common cases, which is either:
+
+ Conflicting edits on a file's text, or
+ Conflicting edits on a property.
+ */
+ if (((desc->node_kind == svn_node_file)
+ && (desc->action == svn_wc_conflict_action_edit)
+ && (desc->reason == svn_wc_conflict_reason_edited))
+ || (desc->kind == svn_wc_conflict_kind_property))
+ {
+ const char *answer;
+ char *prompt;
+ svn_boolean_t diff_allowed = FALSE;
+ /* Have they done something that might have affected the merged
+ file (so that we need to save a .edited copy)? */
+ svn_boolean_t performed_edit = FALSE;
+ /* Have they done *something* (edit, look at diff, etc) to
+ give them a rational basis for choosing (r)esolved? */
+ svn_boolean_t knows_something = FALSE;
+
+ if (desc->kind == svn_wc_conflict_kind_text)
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Conflict discovered in '%s'.\n"),
+ desc->path));
+ else if (desc->kind == svn_wc_conflict_kind_property)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Conflict for property '%s' discovered"
+ " on '%s'.\n"),
+ desc->property_name, desc->path));
+
+ if ((!desc->my_file && desc->their_file)
+ || (desc->my_file && !desc->their_file))
+ {
+ /* One agent wants to change the property, one wants to
+ delete it. This is not something we can diff, so we
+ just tell the user. */
+ svn_stringbuf_t *myval = NULL, *theirval = NULL;
+
+ if (desc->my_file)
+ {
+ SVN_ERR(svn_stringbuf_from_file2(&myval, desc->my_file,
+ subpool));
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("They want to delete the property, "
+ "you want to change the value to '%s'.\n"),
+ myval->data));
+ }
+ else
+ {
+ SVN_ERR(svn_stringbuf_from_file2(&theirval, desc->their_file,
+ subpool));
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("They want to change the property value to '%s', "
+ "you want to delete the property.\n"),
+ theirval->data));
+ }
+ }
+ }
+ else
+ /* We don't recognize any other sort of conflict yet */
+ return SVN_NO_ERROR;
+
+ /* Diffing can happen between base and merged, to show conflict
+ markers to the user (this is the typical 3-way merge
+ scenario), or if no base is available, we can show a diff
+ between mine and theirs. */
+ if ((desc->merged_file && desc->base_file)
+ || (!desc->base_file && desc->my_file && desc->their_file))
+ diff_allowed = TRUE;
+
+ while (TRUE)
+ {
+ svn_pool_clear(subpool);
+
+ prompt = apr_pstrdup(subpool, _("Select: (p) postpone"));
+
+ if (diff_allowed)
+ {
+ prompt = apr_pstrcat(subpool, prompt,
+ _(", (df) diff-full, (e) edit"),
+ (char *)NULL);
+
+ if (knows_something)
+ prompt = apr_pstrcat(subpool, prompt, _(", (r) resolved"),
+ (char *)NULL);
+
+ if (! desc->is_binary &&
+ desc->kind != svn_wc_conflict_kind_property)
+ prompt = apr_pstrcat(subpool, prompt,
+ _(",\n (mc) mine-conflict, "
+ "(tc) theirs-conflict"),
+ (char *)NULL);
+ }
+ else
+ {
+ if (knows_something)
+ prompt = apr_pstrcat(subpool, prompt, _(", (r) resolved"),
+ (char *)NULL);
+ prompt = apr_pstrcat(subpool, prompt,
+ _(",\n "
+ "(mf) mine-full, (tf) theirs-full"),
+ (char *)NULL);
+ }
+
+ prompt = apr_pstrcat(subpool, prompt, ",\n ", (char *)NULL);
+ prompt = apr_pstrcat(subpool, prompt,
+ _("(s) show all options: "),
+ (char *)NULL);
+
+ SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, b->pb, subpool));
+
+ if (strcmp(answer, "s") == 0)
+ {
+ /* These are used in svn_cl__accept_from_word(). */
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("\n"
+ " (e) edit - change merged file in an editor\n"
+ " (df) diff-full - show all changes made to merged "
+ "file\n"
+ " (r) resolved - accept merged version of file\n"
+ "\n"
+ " (dc) display-conflict - show all conflicts "
+ "(ignoring merged version)\n"
+ " (mc) mine-conflict - accept my version for all "
+ "conflicts (same)\n"
+ " (tc) theirs-conflict - accept their version for all "
+ "conflicts (same)\n"
+ "\n"
+ " (mf) mine-full - accept my version of entire file "
+ "(even non-conflicts)\n"
+ " (tf) theirs-full - accept their version of entire "
+ "file (same)\n"
+ "\n"
+ " (p) postpone - mark the conflict to be "
+ "resolved later\n"
+ " (l) launch - launch external tool to "
+ "resolve conflict\n"
+ " (s) show all - show this list\n\n")));
+ }
+ else if (strcmp(answer, "p") == 0 || strcmp(answer, ":-P") == 0)
+ {
+ /* Do nothing, let file be marked conflicted. */
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ break;
+ }
+ else if (strcmp(answer, "mc") == 0 || strcmp(answer, "X-)") == 0)
+ {
+ if (desc->is_binary)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot choose "
+ "based on conflicts in a "
+ "binary file.\n\n")));
+ continue;
+ }
+ else if (desc->kind == svn_wc_conflict_kind_property)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot choose "
+ "based on conflicts for "
+ "properties.\n\n")));
+ continue;
+ }
+
+ (*result)->choice = svn_wc_conflict_choose_mine_conflict;
+ if (performed_edit)
+ (*result)->save_merged = TRUE;
+ break;
+ }
+ else if (strcmp(answer, "tc") == 0 || strcmp(answer, "X-(") == 0)
+ {
+ if (desc->is_binary)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot choose "
+ "based on conflicts in a "
+ "binary file.\n\n")));
+ continue;
+ }
+ else if (desc->kind == svn_wc_conflict_kind_property)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot choose "
+ "based on conflicts for "
+ "properties.\n\n")));
+ continue;
+ }
+ (*result)->choice = svn_wc_conflict_choose_theirs_conflict;
+ if (performed_edit)
+ (*result)->save_merged = TRUE;
+ break;
+ }
+ else if (strcmp(answer, "mf") == 0 || strcmp(answer, ":-)") == 0)
+ {
+ (*result)->choice = svn_wc_conflict_choose_mine_full;
+ if (performed_edit)
+ (*result)->save_merged = TRUE;
+ break;
+ }
+ else if (strcmp(answer, "tf") == 0 || strcmp(answer, ":-(") == 0)
+ {
+ (*result)->choice = svn_wc_conflict_choose_theirs_full;
+ if (performed_edit)
+ (*result)->save_merged = TRUE;
+ break;
+ }
+ else if (strcmp(answer, "dc") == 0)
+ {
+ if (desc->is_binary)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot "
+ "display conflicts for a "
+ "binary file.\n\n")));
+ continue;
+ }
+ else if (desc->kind == svn_wc_conflict_kind_property)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot "
+ "display conflicts for "
+ "properties.\n\n")));
+ continue;
+ }
+ else if (! (desc->my_file && desc->base_file && desc->their_file))
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; original "
+ "files not available.\n\n")));
+ continue;
+ }
+ SVN_ERR(show_conflicts(desc, subpool));
+ knows_something = TRUE;
+ }
+ else if (strcmp(answer, "df") == 0)
+ {
+ if (! diff_allowed)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; there's no "
+ "merged version to diff.\n\n")));
+ continue;
+ }
+
+ SVN_ERR(show_diff(desc, subpool));
+ knows_something = TRUE;
+ }
+ else if (strcmp(answer, "e") == 0 || strcmp(answer, ":-E") == 0)
+ {
+ SVN_ERR(open_editor(&performed_edit, desc, b, subpool));
+ if (performed_edit)
+ knows_something = TRUE;
+ }
+ else if (strcmp(answer, "l") == 0 || strcmp(answer, ":-l") == 0)
+ {
+ if (desc->kind == svn_wc_conflict_kind_property)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option; cannot "
+ "resolve property conflicts "
+ "with an external merge tool."
+ "\n\n")));
+ continue;
+ }
+ if (desc->base_file && desc->their_file && desc->my_file
+ && desc->merged_file)
+ {
+ SVN_ERR(launch_resolver(&performed_edit, desc, b, subpool));
+ if (performed_edit)
+ knows_something = TRUE;
+ }
+ else
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option.\n\n")));
+ }
+ else if (strcmp(answer, "r") == 0)
+ {
+ /* We only allow the user accept the merged version of
+ the file if they've edited it, or at least looked at
+ the diff. */
+ if (knows_something)
+ {
+ (*result)->choice = svn_wc_conflict_choose_merged;
+ break;
+ }
+ else
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _("Invalid option.\n\n")));
+ }
+ }
+ }
+ /*
+ Dealing with obstruction of additions can be tricky. The
+ obstructing item could be unversioned, versioned, or even
+ schedule-add. Here's a matrix of how the caller should behave,
+ based on results we return.
+
+ Unversioned Versioned Schedule-Add
+
+ choose_mine skip addition, skip addition skip addition
+ add existing item
+
+ choose_theirs destroy file, schedule-delete, revert add,
+ add new item. add new item. rm file,
+ add new item
+
+ postpone [ bail out ]
+
+ */
+ else if ((desc->action == svn_wc_conflict_action_add)
+ && (desc->reason == svn_wc_conflict_reason_obstructed))
+ {
+ const char *answer;
+ const char *prompt;
+
+ SVN_ERR(svn_cmdline_fprintf(
+ stderr, subpool,
+ _("Conflict discovered when trying to add '%s'.\n"
+ "An object of the same name already exists.\n"),
+ desc->path));
+ prompt = _("Select: (p) postpone, (mf) mine-full, "
+ "(tf) theirs-full, (h) help:");
+
+ while (1)
+ {
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, b->pb, subpool));
+
+ if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
+ {
+ SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
+ _(" (p) postpone - resolve the conflict later\n"
+ " (mf) mine-full - accept pre-existing item "
+ "(ignore upstream addition)\n"
+ " (tf) theirs-full - accept incoming item "
+ "(overwrite pre-existing item)\n"
+ " (h) help - show this help\n\n")));
+ }
+ if (strcmp(answer, "p") == 0 || strcmp(answer, ":-P") == 0)
+ {
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ break;
+ }
+ if (strcmp(answer, "mf") == 0 || strcmp(answer, ":-)") == 0)
+ {
+ (*result)->choice = svn_wc_conflict_choose_mine_full;
+ break;
+ }
+ if (strcmp(answer, "tf") == 0 || strcmp(answer, ":-(") == 0)
+ {
+ (*result)->choice = svn_wc_conflict_choose_theirs_full;
+ break;
+ }
+ }
+ }
+
+ else /* other types of conflicts -- do nothing about them. */
+ {
+ (*result)->choice = svn_wc_conflict_choose_postpone;
+ }
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/copy-cmd.c b/subversion/svn/copy-cmd.c
new file mode 100644
index 0000000..99b8703
--- /dev/null
+++ b/subversion/svn/copy-cmd.c
@@ -0,0 +1,140 @@
+/*
+ * copy-cmd.c -- Subversion copy 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_path.h"
+#include "svn_error.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__copy(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_array_header_t *targets, *sources;
+ const char *src_path, *dst_path;
+ svn_boolean_t srcs_are_urls, dst_is_url;
+ svn_error_t *err;
+ int i;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ if (targets->nelts < 2)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ /* Get the src list and associated peg revs */
+ sources = apr_array_make(pool, targets->nelts - 1,
+ sizeof(svn_client_copy_source_t *));
+ for (i = 0; i < (targets->nelts - 1); i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_client_copy_source_t *source = apr_palloc(pool, sizeof(*source));
+ const char *src;
+ svn_opt_revision_t *peg_revision = apr_palloc(pool,
+ sizeof(*peg_revision));
+
+ SVN_ERR(svn_opt_parse_path(peg_revision, &src, target, pool));
+ source->path = src;
+ source->revision = &(opt_state->start_revision);
+ source->peg_revision = peg_revision;
+
+ APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = source;
+ }
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ /* Figure out which type of notification to use.
+ (There is no need to check that the src paths are homogeneous;
+ svn_client_copy6() through its subroutine try_copy() will return an
+ error if they are not.) */
+ src_path = APR_ARRAY_IDX(targets, 0, const char *);
+ srcs_are_urls = svn_path_is_url(src_path);
+ dst_path = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
+ apr_array_pop(targets);
+ dst_is_url = svn_path_is_url(dst_path);
+
+ if ((! srcs_are_urls) && (! dst_is_url))
+ {
+ /* WC->WC */
+ }
+ else if ((! srcs_are_urls) && (dst_is_url))
+ {
+ /* WC->URL : Use notification. */
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__notifier_mark_wc_to_repos_copy(ctx->notify_baton2));
+ }
+ else if ((srcs_are_urls) && (! dst_is_url))
+ {
+ /* URL->WC : Use checkout-style notification. */
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__notifier_mark_checkout(ctx->notify_baton2));
+ }
+ else
+ {
+ /* URL -> URL, meaning that no notification is needed. */
+ ctx->notify_func2 = NULL;
+ }
+
+ if (! dst_is_url)
+ {
+ ctx->log_msg_func3 = NULL;
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
+ NULL, ctx->config, pool));
+
+ err = svn_client_copy6(sources, dst_path, TRUE,
+ opt_state->parents, opt_state->ignore_externals,
+ opt_state->revprop_table,
+ (opt_state->quiet ? NULL : svn_cl__print_commit_info),
+ NULL,
+ ctx, pool);
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
+ else if (err)
+ return svn_error_trace(err);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/delete-cmd.c b/subversion/svn/delete-cmd.c
new file mode 100644
index 0000000..e73813b
--- /dev/null
+++ b/subversion/svn/delete-cmd.c
@@ -0,0 +1,95 @@
+/*
+ * delete-cmd.c -- Delete/undelete commands
+ *
+ * ====================================================================
+ * 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_error.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__delete(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_array_header_t *targets;
+ svn_error_t *err;
+ svn_boolean_t is_url;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__assert_homogeneous_target_type(targets));
+ is_url = svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *));
+
+ if (! is_url)
+ {
+ ctx->log_msg_func3 = NULL;
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+ }
+ else
+ {
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
+ NULL, ctx->config, pool));
+ }
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ err = svn_client_delete4(targets, opt_state->force, opt_state->keep_local,
+ opt_state->revprop_table,
+ (opt_state->quiet
+ ? NULL : svn_cl__print_commit_info),
+ NULL, ctx, pool);
+ if (err)
+ err = svn_cl__may_need_force(err);
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
+ else if (err)
+ return svn_error_trace(err);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/diff-cmd.c b/subversion/svn/diff-cmd.c
new file mode 100644
index 0000000..3c62523
--- /dev/null
+++ b/subversion/svn/diff-cmd.c
@@ -0,0 +1,439 @@
+/*
+ * diff-cmd.c -- Display context diff of a file
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_types.h"
+#include "svn_cmdline.h"
+#include "svn_xml.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Convert KIND into a single character for display to the user. */
+static char
+kind_to_char(svn_client_diff_summarize_kind_t kind)
+{
+ switch (kind)
+ {
+ case svn_client_diff_summarize_kind_modified:
+ return 'M';
+
+ case svn_client_diff_summarize_kind_added:
+ return 'A';
+
+ case svn_client_diff_summarize_kind_deleted:
+ return 'D';
+
+ default:
+ return ' ';
+ }
+}
+
+/* Convert KIND into a word describing the kind to the user. */
+static const char *
+kind_to_word(svn_client_diff_summarize_kind_t kind)
+{
+ switch (kind)
+ {
+ case svn_client_diff_summarize_kind_modified: return "modified";
+ case svn_client_diff_summarize_kind_added: return "added";
+ case svn_client_diff_summarize_kind_deleted: return "deleted";
+ default: return "none";
+ }
+}
+
+/* Print summary information about a given change as XML, implements the
+ * svn_client_diff_summarize_func_t interface. The @a baton is a 'char *'
+ * representing the either the path to the working copy root or the url
+ * the path the working copy root corresponds to. */
+static svn_error_t *
+summarize_xml(const svn_client_diff_summarize_t *summary,
+ void *baton,
+ apr_pool_t *pool)
+{
+ /* Full path to the object being diffed. This is created by taking the
+ * baton, and appending the target's relative path. */
+ const char *path = *(const char **)baton;
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+
+ /* Tack on the target path, so we can differentiate between different parts
+ * of the output when we're given multiple targets. */
+ if (svn_path_is_url(path))
+ {
+ path = svn_path_url_add_component2(path, summary->path, pool);
+ }
+ else
+ {
+ path = svn_dirent_join(path, summary->path, pool);
+
+ /* Convert non-urls to local style, so that things like ""
+ show up as "." */
+ path = svn_dirent_local_style(path, pool);
+ }
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
+ "kind", svn_cl__node_kind_str_xml(summary->node_kind),
+ "item", kind_to_word(summary->summarize_kind),
+ "props", summary->prop_changed ? "modified" : "none",
+ NULL);
+
+ svn_xml_escape_cdata_cstring(&sb, path, pool);
+ svn_xml_make_close_tag(&sb, pool, "path");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+/* Print summary information about a given change, implements the
+ * svn_client_diff_summarize_func_t interface. */
+static svn_error_t *
+summarize_regular(const svn_client_diff_summarize_t *summary,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const char *path = *(const char **)baton;
+
+ /* Tack on the target path, so we can differentiate between different parts
+ * of the output when we're given multiple targets. */
+ if (svn_path_is_url(path))
+ {
+ path = svn_path_url_add_component2(path, summary->path, pool);
+ }
+ else
+ {
+ path = svn_dirent_join(path, summary->path, pool);
+
+ /* Convert non-urls to local style, so that things like ""
+ show up as "." */
+ path = svn_dirent_local_style(path, pool);
+ }
+
+ /* Note: This output format tries to look like the output of 'svn status',
+ * thus the blank spaces where information that is not relevant to
+ * a diff summary would go. */
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ "%c%c %s\n",
+ kind_to_char(summary->summarize_kind),
+ summary->prop_changed ? 'M' : ' ',
+ path));
+
+ return svn_cmdline_fflush(stdout);
+}
+
+/* An svn_opt_subcommand_t to handle the 'diff' command.
+ This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__diff(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_array_header_t *options;
+ apr_array_header_t *targets;
+ apr_file_t *outfile, *errfile;
+ apr_status_t status;
+ const char *old_target, *new_target;
+ apr_pool_t *iterpool;
+ svn_boolean_t pegged_diff = FALSE;
+ int i;
+ const svn_client_diff_summarize_func_t summarize_func =
+ (opt_state->xml ? summarize_xml : summarize_regular);
+
+ if (opt_state->extensions)
+ options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
+ else
+ options = NULL;
+
+ /* Get an apr_file_t representing stdout and stderr, which is where
+ we'll have the external 'diff' program print to. */
+ if ((status = apr_file_open_stdout(&outfile, pool)))
+ return svn_error_wrap_apr(status, _("Can't open stdout"));
+ if ((status = apr_file_open_stderr(&errfile, pool)))
+ return svn_error_wrap_apr(status, _("Can't open stderr"));
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb;
+
+ /* Check that the --summarize is passed as well. */
+ if (!opt_state->summarize)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'--xml' option only valid with "
+ "'--summarize' option"));
+
+ SVN_ERR(svn_cl__xml_print_header("diff", pool));
+
+ sb = svn_stringbuf_create("", pool);
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths", NULL);
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ }
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (! opt_state->old_target && ! opt_state->new_target
+ && (targets->nelts == 2)
+ && svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *))
+ && svn_path_is_url(APR_ARRAY_IDX(targets, 1, const char *))
+ && opt_state->start_revision.kind == svn_opt_revision_unspecified
+ && opt_state->end_revision.kind == svn_opt_revision_unspecified)
+ {
+ /* The 'svn diff OLD_URL[@OLDREV] NEW_URL[@NEWREV]' case matches. */
+
+ SVN_ERR(svn_opt_parse_path(&opt_state->start_revision, &old_target,
+ APR_ARRAY_IDX(targets, 0, const char *),
+ pool));
+ SVN_ERR(svn_opt_parse_path(&opt_state->end_revision, &new_target,
+ APR_ARRAY_IDX(targets, 1, const char *),
+ pool));
+ targets->nelts = 0;
+
+ if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
+ opt_state->start_revision.kind = svn_opt_revision_head;
+ if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+ opt_state->end_revision.kind = svn_opt_revision_head;
+ }
+ else if (opt_state->old_target)
+ {
+ apr_array_header_t *tmp, *tmp2;
+ svn_opt_revision_t old_rev, new_rev;
+
+ /* The 'svn diff --old=OLD[@OLDREV] [--new=NEW[@NEWREV]]
+ [PATH...]' case matches. */
+
+ tmp = apr_array_make(pool, 2, sizeof(const char *));
+ APR_ARRAY_PUSH(tmp, const char *) = (opt_state->old_target);
+ APR_ARRAY_PUSH(tmp, const char *) = (opt_state->new_target
+ ? opt_state->new_target
+ : APR_ARRAY_IDX(tmp, 0,
+ const char *));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&tmp2, os, tmp,
+ ctx, FALSE, pool));
+
+ /* Check if either or both targets were skipped (e.g. because they
+ * were .svn directories). */
+ if (tmp2->nelts < 2)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
+
+ SVN_ERR(svn_opt_parse_path(&old_rev, &old_target,
+ APR_ARRAY_IDX(tmp2, 0, const char *),
+ pool));
+ if (old_rev.kind != svn_opt_revision_unspecified)
+ opt_state->start_revision = old_rev;
+ SVN_ERR(svn_opt_parse_path(&new_rev, &new_target,
+ APR_ARRAY_IDX(tmp2, 1, const char *),
+ pool));
+ if (new_rev.kind != svn_opt_revision_unspecified)
+ opt_state->end_revision = new_rev;
+
+ if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
+ opt_state->start_revision.kind = svn_path_is_url(old_target)
+ ? svn_opt_revision_head : svn_opt_revision_base;
+
+ if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+ opt_state->end_revision.kind = svn_path_is_url(new_target)
+ ? svn_opt_revision_head : svn_opt_revision_working;
+ }
+ else if (opt_state->new_target)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'--new' option only valid with "
+ "'--old' option"));
+ }
+ else
+ {
+ svn_boolean_t working_copy_present;
+
+ /* The 'svn diff [-r N[:M]] [TARGET[@REV]...]' case matches. */
+
+ /* Here each target is a pegged object. Find out the starting
+ and ending paths for each target. */
+
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ old_target = "";
+ new_target = "";
+
+ SVN_ERR(svn_cl__assert_homogeneous_target_type(targets));
+
+ working_copy_present = ! svn_path_is_url(APR_ARRAY_IDX(targets, 0,
+ const char *));
+
+ if (opt_state->start_revision.kind == svn_opt_revision_unspecified
+ && working_copy_present)
+ opt_state->start_revision.kind = svn_opt_revision_base;
+ if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
+ opt_state->end_revision.kind = working_copy_present
+ ? svn_opt_revision_working : svn_opt_revision_head;
+
+ /* Determine if we need to do pegged diffs. */
+ if ((opt_state->start_revision.kind != svn_opt_revision_base
+ && opt_state->start_revision.kind != svn_opt_revision_working)
+ || (opt_state->end_revision.kind != svn_opt_revision_base
+ && opt_state->end_revision.kind != svn_opt_revision_working))
+ pegged_diff = TRUE;
+
+ }
+
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ iterpool = svn_pool_create(pool);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *path = APR_ARRAY_IDX(targets, i, const char *);
+ const char *target1, *target2;
+
+ svn_pool_clear(iterpool);
+ if (! pegged_diff)
+ {
+ /* We can't be tacking URLs onto base paths! */
+ if (svn_path_is_url(path))
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Path '%s' not relative to base URLs"),
+ path);
+
+ if (svn_path_is_url(old_target))
+ target1 = svn_path_url_add_component2(
+ old_target,
+ svn_relpath_canonicalize(path, iterpool),
+ iterpool);
+ else
+ target1 = svn_dirent_join(old_target, path, iterpool);
+
+ if (svn_path_is_url(new_target))
+ target2 = svn_path_url_add_component2(
+ new_target,
+ svn_relpath_canonicalize(path, iterpool),
+ iterpool);
+ else
+ target2 = svn_dirent_join(new_target, path, iterpool);
+
+ if (opt_state->summarize)
+ SVN_ERR(svn_client_diff_summarize2
+ (target1,
+ &opt_state->start_revision,
+ target2,
+ &opt_state->end_revision,
+ opt_state->depth,
+ ! opt_state->notice_ancestry,
+ opt_state->changelists,
+ summarize_func, &target1,
+ ctx, iterpool));
+ else
+ SVN_ERR(svn_client_diff5
+ (options,
+ target1,
+ &(opt_state->start_revision),
+ target2,
+ &(opt_state->end_revision),
+ NULL,
+ opt_state->depth,
+ ! opt_state->notice_ancestry,
+ opt_state->no_diff_deleted,
+ opt_state->show_copies_as_adds,
+ opt_state->force,
+ opt_state->use_git_diff_format,
+ svn_cmdline_output_encoding(pool),
+ outfile,
+ errfile,
+ opt_state->changelists,
+ ctx, iterpool));
+ }
+ else
+ {
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ /* First check for a peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, path,
+ iterpool));
+
+ /* Set the default peg revision if one was not specified. */
+ if (peg_revision.kind == svn_opt_revision_unspecified)
+ peg_revision.kind = svn_path_is_url(path)
+ ? svn_opt_revision_head : svn_opt_revision_working;
+
+ if (opt_state->summarize)
+ SVN_ERR(svn_client_diff_summarize_peg2
+ (truepath,
+ &peg_revision,
+ &opt_state->start_revision,
+ &opt_state->end_revision,
+ opt_state->depth,
+ ! opt_state->notice_ancestry,
+ opt_state->changelists,
+ summarize_func, &truepath,
+ ctx, iterpool));
+ else
+ SVN_ERR(svn_client_diff_peg5
+ (options,
+ truepath,
+ &peg_revision,
+ &opt_state->start_revision,
+ &opt_state->end_revision,
+ NULL,
+ opt_state->depth,
+ ! opt_state->notice_ancestry,
+ opt_state->no_diff_deleted,
+ opt_state->show_copies_as_adds,
+ opt_state->force,
+ opt_state->use_git_diff_format,
+ svn_cmdline_output_encoding(pool),
+ outfile,
+ errfile,
+ opt_state->changelists,
+ ctx, iterpool));
+ }
+ }
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ svn_xml_make_close_tag(&sb, pool, "paths");
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ SVN_ERR(svn_cl__xml_print_footer("diff", pool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/export-cmd.c b/subversion/svn/export-cmd.c
new file mode 100644
index 0000000..c4c4aa4
--- /dev/null
+++ b/subversion/svn/export-cmd.c
@@ -0,0 +1,122 @@
+/*
+ * export-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_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_opt_private.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__export(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;
+ const char *from, *to;
+ apr_array_header_t *targets;
+ svn_error_t *err;
+ svn_opt_revision_t peg_revision;
+ const char *truefrom;
+ struct svn_cl__check_externals_failed_notify_baton nwb;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We want exactly 1 or 2 targets for this subcommand. */
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ /* The first target is the `from' path. */
+ from = APR_ARRAY_IDX(targets, 0, const char *);
+
+ /* Get the peg revision if present. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool));
+
+ /* If only one target was given, split off the basename to use as
+ the `to' path. Else, a `to' path was supplied. */
+ if (targets->nelts == 1)
+ {
+ if (svn_path_is_url(truefrom))
+ to = svn_uri_basename(truefrom, pool);
+ else
+ to = svn_dirent_basename(truefrom, pool);
+ }
+ else
+ {
+ to = APR_ARRAY_IDX(targets, 1, const char *);
+
+ if (strcmp("", to) != 0)
+ /* svn_cl__eat_peg_revisions() but only on one target */
+ SVN_ERR(svn_opt__split_arg_at_peg_revision(&to, NULL, to, pool));
+ }
+
+ SVN_ERR(svn_cl__check_target_is_local_path(to));
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__notifier_mark_export(ctx->notify_baton2));
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_infinity;
+
+ nwb.wrapped_func = ctx->notify_func2;
+ nwb.wrapped_baton = ctx->notify_baton2;
+ nwb.had_externals_error = FALSE;
+ ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
+ ctx->notify_baton2 = &nwb;
+
+ /* Do the export. */
+ err = svn_client_export5(NULL, truefrom, to, &peg_revision,
+ &(opt_state->start_revision),
+ opt_state->force, opt_state->ignore_externals,
+ opt_state->ignore_keywords, opt_state->depth,
+ opt_state->native_eol, ctx, pool);
+ if (err && err->apr_err == SVN_ERR_WC_OBSTRUCTED_UPDATE && !opt_state->force)
+ SVN_ERR_W(err,
+ _("Destination directory exists; please remove "
+ "the directory or use --force to overwrite"));
+
+ if (nwb.had_externals_error)
+ return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL,
+ _("Failure occurred processing one or more "
+ "externals definitions"));
+
+ return svn_error_trace(err);
+}
diff --git a/subversion/svn/help-cmd.c b/subversion/svn/help-cmd.c
new file mode 100644
index 0000000..cd0373a
--- /dev/null
+++ b/subversion/svn/help-cmd.c
@@ -0,0 +1,93 @@
+/*
+ * help-cmd.c -- Provide help
+ *
+ * ====================================================================
+ * 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_string.h"
+#include "svn_error.h"
+#include "svn_version.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__help(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state;
+
+ /* xgettext: the %s is for SVN_VER_NUMBER. */
+ char help_header_template[] =
+ N_("usage: svn <subcommand> [options] [args]\n"
+ "Subversion command-line client, version %s.\n"
+ "Type 'svn help <subcommand>' for help on a specific subcommand.\n"
+ "Type 'svn --version' to see the program version and RA modules\n"
+ " or 'svn --version --quiet' to see just the version number.\n"
+ "\n"
+ "Most subcommands take file and/or directory arguments, recursing\n"
+ "on the directories. If no arguments are supplied to such a\n"
+ "command, it recurses on the current directory (inclusive) by default.\n"
+ "\n"
+ "Available subcommands:\n");
+
+ char help_footer[] =
+ N_("Subversion is a tool for version control.\n"
+ "For additional information, see http://subversion.apache.org/\n");
+
+ char *help_header =
+ apr_psprintf(pool, _(help_header_template), SVN_VER_NUMBER);
+
+ const char *ra_desc_start
+ = _("The following repository access (RA) modules are available:\n\n");
+
+ svn_stringbuf_t *version_footer;
+
+ if (baton)
+ opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ else
+ opt_state = NULL;
+
+ version_footer = svn_stringbuf_create(ra_desc_start, pool);
+ SVN_ERR(svn_ra_print_modules(version_footer, pool));
+
+ return svn_opt_print_help3(os,
+ "svn", /* ### erm, derive somehow? */
+ opt_state ? opt_state->version : FALSE,
+ opt_state ? opt_state->quiet : FALSE,
+ version_footer->data,
+ help_header, /* already gettext()'d */
+ svn_cl__cmd_table,
+ svn_cl__options,
+ svn_cl__global_options,
+ _(help_footer),
+ pool);
+}
diff --git a/subversion/svn/import-cmd.c b/subversion/svn/import-cmd.c
new file mode 100644
index 0000000..f795092
--- /dev/null
+++ b/subversion/svn/import-cmd.c
@@ -0,0 +1,130 @@
+/*
+ * import-cmd.c -- Import a file or tree into the repository.
+ *
+ * ====================================================================
+ * 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_path.h"
+#include "svn_error.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__import(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_array_header_t *targets;
+ const char *path;
+ const char *url;
+
+ /* Import takes two arguments, for example
+ *
+ * $ svn import projects/test file:///home/jrandom/repos/trunk
+ * ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * (source) (repository)
+ *
+ * or
+ *
+ * $ svn import file:///home/jrandom/repos/some/subdir
+ *
+ * What is the nicest behavior for import, from the user's point of
+ * view? This is a subtle question. Seemingly intuitive answers
+ * can lead to weird situations, such never being able to create
+ * non-directories in the top-level of the repository.
+ *
+ * If 'source' is a file then the basename of 'url' is used as the
+ * filename in the repository. If 'source' is a directory then the
+ * import happens directly in the repository target dir, creating
+ * however many new entries are necessary. If some part of 'url'
+ * does not exist in the repository then parent directories are created
+ * as necessary.
+ *
+ * In the case where no 'source' is given '.' (the current directory)
+ * is implied.
+ *
+ * ### kff todo: review above behaviors.
+ */
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (targets->nelts < 1)
+ return svn_error_create
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Repository URL required when importing"));
+ else if (targets->nelts > 2)
+ return svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments to import command"));
+ else if (targets->nelts == 1)
+ {
+ url = APR_ARRAY_IDX(targets, 0, const char *);
+ path = "";
+ }
+ else
+ {
+ path = APR_ARRAY_IDX(targets, 0, const char *);
+ url = APR_ARRAY_IDX(targets, 1, const char *);
+ }
+
+ SVN_ERR(svn_cl__check_target_is_local_path(path));
+
+ if (! svn_path_is_url(url))
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Invalid URL '%s'"), url);
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_infinity;
+
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
+ NULL, ctx->config, pool));
+
+ SVN_ERR(svn_cl__cleanup_log_msg
+ (ctx->log_msg_baton3,
+ svn_client_import4(path,
+ url,
+ opt_state->depth,
+ opt_state->no_ignore,
+ opt_state->force,
+ opt_state->revprop_table,
+ (opt_state->quiet
+ ? NULL : svn_cl__print_commit_info),
+ NULL,
+ ctx,
+ pool), pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/info-cmd.c b/subversion/svn/info-cmd.c
new file mode 100644
index 0000000..14d0db6
--- /dev/null
+++ b/subversion/svn/info-cmd.c
@@ -0,0 +1,635 @@
+/*
+ * info-cmd.c -- Display information about a resource
+ *
+ * ====================================================================
+ * 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_string.h"
+#include "svn_cmdline.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_time.h"
+#include "svn_xml.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "tree-conflicts.h"
+
+
+/*** Code. ***/
+
+static svn_error_t *
+svn_cl__info_print_time(apr_time_t atime,
+ const char *desc,
+ apr_pool_t *pool)
+{
+ const char *time_utf8;
+
+ time_utf8 = svn_time_to_human_cstring(atime, pool);
+ return svn_cmdline_printf(pool, "%s: %s\n", desc, time_utf8);
+}
+
+
+/* Return string representation of SCHEDULE */
+static const char *
+schedule_str(svn_wc_schedule_t schedule)
+{
+ switch (schedule)
+ {
+ case svn_wc_schedule_normal:
+ return "normal";
+ case svn_wc_schedule_add:
+ return "add";
+ case svn_wc_schedule_delete:
+ return "delete";
+ case svn_wc_schedule_replace:
+ return "replace";
+ default:
+ return "none";
+ }
+}
+
+
+/* A callback of type svn_client_info_receiver2_t.
+ Prints svn info in xml mode to standard out */
+static svn_error_t *
+print_info_xml(void *baton,
+ const char *target,
+ const svn_client_info2_t *info,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ const char *rev_str;
+ const char *path_prefix = baton;
+
+ if (SVN_IS_VALID_REVNUM(info->rev))
+ rev_str = apr_psprintf(pool, "%ld", info->rev);
+ else
+ rev_str = apr_pstrdup(pool, _("Resource is not under version control."));
+
+ /* "<entry ...>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "path", svn_cl__local_style_skip_ancestor(
+ path_prefix, target, pool),
+ "kind", svn_cl__node_kind_str_xml(info->kind),
+ "revision", rev_str,
+ NULL);
+
+ svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL);
+
+ if (info->repos_root_URL || info->repos_UUID)
+ {
+ /* "<repository>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL);
+
+ /* "<root> xx </root>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL);
+
+ /* "<uuid> xx </uuid>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "uuid", info->repos_UUID);
+
+ /* "</repository>" */
+ svn_xml_make_close_tag(&sb, pool, "repository");
+ }
+
+ if (info->wc_info)
+ {
+ /* "<wc-info>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL);
+
+ /* "<wcroot-abspath> xx </wcroot-abspath>" */
+ if (info->wc_info->wcroot_abspath)
+ svn_cl__xml_tagged_cdata(&sb, pool, "wcroot-abspath",
+ info->wc_info->wcroot_abspath);
+
+ /* "<schedule> xx </schedule>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "schedule",
+ schedule_str(info->wc_info->schedule));
+
+ /* "<depth> xx </depth>" */
+ {
+ svn_depth_t depth = info->wc_info->depth;
+
+ /* In the entries world info just passed depth infinity for files */
+ if (depth == svn_depth_unknown && info->kind == svn_node_file)
+ depth = svn_depth_infinity;
+
+ svn_cl__xml_tagged_cdata(&sb, pool, "depth", svn_depth_to_word(depth));
+ }
+
+ /* "<copy-from-url> xx </copy-from-url>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-url",
+ info->wc_info->copyfrom_url);
+
+ /* "<copy-from-rev> xx </copy-from-rev>" */
+ if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
+ svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-rev",
+ apr_psprintf(pool, "%ld",
+ info->wc_info->copyfrom_rev));
+
+ /* "<text-updated> xx </text-updated>" */
+ if (info->wc_info->recorded_time)
+ svn_cl__xml_tagged_cdata(&sb, pool, "text-updated",
+ svn_time_to_cstring(
+ info->wc_info->recorded_time,
+ pool));
+
+ /* "<checksum> xx </checksum>" */
+ /* ### Print the checksum kind. */
+ svn_cl__xml_tagged_cdata(&sb, pool, "checksum",
+ svn_checksum_to_cstring(info->wc_info->checksum,
+ pool));
+
+ if (info->wc_info->changelist)
+ /* "<changelist> xx </changelist>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "changelist",
+ info->wc_info->changelist);
+
+ /* "</wc-info>" */
+ svn_xml_make_close_tag(&sb, pool, "wc-info");
+ }
+
+ if (info->last_changed_author
+ || SVN_IS_VALID_REVNUM(info->last_changed_rev)
+ || info->last_changed_date)
+ {
+ svn_cl__print_xml_commit(&sb, info->last_changed_rev,
+ info->last_changed_author,
+ svn_time_to_cstring(info->last_changed_date,
+ pool),
+ pool);
+ }
+
+ if (info->wc_info && info->wc_info->conflicts)
+ {
+ int i;
+
+ for (i = 0; i < info->wc_info->conflicts->nelts; i++)
+ {
+ const svn_wc_conflict_description2_t *conflict =
+ APR_ARRAY_IDX(info->wc_info->conflicts, i,
+ const svn_wc_conflict_description2_t *);
+
+ switch (conflict->kind)
+ {
+ case svn_wc_conflict_kind_text:
+ /* "<conflict>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "conflict",
+ NULL);
+
+ /* "<prev-base-file> xx </prev-base-file>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "prev-base-file",
+ conflict->base_abspath);
+
+ /* "<prev-wc-file> xx </prev-wc-file>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "prev-wc-file",
+ conflict->my_abspath);
+
+ /* "<cur-base-file> xx </cur-base-file>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "cur-base-file",
+ conflict->their_abspath);
+
+ /* "</conflict>" */
+ svn_xml_make_close_tag(&sb, pool, "conflict");
+ break;
+
+ case svn_wc_conflict_kind_property:
+ /* "<conflict>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "conflict",
+ NULL);
+
+ /* "<prop-file> xx </prop-file>" */
+ svn_cl__xml_tagged_cdata(&sb, pool, "prop-file",
+ conflict->their_abspath);
+
+ /* "</conflict>" */
+ svn_xml_make_close_tag(&sb, pool, "conflict");
+ break;
+
+ case svn_wc_conflict_kind_tree:
+ SVN_ERR(svn_cl__append_tree_conflict_info_xml(sb, conflict,
+ pool));
+ break;
+ }
+ }
+ }
+
+ if (info->lock)
+ svn_cl__print_xml_lock(&sb, info->lock, pool);
+
+ /* "</entry>" */
+ svn_xml_make_close_tag(&sb, pool, "entry");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* A callback of type svn_client_info_receiver2_t. */
+static svn_error_t *
+print_info(void *baton,
+ const char *target,
+ const svn_client_info2_t *info,
+ apr_pool_t *pool)
+{
+ const char *path_prefix = baton;
+
+ SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, target, pool)));
+
+ /* ### remove this someday: it's only here for cmdline output
+ compatibility with svn 1.1 and older. */
+ if (info->kind != svn_node_dir)
+ SVN_ERR(svn_cmdline_printf(pool, _("Name: %s\n"),
+ svn_dirent_basename(target, pool)));
+
+ if (info->wc_info && info->wc_info->wcroot_abspath)
+ SVN_ERR(svn_cmdline_printf(pool, _("Working Copy Root Path: %s\n"),
+ svn_dirent_local_style(
+ info->wc_info->wcroot_abspath,
+ pool)));
+
+ if (info->URL)
+ SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL));
+
+ if (info->repos_root_URL)
+ SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"),
+ info->repos_root_URL));
+
+ if (info->repos_UUID)
+ SVN_ERR(svn_cmdline_printf(pool, _("Repository UUID: %s\n"),
+ info->repos_UUID));
+
+ if (SVN_IS_VALID_REVNUM(info->rev))
+ SVN_ERR(svn_cmdline_printf(pool, _("Revision: %ld\n"), info->rev));
+
+ switch (info->kind)
+ {
+ case svn_node_file:
+ SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: file\n")));
+ break;
+
+ case svn_node_dir:
+ SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: directory\n")));
+ break;
+
+ case svn_node_none:
+ SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: none\n")));
+ break;
+
+ case svn_node_unknown:
+ default:
+ SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: unknown\n")));
+ break;
+ }
+
+ if (info->wc_info)
+ {
+ switch (info->wc_info->schedule)
+ {
+ case svn_wc_schedule_normal:
+ SVN_ERR(svn_cmdline_printf(pool, _("Schedule: normal\n")));
+ break;
+
+ case svn_wc_schedule_add:
+ SVN_ERR(svn_cmdline_printf(pool, _("Schedule: add\n")));
+ break;
+
+ case svn_wc_schedule_delete:
+ SVN_ERR(svn_cmdline_printf(pool, _("Schedule: delete\n")));
+ break;
+
+ case svn_wc_schedule_replace:
+ SVN_ERR(svn_cmdline_printf(pool, _("Schedule: replace\n")));
+ break;
+
+ default:
+ break;
+ }
+
+ switch (info->wc_info->depth)
+ {
+ case svn_depth_unknown:
+ /* Unknown depth is the norm for remote directories anyway
+ (although infinity would be equally appropriate). Let's
+ not bother to print it. */
+ break;
+
+ case svn_depth_empty:
+ SVN_ERR(svn_cmdline_printf(pool, _("Depth: empty\n")));
+ break;
+
+ case svn_depth_files:
+ SVN_ERR(svn_cmdline_printf(pool, _("Depth: files\n")));
+ break;
+
+ case svn_depth_immediates:
+ SVN_ERR(svn_cmdline_printf(pool, _("Depth: immediates\n")));
+ break;
+
+ case svn_depth_exclude:
+ SVN_ERR(svn_cmdline_printf(pool, _("Depth: exclude\n")));
+ break;
+
+ case svn_depth_infinity:
+ /* Infinity is the default depth for working copy
+ directories. Let's not print it, it's not special enough
+ to be worth mentioning. */
+ break;
+
+ default:
+ /* Other depths should never happen here. */
+ SVN_ERR(svn_cmdline_printf(pool, _("Depth: INVALID\n")));
+ }
+
+ if (info->wc_info->copyfrom_url)
+ SVN_ERR(svn_cmdline_printf(pool, _("Copied From URL: %s\n"),
+ info->wc_info->copyfrom_url));
+
+ if (SVN_IS_VALID_REVNUM(info->wc_info->copyfrom_rev))
+ SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"),
+ info->wc_info->copyfrom_rev));
+ }
+
+ if (info->last_changed_author)
+ SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Author: %s\n"),
+ info->last_changed_author));
+
+ if (SVN_IS_VALID_REVNUM(info->last_changed_rev))
+ SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Rev: %ld\n"),
+ info->last_changed_rev));
+
+ if (info->last_changed_date)
+ SVN_ERR(svn_cl__info_print_time(info->last_changed_date,
+ _("Last Changed Date"), pool));
+
+ if (info->wc_info)
+ {
+ if (info->wc_info->recorded_time)
+ SVN_ERR(svn_cl__info_print_time(info->wc_info->recorded_time,
+ _("Text Last Updated"), pool));
+
+ if (info->wc_info->checksum)
+ SVN_ERR(svn_cmdline_printf(pool, _("Checksum: %s\n"),
+ svn_checksum_to_cstring(
+ info->wc_info->checksum, pool)));
+
+ if (info->wc_info->conflicts)
+ {
+ int i;
+
+ for (i = 0; i < info->wc_info->conflicts->nelts; i++)
+ {
+ const svn_wc_conflict_description2_t *conflict =
+ APR_ARRAY_IDX(info->wc_info->conflicts, i,
+ const svn_wc_conflict_description2_t *);
+ const char *desc;
+ const char *src_left_version;
+ const char *src_right_version;
+
+ switch (conflict->kind)
+ {
+ case svn_wc_conflict_kind_text:
+ if (conflict->base_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Previous Base File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, conflict->base_abspath,
+ pool)));
+
+ if (conflict->my_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Previous Working File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, conflict->my_abspath,
+ pool)));
+
+ if (conflict->their_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Current Base File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, conflict->their_abspath,
+ pool)));
+ break;
+
+ case svn_wc_conflict_kind_property:
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Properties File: %s\n"),
+ svn_dirent_local_style(conflict->their_abspath,
+ pool)));
+ break;
+
+ case svn_wc_conflict_kind_tree:
+ SVN_ERR(
+ svn_cl__get_human_readable_tree_conflict_description(
+ &desc, conflict, pool));
+
+ src_left_version =
+ svn_cl__node_description(conflict->src_left_version,
+ info->repos_root_URL, pool);
+
+ src_right_version =
+ svn_cl__node_description(conflict->src_right_version,
+ info->repos_root_URL, pool);
+
+ SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
+ _("Tree conflict"), desc));
+
+ if (src_left_version)
+ SVN_ERR(svn_cmdline_printf(pool, " %s: %s\n",
+ _("Source left"), /* (1) */
+ src_left_version));
+ /* (1): Sneaking in a space in "Source left" so that
+ * it is the same length as "Source right" while it still
+ * starts in the same column. That's just a tiny tweak in
+ * the English `svn'. */
+
+ if (src_right_version)
+ SVN_ERR(svn_cmdline_printf(pool, " %s: %s\n",
+ _("Source right"),
+ src_right_version));
+ break;
+ }
+ }
+ }
+ }
+
+ if (info->lock)
+ {
+ if (info->lock->token)
+ SVN_ERR(svn_cmdline_printf(pool, _("Lock Token: %s\n"),
+ info->lock->token));
+
+ if (info->lock->owner)
+ SVN_ERR(svn_cmdline_printf(pool, _("Lock Owner: %s\n"),
+ info->lock->owner));
+
+ if (info->lock->creation_date)
+ SVN_ERR(svn_cl__info_print_time(info->lock->creation_date,
+ _("Lock Created"), pool));
+
+ if (info->lock->expiration_date)
+ SVN_ERR(svn_cl__info_print_time(info->lock->expiration_date,
+ _("Lock Expires"), pool));
+
+ if (info->lock->comment)
+ {
+ int comment_lines;
+ /* NOTE: The stdio will handle newline translation. */
+ comment_lines = svn_cstring_count_newlines(info->lock->comment) + 1;
+ SVN_ERR(svn_cmdline_printf(pool,
+ Q_("Lock Comment (%i line):\n%s\n",
+ "Lock Comment (%i lines):\n%s\n",
+ comment_lines),
+ comment_lines,
+ info->lock->comment));
+ }
+ }
+
+ if (info->wc_info && info->wc_info->changelist)
+ SVN_ERR(svn_cmdline_printf(pool, _("Changelist: %s\n"),
+ info->wc_info->changelist));
+
+ /* Print extra newline separator. */
+ return svn_cmdline_printf(pool, "\n");
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__info(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_array_header_t *targets = NULL;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ int i;
+ svn_error_t *err;
+ svn_boolean_t seen_nonexistent_target = FALSE;
+ svn_opt_revision_t peg_revision;
+ svn_client_info_receiver2_t receiver;
+ const char *path_prefix;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments. */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ if (opt_state->xml)
+ {
+ receiver = print_info_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("info", pool));
+ }
+ else
+ {
+ receiver = print_info;
+
+ if (opt_state->incremental)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'incremental' option only valid in XML "
+ "mode"));
+ }
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *truepath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Get peg revisions. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool));
+
+ /* If no peg-rev was attached to a URL target, then assume HEAD. */
+ if (svn_path_is_url(truepath))
+ {
+ if (peg_revision.kind == svn_opt_revision_unspecified)
+ peg_revision.kind = svn_opt_revision_head;
+ }
+ else
+ {
+ SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
+ }
+
+ err = svn_client_info3(truepath,
+ &peg_revision, &(opt_state->start_revision),
+ opt_state->depth, TRUE, TRUE,
+ opt_state->changelists,
+ receiver, (void *) path_prefix,
+ ctx, subpool);
+
+ if (err)
+ {
+ /* If one of the targets is a non-existent URL or wc-entry,
+ don't bail out. Just warn and move on to the next target. */
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+ err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(svn_cmdline_fprintf(stderr, subpool, "\n"));
+ }
+ else
+ {
+ return svn_error_trace(err);
+ }
+
+ svn_error_clear(err);
+ err = NULL;
+ seen_nonexistent_target = TRUE;
+ }
+ }
+ svn_pool_destroy(subpool);
+
+ if (opt_state->xml && (! opt_state->incremental))
+ SVN_ERR(svn_cl__xml_print_footer("info", pool));
+
+ if (seen_nonexistent_target)
+ return svn_error_create(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Could not display info for all targets because some "
+ "targets don't exist"));
+ else
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/list-cmd.c b/subversion/svn/list-cmd.c
new file mode 100644
index 0000000..7bc5183
--- /dev/null
+++ b/subversion/svn/list-cmd.c
@@ -0,0 +1,328 @@
+/*
+ * list-cmd.c -- list a URL
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#include "svn_cmdline.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_time.h"
+#include "svn_xml.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_utf.h"
+#include "svn_opt.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+/* Baton used when printing directory entries. */
+struct print_baton {
+ svn_boolean_t verbose;
+ svn_client_ctx_t *ctx;
+};
+
+/* This implements the svn_client_list_func_t API, printing a single
+ directory entry in text format. */
+static svn_error_t *
+print_dirent(void *baton,
+ const char *path,
+ const svn_dirent_t *dirent,
+ const svn_lock_t *lock,
+ const char *abs_path,
+ apr_pool_t *pool)
+{
+ struct print_baton *pb = baton;
+ const char *entryname;
+
+ if (pb->ctx->cancel_func)
+ SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
+
+ if (strcmp(path, "") == 0)
+ {
+ if (dirent->kind == svn_node_file)
+ entryname = svn_dirent_basename(abs_path, pool);
+ else if (pb->verbose)
+ entryname = ".";
+ else
+ /* Don't bother to list if no useful information will be shown. */
+ return SVN_NO_ERROR;
+ }
+ else
+ entryname = path;
+
+ if (pb->verbose)
+ {
+ apr_time_t now = apr_time_now();
+ apr_time_exp_t exp_time;
+ apr_status_t apr_err;
+ apr_size_t size;
+ char timestr[20];
+ const char *sizestr, *utf8_timestr;
+
+ /* svn_time_to_human_cstring gives us something *way* too long
+ to use for this, so we have to roll our own. We include
+ the year if the entry's time is not within half a year. */
+ apr_time_exp_lt(&exp_time, dirent->time);
+ if (apr_time_sec(now - dirent->time) < (365 * 86400 / 2)
+ && apr_time_sec(dirent->time - now) < (365 * 86400 / 2))
+ {
+ apr_err = apr_strftime(timestr, &size, sizeof(timestr),
+ _("%b %d %H:%M"), &exp_time);
+ }
+ else
+ {
+ apr_err = apr_strftime(timestr, &size, sizeof(timestr),
+ _("%b %d %Y"), &exp_time);
+ }
+
+ /* if that failed, just zero out the string and print nothing */
+ if (apr_err)
+ timestr[0] = '\0';
+
+ /* we need it in UTF-8. */
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_timestr, timestr, pool));
+
+ sizestr = apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT, dirent->size);
+
+ return svn_cmdline_printf
+ (pool, "%7ld %-8.8s %c %10s %12s %s%s\n",
+ dirent->created_rev,
+ dirent->last_author ? dirent->last_author : " ? ",
+ lock ? 'O' : ' ',
+ (dirent->kind == svn_node_file) ? sizestr : "",
+ utf8_timestr,
+ entryname,
+ (dirent->kind == svn_node_dir) ? "/" : "");
+ }
+ else
+ {
+ return svn_cmdline_printf(pool, "%s%s\n", entryname,
+ (dirent->kind == svn_node_dir)
+ ? "/" : "");
+ }
+}
+
+
+/* This implements the svn_client_list_func_t API, printing a single dirent
+ in XML format. */
+static svn_error_t *
+print_dirent_xml(void *baton,
+ const char *path,
+ const svn_dirent_t *dirent,
+ const svn_lock_t *lock,
+ const char *abs_path,
+ apr_pool_t *pool)
+{
+ struct print_baton *pb = baton;
+ const char *entryname;
+ svn_stringbuf_t *sb;
+
+ if (strcmp(path, "") == 0)
+ {
+ if (dirent->kind == svn_node_file)
+ entryname = svn_dirent_basename(abs_path, pool);
+ else if (pb->verbose)
+ entryname = ".";
+ else
+ /* Don't bother to list if no useful information will be shown. */
+ return SVN_NO_ERROR;
+ }
+ else
+ entryname = path;
+
+ if (pb->ctx->cancel_func)
+ SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
+
+ sb = svn_stringbuf_create("", pool);
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "kind", svn_cl__node_kind_str_xml(dirent->kind),
+ NULL);
+
+ svn_cl__xml_tagged_cdata(&sb, pool, "name", entryname);
+
+ if (dirent->kind == svn_node_file)
+ {
+ svn_cl__xml_tagged_cdata
+ (&sb, pool, "size",
+ apr_psprintf(pool, "%" SVN_FILESIZE_T_FMT, dirent->size));
+ }
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "commit",
+ "revision",
+ apr_psprintf(pool, "%ld", dirent->created_rev),
+ NULL);
+ svn_cl__xml_tagged_cdata(&sb, pool, "author", dirent->last_author);
+ if (dirent->time)
+ svn_cl__xml_tagged_cdata(&sb, pool, "date",
+ svn_time_to_cstring(dirent->time, pool));
+ svn_xml_make_close_tag(&sb, pool, "commit");
+
+ if (lock)
+ {
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "lock", NULL);
+ svn_cl__xml_tagged_cdata(&sb, pool, "token", lock->token);
+ svn_cl__xml_tagged_cdata(&sb, pool, "owner", lock->owner);
+ svn_cl__xml_tagged_cdata(&sb, pool, "comment", lock->comment);
+ svn_cl__xml_tagged_cdata(&sb, pool, "created",
+ svn_time_to_cstring(lock->creation_date,
+ pool));
+ if (lock->expiration_date != 0)
+ svn_cl__xml_tagged_cdata(&sb, pool, "expires",
+ svn_time_to_cstring
+ (lock->expiration_date, pool));
+ svn_xml_make_close_tag(&sb, pool, "lock");
+ }
+
+ svn_xml_make_close_tag(&sb, pool, "entry");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__list(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_array_header_t *targets;
+ int i;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ apr_uint32_t dirent_fields;
+ struct print_baton pb;
+ svn_boolean_t seen_nonexistent_target = FALSE;
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ if (opt_state->xml)
+ {
+ /* The XML output contains all the information, so "--verbose"
+ does not apply. */
+ if (opt_state->verbose)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'verbose' option invalid in XML mode"));
+
+ /* 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("lists", pool));
+ }
+ else
+ {
+ if (opt_state->incremental)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'incremental' option only valid in XML "
+ "mode"));
+ }
+
+ if (opt_state->verbose || opt_state->xml)
+ dirent_fields = SVN_DIRENT_ALL;
+ else
+ dirent_fields = SVN_DIRENT_KIND; /* the only thing we actually need... */
+
+ pb.ctx = ctx;
+ pb.verbose = opt_state->verbose;
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_immediates;
+
+ /* For each target, try to list it. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(subpool);
+
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Get peg revisions. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ subpool));
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "list",
+ "path", truepath[0] == '\0' ? "." : truepath,
+ NULL);
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ }
+
+ err = svn_client_list2(truepath, &peg_revision,
+ &(opt_state->start_revision),
+ opt_state->depth,
+ dirent_fields,
+ (opt_state->xml || opt_state->verbose),
+ opt_state->xml ? print_dirent_xml : print_dirent,
+ &pb, ctx, subpool);
+
+ if (err)
+ {
+ /* If one of the targets is a non-existent URL or wc-entry,
+ don't bail out. Just warn and move on to the next target. */
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+ err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ svn_handle_warning2(stderr, err, "svn: ");
+ else
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ err = NULL;
+ seen_nonexistent_target = TRUE;
+ }
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ svn_xml_make_close_tag(&sb, pool, "list");
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ }
+ }
+
+ svn_pool_destroy(subpool);
+
+ if (opt_state->xml && ! opt_state->incremental)
+ SVN_ERR(svn_cl__xml_print_footer("lists", pool));
+
+ if (seen_nonexistent_target)
+ return svn_error_create(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Could not list all targets because some targets don't exist"));
+ else
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/lock-cmd.c b/subversion/svn/lock-cmd.c
new file mode 100644
index 0000000..c2795da
--- /dev/null
+++ b/subversion/svn/lock-cmd.c
@@ -0,0 +1,110 @@
+/*
+ * lock-cmd.c -- LOck a working copy path in the repository.
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_subst.h"
+#include "svn_path.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_cmdline.h"
+#include "cl.h"
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Get a lock comment, allocate it in POOL and store it in *COMMENT. */
+static svn_error_t *
+get_comment(const char **comment, svn_client_ctx_t *ctx,
+ svn_cl__opt_state_t *opt_state, apr_pool_t *pool)
+{
+ svn_string_t *comment_string;
+
+ if (opt_state->filedata)
+ {
+ /* Get it from the -F argument. */
+ if (strlen(opt_state->filedata->data) < opt_state->filedata->len)
+ {
+ /* A message containing a zero byte can't be represented as a C
+ string. */
+ return svn_error_create(SVN_ERR_CL_BAD_LOG_MESSAGE, NULL,
+ _("Lock comment contains a zero byte"));
+ }
+ comment_string = svn_string_create(opt_state->filedata->data, pool);
+
+ }
+ else if (opt_state->message)
+ {
+ /* Get if from the -m option. */
+ comment_string = svn_string_create(opt_state->message, pool);
+ }
+ else
+ {
+ *comment = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ /* Translate to UTF8/LF. */
+ SVN_ERR(svn_subst_translate_string2(&comment_string, NULL, NULL,
+ comment_string, opt_state->encoding,
+ FALSE, pool, pool));
+ *comment = comment_string->data;
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__lock(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_array_header_t *targets;
+ const char *comment;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We only support locking files, so '.' is not valid. */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__assert_homogeneous_target_type(targets));
+
+ /* Get comment. */
+ SVN_ERR(get_comment(&comment, ctx, opt_state, pool));
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ return svn_client_lock(targets, comment, opt_state->force, ctx, pool);
+}
diff --git a/subversion/svn/log-cmd.c b/subversion/svn/log-cmd.c
new file mode 100644
index 0000000..4057b0a
--- /dev/null
+++ b/subversion/svn/log-cmd.c
@@ -0,0 +1,706 @@
+/*
+ * log-cmd.c -- Display log messages
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#define APR_WANT_STRFUNC
+#define APR_WANT_STDIO
+#include <apr_want.h>
+
+#include "svn_client.h"
+#include "svn_compat.h"
+#include "svn_dirent_uri.h"
+#include "svn_string.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_sorts.h"
+#include "svn_xml.h"
+#include "svn_time.h"
+#include "svn_cmdline.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */
+struct log_receiver_baton
+{
+ /* Client context. */
+ svn_client_ctx_t *ctx;
+
+ /* The target of the log operation. */
+ const char *target_path_or_url;
+ svn_opt_revision_t target_peg_revision;
+
+ /* Don't print log message body nor its line count. */
+ svn_boolean_t omit_log_message;
+
+ /* Whether to show diffs in the log. (maps to --diff) */
+ svn_boolean_t show_diff;
+
+ /* Depth applied to diff output. */
+ svn_depth_t depth;
+
+ /* Diff arguments received from command line. */
+ const char *diff_extensions;
+
+ /* Stack which keeps track of merge revision nesting, using svn_revnum_t's */
+ apr_array_header_t *merge_stack;
+
+ /* Pool for persistent allocations. */
+ apr_pool_t *pool;
+};
+
+
+/* The separator between log messages. */
+#define SEP_STRING \
+ "------------------------------------------------------------------------\n"
+
+
+/* Implement `svn_log_entry_receiver_t', printing the logs in
+ * a human-readable and machine-parseable format.
+ *
+ * BATON is of type `struct log_receiver_baton'.
+ *
+ * First, print a header line. Then if CHANGED_PATHS is non-null,
+ * print all affected paths in a list headed "Changed paths:\n",
+ * immediately following the header line. Then print a newline
+ * followed by the message body, unless BATON->omit_log_message is true.
+ *
+ * Here are some examples of the output:
+ *
+ * $ svn log -r1847:1846
+ * ------------------------------------------------------------------------
+ * rev 1847: cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
+ *
+ * Fix for Issue #694.
+ *
+ * * subversion/libsvn_repos/delta.c
+ * (delta_files): Rework the logic in this function to only call
+ * send_text_deltas if there are deltas to send, and within that case,
+ * only use a real delta stream if the caller wants real text deltas.
+ *
+ * ------------------------------------------------------------------------
+ * rev 1846: whoever | Wed 1 May 2002 15:23:41 | 1 line
+ *
+ * imagine an example log message here
+ * ------------------------------------------------------------------------
+ *
+ * Or:
+ *
+ * $ svn log -r1847:1846 -v
+ * ------------------------------------------------------------------------
+ * rev 1847: cmpilato | Wed 1 May 2002 15:44:26 | 7 lines
+ * Changed paths:
+ * M /trunk/subversion/libsvn_repos/delta.c
+ *
+ * Fix for Issue #694.
+ *
+ * * subversion/libsvn_repos/delta.c
+ * (delta_files): Rework the logic in this function to only call
+ * send_text_deltas if there are deltas to send, and within that case,
+ * only use a real delta stream if the caller wants real text deltas.
+ *
+ * ------------------------------------------------------------------------
+ * rev 1846: whoever | Wed 1 May 2002 15:23:41 | 1 line
+ * Changed paths:
+ * M /trunk/notes/fs_dumprestore.txt
+ * M /trunk/subversion/libsvn_repos/dump.c
+ *
+ * imagine an example log message here
+ * ------------------------------------------------------------------------
+ *
+ * Or:
+ *
+ * $ svn log -r1847:1846 -q
+ * ------------------------------------------------------------------------
+ * rev 1847: cmpilato | Wed 1 May 2002 15:44:26
+ * ------------------------------------------------------------------------
+ * rev 1846: whoever | Wed 1 May 2002 15:23:41
+ * ------------------------------------------------------------------------
+ *
+ * Or:
+ *
+ * $ svn log -r1847:1846 -qv
+ * ------------------------------------------------------------------------
+ * rev 1847: cmpilato | Wed 1 May 2002 15:44:26
+ * Changed paths:
+ * M /trunk/subversion/libsvn_repos/delta.c
+ * ------------------------------------------------------------------------
+ * rev 1846: whoever | Wed 1 May 2002 15:23:41
+ * Changed paths:
+ * M /trunk/notes/fs_dumprestore.txt
+ * M /trunk/subversion/libsvn_repos/dump.c
+ * ------------------------------------------------------------------------
+ *
+ */
+static svn_error_t *
+log_entry_receiver(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct log_receiver_baton *lb = baton;
+ const char *author;
+ const char *date;
+ const char *message;
+
+ if (lb->ctx->cancel_func)
+ SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
+
+ svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
+
+ if (log_entry->revision == 0 && message == NULL)
+ return SVN_NO_ERROR;
+
+ if (! SVN_IS_VALID_REVNUM(log_entry->revision))
+ {
+ apr_array_pop(lb->merge_stack);
+ return SVN_NO_ERROR;
+ }
+
+ /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=807
+ for more on the fallback fuzzy conversions below. */
+
+ if (author == NULL)
+ author = _("(no author)");
+
+ if (date && date[0])
+ /* Convert date to a format for humans. */
+ SVN_ERR(svn_cl__time_cstring_to_human_cstring(&date, date, pool));
+ else
+ date = _("(no date)");
+
+ if (! lb->omit_log_message && message == NULL)
+ message = "";
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ SEP_STRING "r%ld | %s | %s",
+ log_entry->revision, author, date));
+
+ if (message != NULL)
+ {
+ /* Number of lines in the msg. */
+ int lines = svn_cstring_count_newlines(message) + 1;
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ Q_(" | %d line", " | %d lines", lines),
+ lines));
+ }
+
+ SVN_ERR(svn_cmdline_printf(pool, "\n"));
+
+ if (log_entry->changed_paths2)
+ {
+ apr_array_header_t *sorted_paths;
+ int i;
+
+ /* Get an array of sorted hash keys. */
+ sorted_paths = svn_sort__hash(log_entry->changed_paths2,
+ svn_sort_compare_items_as_paths, pool);
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Changed paths:\n")));
+ for (i = 0; i < sorted_paths->nelts; i++)
+ {
+ svn_sort__item_t *item = &(APR_ARRAY_IDX(sorted_paths, i,
+ svn_sort__item_t));
+ const char *path = item->key;
+ svn_log_changed_path2_t *log_item
+ = apr_hash_get(log_entry->changed_paths2, item->key, item->klen);
+ const char *copy_data = "";
+
+ if (lb->ctx->cancel_func)
+ SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
+
+ if (log_item->copyfrom_path
+ && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
+ {
+ copy_data
+ = apr_psprintf(pool,
+ _(" (from %s:%ld)"),
+ log_item->copyfrom_path,
+ log_item->copyfrom_rev);
+ }
+ SVN_ERR(svn_cmdline_printf(pool, " %c %s%s\n",
+ log_item->action, path,
+ copy_data));
+ }
+ }
+
+ if (lb->merge_stack->nelts > 0)
+ {
+ int i;
+
+ /* Print the result of merge line */
+ if (log_entry->subtractive_merge)
+ SVN_ERR(svn_cmdline_printf(pool, _("Reverse merged via:")));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, _("Merged via:")));
+ for (i = 0; i < lb->merge_stack->nelts; i++)
+ {
+ svn_revnum_t rev = APR_ARRAY_IDX(lb->merge_stack, i, svn_revnum_t);
+
+ SVN_ERR(svn_cmdline_printf(pool, " r%ld%c", rev,
+ i == lb->merge_stack->nelts - 1 ?
+ '\n' : ','));
+ }
+ }
+
+ if (message != NULL)
+ {
+ /* A blank line always precedes the log message. */
+ SVN_ERR(svn_cmdline_printf(pool, "\n%s\n", message));
+ }
+
+ /* Print a diff if requested. */
+ if (lb->show_diff)
+ {
+ apr_file_t *outfile;
+ apr_file_t *errfile;
+ apr_array_header_t *diff_options;
+ apr_status_t status;
+ svn_opt_revision_t start_revision;
+ svn_opt_revision_t end_revision;
+
+ if ((status = apr_file_open_stdout(&outfile, pool)))
+ return svn_error_wrap_apr(status, _("Can't open stdout"));
+ if ((status = apr_file_open_stderr(&errfile, pool)))
+ return svn_error_wrap_apr(status, _("Can't open stderr"));
+
+ /* Fall back to "" to get options initialized either way. */
+ if (lb->diff_extensions)
+ diff_options = svn_cstring_split(lb->diff_extensions, " \t\n\r",
+ TRUE, pool);
+ else
+ diff_options = NULL;
+
+ start_revision.kind = svn_opt_revision_number;
+ start_revision.value.number = log_entry->revision - 1;
+ end_revision.kind = svn_opt_revision_number;
+ end_revision.value.number = log_entry->revision;
+
+ SVN_ERR(svn_cmdline_printf(pool, _("\n")));
+ SVN_ERR(svn_cmdline_fflush(stdout));
+ SVN_ERR(svn_client_diff_peg5(diff_options,
+ lb->target_path_or_url,
+ &lb->target_peg_revision,
+ &start_revision, &end_revision,
+ NULL,
+ lb->depth,
+ FALSE, /* ignore ancestry */
+ TRUE, /* no diff deleted */
+ FALSE, /* show copies as adds */
+ FALSE, /* ignore content type */
+ FALSE, /* use git diff format */
+ svn_cmdline_output_encoding(pool),
+ outfile,
+ errfile,
+ NULL,
+ lb->ctx, pool));
+ SVN_ERR(svn_cmdline_printf(pool, _("\n")));
+ }
+
+ SVN_ERR(svn_cmdline_fflush(stdout));
+ SVN_ERR(svn_cmdline_fflush(stderr));
+
+ if (log_entry->has_children)
+ APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements `svn_log_entry_receiver_t', printing the logs in XML.
+ *
+ * BATON is of type `struct log_receiver_baton'.
+ *
+ * Here is an example of the output; note that the "<log>" and
+ * "</log>" tags are not emitted by this function:
+ *
+ * $ svn log --xml -r 1648:1649
+ * <log>
+ * <logentry
+ * revision="1648">
+ * <author>david</author>
+ * <date>2002-04-06T16:34:51.428043Z</date>
+ * <msg> * packages/rpm/subversion.spec : Now requires apache 2.0.36.
+ * </msg>
+ * </logentry>
+ * <logentry
+ * revision="1649">
+ * <author>cmpilato</author>
+ * <date>2002-04-06T17:01:28.185136Z</date>
+ * <msg>Fix error handling when the $EDITOR is needed but unavailable. Ah
+ * ... now that&apos;s *much* nicer.
+ *
+ * * subversion/clients/cmdline/util.c
+ * (svn_cl__edit_externally): Clean up the &quot;no external editor&quot;
+ * error message.
+ * (svn_cl__get_log_message): Wrap &quot;no external editor&quot;
+ * errors with helpful hints about the -m and -F options.
+ *
+ * * subversion/libsvn_client/commit.c
+ * (svn_client_commit): Actually capture and propagate &quot;no external
+ * editor&quot; errors.</msg>
+ * </logentry>
+ * </log>
+ *
+ */
+static svn_error_t *
+log_entry_receiver_xml(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ struct log_receiver_baton *lb = baton;
+ /* Collate whole log message into sb before printing. */
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ char *revstr;
+ const char *author;
+ const char *date;
+ const char *message;
+
+ if (lb->ctx->cancel_func)
+ SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
+
+ svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
+
+ if (author)
+ author = svn_xml_fuzzy_escape(author, pool);
+ if (date)
+ date = svn_xml_fuzzy_escape(date, pool);
+ if (message)
+ message = svn_xml_fuzzy_escape(message, pool);
+
+ if (log_entry->revision == 0 && message == NULL)
+ return SVN_NO_ERROR;
+
+ if (! SVN_IS_VALID_REVNUM(log_entry->revision))
+ {
+ svn_xml_make_close_tag(&sb, pool, "logentry");
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ apr_array_pop(lb->merge_stack);
+
+ return SVN_NO_ERROR;
+ }
+
+ revstr = apr_psprintf(pool, "%ld", log_entry->revision);
+ /* <logentry revision="xxx"> */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "logentry",
+ "revision", revstr, NULL);
+
+ /* <author>xxx</author> */
+ svn_cl__xml_tagged_cdata(&sb, pool, "author", author);
+
+ /* Print the full, uncut, date. This is machine output. */
+ /* According to the docs for svn_log_entry_receiver_t, either
+ NULL or the empty string represents no date. Avoid outputting an
+ empty date element. */
+ if (date && date[0] == '\0')
+ date = NULL;
+ /* <date>xxx</date> */
+ svn_cl__xml_tagged_cdata(&sb, pool, "date", date);
+
+ if (log_entry->changed_paths2)
+ {
+ apr_hash_index_t *hi;
+
+ /* <paths> */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths",
+ NULL);
+
+ for (hi = apr_hash_first(pool, log_entry->changed_paths2);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ svn_log_changed_path2_t *log_item = svn__apr_hash_index_val(hi);
+ char action[2];
+
+ action[0] = log_item->action;
+ action[1] = '\0';
+ if (log_item->copyfrom_path
+ && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
+ {
+ /* <path action="X" copyfrom-path="xxx" copyfrom-rev="xxx"> */
+ revstr = apr_psprintf(pool, "%ld",
+ log_item->copyfrom_rev);
+ svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
+ "action", action,
+ "copyfrom-path", log_item->copyfrom_path,
+ "copyfrom-rev", revstr,
+ "kind", svn_cl__node_kind_str_xml(
+ log_item->node_kind),
+ "text-mods", svn_tristate__to_word(
+ log_item->text_modified),
+ "prop-mods", svn_tristate__to_word(
+ log_item->props_modified),
+ NULL);
+ }
+ else
+ {
+ /* <path action="X"> */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
+ "action", action,
+ "kind", svn_cl__node_kind_str_xml(log_item->node_kind), NULL);
+ }
+ /* xxx</path> */
+ svn_xml_escape_cdata_cstring(&sb, path, pool);
+ svn_xml_make_close_tag(&sb, pool, "path");
+ }
+
+ /* </paths> */
+ svn_xml_make_close_tag(&sb, pool, "paths");
+ }
+
+ if (message != NULL)
+ {
+ /* <msg>xxx</msg> */
+ svn_cl__xml_tagged_cdata(&sb, pool, "msg", message);
+ }
+
+ svn_compat_log_revprops_clear(log_entry->revprops);
+ if (log_entry->revprops && apr_hash_count(log_entry->revprops) > 0)
+ {
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "revprops", NULL);
+ SVN_ERR(svn_cl__print_xml_prop_hash(&sb, log_entry->revprops,
+ FALSE, /* name_only */
+ pool));
+ svn_xml_make_close_tag(&sb, pool, "revprops");
+ }
+
+ if (log_entry->has_children)
+ APR_ARRAY_PUSH(lb->merge_stack, svn_revnum_t) = log_entry->revision;
+ else
+ svn_xml_make_close_tag(&sb, pool, "logentry");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__log(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_array_header_t *targets;
+ struct log_receiver_baton lb;
+ const char *target;
+ int i;
+ apr_array_header_t *revprops;
+
+ if (!opt_state->xml)
+ {
+ if (opt_state->all_revprops)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'with-all-revprops' option only valid in"
+ " XML mode"));
+ if (opt_state->no_revprops)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'with-no-revprops' option only valid in"
+ " XML mode"));
+ if (opt_state->revprop_table != NULL)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'with-revprop' option only valid in"
+ " XML mode"));
+ }
+ else
+ {
+ if (opt_state->show_diff)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'diff' option is not supported in "
+ "XML mode"));
+ }
+
+ if (opt_state->quiet && opt_state->show_diff)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'quiet' and 'diff' options are "
+ "mutually exclusive"));
+ if (opt_state->diff_cmd && (! opt_state->show_diff))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'diff-cmd' option requires 'diff' "
+ "option"));
+ if (opt_state->internal_diff && (! opt_state->show_diff))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'internal-diff' option requires "
+ "'diff' option"));
+ if (opt_state->extensions && (! opt_state->show_diff))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'extensions' option requires 'diff' "
+ "option"));
+
+ if (opt_state->depth != svn_depth_unknown && (! opt_state->show_diff))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'depth' option requires 'diff' option"));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ /* Determine if they really want a two-revision range. */
+ if (opt_state->used_change_arg)
+ {
+ if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1)
+ {
+ return svn_error_create
+ (SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("-c and -r are mutually exclusive"));
+ }
+ for (i = 0; i < opt_state->revision_ranges->nelts; i++)
+ {
+ svn_opt_revision_range_t *range;
+ range = APR_ARRAY_IDX(opt_state->revision_ranges, i,
+ svn_opt_revision_range_t *);
+ if (range->start.value.number < range->end.value.number)
+ range->start.value.number++;
+ else
+ range->end.value.number++;
+ }
+ }
+
+ /* Parse the first target into path-or-url and peg revision. */
+ target = APR_ARRAY_IDX(targets, 0, const char *);
+ SVN_ERR(svn_opt_parse_path(&lb.target_peg_revision, &lb.target_path_or_url,
+ target, pool));
+ if (lb.target_peg_revision.kind == svn_opt_revision_unspecified)
+ lb.target_peg_revision.kind = (svn_path_is_url(target)
+ ? svn_opt_revision_head
+ : svn_opt_revision_working);
+ APR_ARRAY_IDX(targets, 0, const char *) = lb.target_path_or_url;
+
+ if (svn_path_is_url(target))
+ {
+ for (i = 1; i < targets->nelts; i++)
+ {
+ target = APR_ARRAY_IDX(targets, i, const char *);
+
+ if (svn_path_is_url(target) || target[0] == '/')
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Only relative paths can be specified"
+ " after a URL for 'svn log', "
+ "but '%s' is not a relative path"),
+ target);
+ }
+ }
+
+ lb.ctx = ctx;
+ lb.omit_log_message = opt_state->quiet;
+ lb.show_diff = opt_state->show_diff;
+ lb.depth = opt_state->depth == svn_depth_unknown ? svn_depth_infinity
+ : opt_state->depth;
+ lb.diff_extensions = opt_state->extensions;
+ lb.merge_stack = apr_array_make(pool, 0, sizeof(svn_revnum_t));
+ lb.pool = pool;
+
+ 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("log", pool));
+
+ if (opt_state->all_revprops)
+ revprops = NULL;
+ else if(opt_state->no_revprops)
+ {
+ revprops = apr_array_make(pool, 0, sizeof(char *));
+ }
+ else if (opt_state->revprop_table != NULL)
+ {
+ apr_hash_index_t *hi;
+ revprops = apr_array_make(pool,
+ apr_hash_count(opt_state->revprop_table),
+ sizeof(char *));
+ for (hi = apr_hash_first(pool, opt_state->revprop_table);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *property = svn__apr_hash_index_key(hi);
+ svn_string_t *value = svn__apr_hash_index_val(hi);
+
+ if (value && value->data[0] != '\0')
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("cannot assign with 'with-revprop'"
+ " option (drop the '=')"));
+ APR_ARRAY_PUSH(revprops, const char *) = property;
+ }
+ }
+ else
+ {
+ revprops = apr_array_make(pool, 3, sizeof(char *));
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
+ if (!opt_state->quiet)
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
+ }
+ SVN_ERR(svn_client_log5(targets,
+ &lb.target_peg_revision,
+ opt_state->revision_ranges,
+ opt_state->limit,
+ opt_state->verbose,
+ opt_state->stop_on_copy,
+ opt_state->use_merge_history,
+ revprops,
+ log_entry_receiver_xml,
+ &lb,
+ ctx,
+ pool));
+
+ if (! opt_state->incremental)
+ SVN_ERR(svn_cl__xml_print_footer("log", pool));
+ }
+ else /* default output format */
+ {
+ revprops = apr_array_make(pool, 3, sizeof(char *));
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
+ if (!opt_state->quiet)
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
+ SVN_ERR(svn_client_log5(targets,
+ &lb.target_peg_revision,
+ opt_state->revision_ranges,
+ opt_state->limit,
+ opt_state->verbose,
+ opt_state->stop_on_copy,
+ opt_state->use_merge_history,
+ revprops,
+ log_entry_receiver,
+ &lb,
+ ctx,
+ pool));
+
+ if (! opt_state->incremental)
+ SVN_ERR(svn_cmdline_printf(pool, SEP_STRING));
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/main.c b/subversion/svn/main.c
new file mode 100644
index 0000000..8f3a7c4
--- /dev/null
+++ b/subversion/svn/main.c
@@ -0,0 +1,2709 @@
+/*
+ * main.c: Subversion command line client.
+ *
+ * ====================================================================
+ * 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 <string.h>
+#include <assert.h>
+
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_general.h>
+#include <apr_signal.h>
+
+#include "svn_cmdline.h"
+#include "svn_pools.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_config.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_delta.h"
+#include "svn_diff.h"
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_opt.h"
+#include "svn_utf.h"
+#include "svn_auth.h"
+#include "svn_hash.h"
+#include "svn_version.h"
+#include "cl.h"
+
+#include "private/svn_wc_private.h"
+#include "private/svn_cmdline_private.h"
+
+#include "svn_private_config.h"
+
+
+/*** Option Processing ***/
+
+/* Add an identifier here for long options that don't have a short
+ option. Options that have both long and short options should just
+ use the short option letter as identifier. */
+typedef enum svn_cl__longopt_t {
+ opt_ancestor_path = SVN_OPT_FIRST_LONGOPT_ID,
+ opt_auth_password,
+ opt_auth_username,
+ opt_autoprops,
+ opt_changelist,
+ opt_config_dir,
+ opt_config_options,
+ opt_diff_cmd,
+ opt_dry_run,
+ opt_editor_cmd,
+ opt_encoding,
+ opt_force_log,
+ opt_force,
+ opt_keep_changelists,
+ opt_ignore_ancestry,
+ opt_ignore_externals,
+ opt_incremental,
+ opt_merge_cmd,
+ opt_native_eol,
+ opt_new_cmd,
+ opt_no_auth_cache,
+ opt_no_autoprops,
+ opt_no_diff_deleted,
+ opt_no_ignore,
+ opt_no_unlock,
+ opt_non_interactive,
+ opt_notice_ancestry,
+ opt_old_cmd,
+ opt_record_only,
+ opt_relocate,
+ opt_remove,
+ opt_revprop,
+ opt_stop_on_copy,
+ opt_strict,
+ opt_summarize,
+ opt_targets,
+ opt_depth,
+ opt_set_depth,
+ opt_version,
+ opt_xml,
+ opt_keep_local,
+ opt_with_revprop,
+ opt_with_all_revprops,
+ opt_with_no_revprops,
+ opt_parents,
+ opt_accept,
+ opt_show_revs,
+ opt_reintegrate,
+ opt_trust_server_cert,
+ opt_strip,
+ opt_show_copies_as_adds,
+ opt_ignore_keywords,
+ opt_reverse_diff,
+ opt_ignore_whitespace,
+ opt_diff,
+ opt_internal_diff,
+ opt_use_git_diff_format,
+ opt_allow_mixed_revisions
+} svn_cl__longopt_t;
+
+
+/* Option codes and descriptions for the command line client.
+ *
+ * The entire list must be terminated with an entry of nulls.
+ */
+const apr_getopt_option_t svn_cl__options[] =
+{
+ {"force", opt_force, 0, N_("force operation to run")},
+ {"force-log", opt_force_log, 0,
+ N_("force validity of log message source")},
+ {"help", 'h', 0, N_("show help on a subcommand")},
+ {NULL, '?', 0, N_("show help on a subcommand")},
+ {"message", 'm', 1, N_("specify log message ARG")},
+ {"quiet", 'q', 0, N_("print nothing, or only summary information")},
+ {"recursive", 'R', 0, N_("descend recursively, same as --depth=infinity")},
+ {"non-recursive", 'N', 0, N_("obsolete; try --depth=files or --depth=immediates")},
+ {"change", 'c', 1,
+ N_("the change made by revision ARG (like -r ARG-1:ARG)\n"
+ " "
+ "If ARG is negative this is like -r ARG:ARG-1")},
+ {"revision", 'r', 1,
+ N_("ARG (some commands also take ARG1:ARG2 range)\n"
+ " "
+ "A revision argument can be one of:\n"
+ " "
+ " NUMBER revision number\n"
+ " "
+ " '{' DATE '}' revision at start of the date\n"
+ " "
+ " 'HEAD' latest in repository\n"
+ " "
+ " 'BASE' base rev of item's working copy\n"
+ " "
+ " 'COMMITTED' last commit at or before BASE\n"
+ " "
+ " 'PREV' revision just before COMMITTED")},
+ {"file", 'F', 1, N_("read log message from file ARG")},
+ {"incremental", opt_incremental, 0,
+ N_("give output suitable for concatenation")},
+ {"encoding", opt_encoding, 1,
+ N_("treat value as being in charset encoding ARG")},
+ {"version", opt_version, 0, N_("show program version information")},
+ {"verbose", 'v', 0, N_("print extra information")},
+ {"show-updates", 'u', 0, N_("display update information")},
+ {"username", opt_auth_username, 1, N_("specify a username ARG")},
+ {"password", opt_auth_password, 1, N_("specify a password ARG")},
+ {"extensions", 'x', 1,
+ N_("Default: '-u'. When Subversion is invoking an\n"
+ " "
+ "external diff program, ARG is simply passed along\n"
+ " "
+ "to the program. But when Subversion is using its\n"
+ " "
+ "default internal diff implementation, or when\n"
+ " "
+ "Subversion is displaying blame annotations, ARG\n"
+ " "
+ "could be any of the following:\n"
+ " "
+ " -u (--unified):\n"
+ " "
+ " Output 3 lines of unified context.\n"
+ " "
+ " -b (--ignore-space-change):\n"
+ " "
+ " Ignore changes in the amount of white space.\n"
+ " "
+ " -w (--ignore-all-space):\n"
+ " "
+ " Ignore all white space.\n"
+ " "
+ " --ignore-eol-style:\n"
+ " "
+ " Ignore changes in EOL style.\n"
+ " "
+ " -p (--show-c-function):\n"
+ " "
+ " Show C function name in diff output.")},
+ {"targets", opt_targets, 1,
+ N_("pass contents of file ARG as additional args")},
+ {"depth", opt_depth, 1,
+ N_("limit operation by depth ARG ('empty', 'files',\n"
+ " "
+ "'immediates', or 'infinity')")},
+ {"set-depth", opt_set_depth, 1,
+ N_("set new working copy depth to ARG ('exclude',\n"
+ " "
+ "'empty', 'files', 'immediates', or 'infinity')")},
+ {"xml", opt_xml, 0, N_("output in XML")},
+ {"strict", opt_strict, 0, N_("use strict semantics")},
+ {"stop-on-copy", opt_stop_on_copy, 0,
+ N_("do not cross copies while traversing history")},
+ {"no-ignore", opt_no_ignore, 0,
+ N_("disregard default and svn:ignore property ignores")},
+ {"no-auth-cache", opt_no_auth_cache, 0,
+ N_("do not cache authentication tokens")},
+ {"trust-server-cert", opt_trust_server_cert, 0,
+ N_("accept SSL server certificates from unknown\n"
+ " "
+ "certificate authorities without prompting (but only\n"
+ " "
+ "with '--non-interactive')") },
+ {"non-interactive", opt_non_interactive, 0,
+ N_("do no interactive prompting")},
+ {"dry-run", opt_dry_run, 0,
+ N_("try operation but make no changes")},
+ {"no-diff-deleted", opt_no_diff_deleted, 0,
+ N_("do not print differences for deleted files")},
+ {"notice-ancestry", opt_notice_ancestry, 0,
+ N_("notice ancestry when calculating differences")},
+ {"ignore-ancestry", opt_ignore_ancestry, 0,
+ N_("ignore ancestry when calculating merges")},
+ {"ignore-externals", opt_ignore_externals, 0,
+ N_("ignore externals definitions")},
+ {"diff-cmd", opt_diff_cmd, 1, N_("use ARG as diff command")},
+ {"diff3-cmd", opt_merge_cmd, 1, N_("use ARG as merge command")},
+ {"editor-cmd", opt_editor_cmd, 1, N_("use ARG as external editor")},
+ {"record-only", opt_record_only, 0,
+ N_("merge only mergeinfo differences")},
+ {"old", opt_old_cmd, 1, N_("use ARG as the older target")},
+ {"new", opt_new_cmd, 1, N_("use ARG as the newer target")},
+ {"revprop", opt_revprop, 0,
+ N_("operate on a revision property (use with -r)")},
+ {"relocate", opt_relocate, 0, N_("relocate via URL-rewriting")},
+ {"config-dir", opt_config_dir, 1,
+ N_("read user configuration files from directory ARG")},
+ {"config-option", opt_config_options, 1,
+ N_("set user configuration option in the format:\n"
+ " "
+ " FILE:SECTION:OPTION=[VALUE]\n"
+ " "
+ "For example:\n"
+ " "
+ " servers:global:http-library=serf")},
+ {"auto-props", opt_autoprops, 0, N_("enable automatic properties")},
+ {"no-auto-props", opt_no_autoprops, 0, N_("disable automatic properties")},
+ {"native-eol", opt_native_eol, 1,
+ N_("use a different EOL marker than the standard\n"
+ " "
+ "system marker for files with the svn:eol-style\n"
+ " "
+ "property set to 'native'.\n"
+ " "
+ "ARG may be one of 'LF', 'CR', 'CRLF'")},
+ {"limit", 'l', 1, N_("maximum number of log entries")},
+ {"no-unlock", opt_no_unlock, 0, N_("don't unlock the targets")},
+ {"summarize", opt_summarize, 0, N_("show a summary of the results")},
+ {"remove", opt_remove, 0, N_("remove changelist association")},
+ {"changelist", opt_changelist, 1,
+ N_("operate only on members of changelist ARG")},
+ {"keep-changelists", opt_keep_changelists, 0,
+ N_("don't delete changelists after commit")},
+ {"keep-local", opt_keep_local, 0, N_("keep path in working copy")},
+ {"with-all-revprops", opt_with_all_revprops, 0,
+ N_("retrieve all revision properties")},
+ {"with-no-revprops", opt_with_no_revprops, 0,
+ N_("retrieve no revision properties")},
+ {"with-revprop", opt_with_revprop, 1,
+ N_("set revision property ARG in new revision\n"
+ " "
+ "using the name[=value] format")},
+ {"parents", opt_parents, 0, N_("make intermediate directories")},
+ {"use-merge-history", 'g', 0,
+ N_("use/display additional information from merge\n"
+ " "
+ "history")},
+ {"accept", opt_accept, 1,
+ N_("specify automatic conflict resolution action\n"
+ " "
+ "('postpone', 'working', 'base', 'mine-conflict',\n"
+ " "
+ "'theirs-conflict', 'mine-full', 'theirs-full',\n"
+ " "
+ "'edit', 'launch')\n"
+ " "
+ "(shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')"
+ )},
+ {"show-revs", opt_show_revs, 1,
+ N_("specify which collection of revisions to display\n"
+ " "
+ "('merged', 'eligible')")},
+ {"reintegrate", opt_reintegrate, 0,
+ N_("merge a branch back into its parent branch")},
+ {"strip", opt_strip, 1,
+ N_("number of leading path components to strip from\n"
+ " "
+ "paths parsed from the patch file. --strip 0\n"
+ " "
+ "is the default and leaves paths unmodified.\n"
+ " "
+ "--strip 1 would change the path\n"
+ " "
+ "'doc/fudge/crunchy.html' to 'fudge/crunchy.html'.\n"
+ " "
+ "--strip 2 would leave just 'crunchy.html'\n"
+ " "
+ "The expected component separator is '/' on all\n"
+ " "
+ "platforms. A leading '/' counts as one component.")},
+ {"show-copies-as-adds", opt_show_copies_as_adds, 0,
+ N_("don't diff copied or moved files with their source")},
+ {"ignore-keywords", opt_ignore_keywords, 0,
+ N_("don't expand keywords")},
+ {"reverse-diff", opt_reverse_diff, 0,
+ N_("apply the unidiff in reverse")},
+ {"ignore-whitespace", opt_ignore_whitespace, 0,
+ N_("ignore whitespace during pattern matching")},
+ {"diff", opt_diff, 0, N_("produce diff output")}, /* maps to show_diff */
+ {"internal-diff", opt_internal_diff, 0,
+ N_("override diff-cmd specified in config file")},
+ {"git", opt_use_git_diff_format, 0,
+ N_("use git's extended diff format")},
+ {"allow-mixed-revisions", opt_allow_mixed_revisions, 0,
+ N_("Allow merge into mixed-revision working copy.\n"
+ " "
+ "Use of this option is not recommended!\n"
+ " "
+ "Please run 'svn update' instead.")},
+
+ /* Long-opt Aliases
+ *
+ * These have NULL desriptions, but an option code that matches some
+ * other option (whose description should probably mention its aliases).
+ */
+
+ {"cl", opt_changelist, 1, NULL},
+
+ {0, 0, 0, 0},
+};
+
+
+
+/*** Command dispatch. ***/
+
+/* Our array of available subcommands.
+ *
+ * The entire list must be terminated with an entry of nulls.
+ *
+ * In most of the help text "PATH" is used where a working copy path is
+ * required, "URL" where a repository URL is required and "TARGET" when
+ * either a path or an url can be used. Hmm, should this be part of the
+ * help text?
+ */
+
+/* Options that apply to all commands. (While not every command may
+ currently require authentication or be interactive, allowing every
+ command to take these arguments allows scripts to just pass them
+ willy-nilly to every invocation of 'svn') . */
+const int svn_cl__global_options[] =
+{ opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive,
+ opt_trust_server_cert, opt_config_dir, opt_config_options, 0
+};
+
+/* Options for giving a log message. (Some of these also have other uses.)
+ */
+#define SVN_CL__LOG_MSG_OPTIONS 'm', 'F', \
+ opt_force_log, \
+ opt_editor_cmd, \
+ opt_encoding, \
+ opt_with_revprop
+
+const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
+{
+ { "add", svn_cl__add, {0}, N_
+ ("Put files and directories under version control, scheduling\n"
+ "them for addition to repository. They will be added in next commit.\n"
+ "usage: add PATH...\n"),
+ {opt_targets, 'N', opt_depth, 'q', opt_force, opt_no_ignore, opt_autoprops,
+ opt_no_autoprops, opt_parents },
+ {{opt_parents, N_("add intermediate parents")}} },
+
+ { "blame", svn_cl__blame, {"praise", "annotate", "ann"}, N_
+ ("Output the content of specified files or\n"
+ "URLs with revision and author information in-line.\n"
+ "usage: blame TARGET[@REV]...\n"
+ "\n"
+ " If specified, REV determines in which revision the target is first\n"
+ " looked up.\n"),
+ {'r', 'v', 'g', opt_incremental, opt_xml, 'x', opt_force} },
+
+ { "cat", svn_cl__cat, {0}, N_
+ ("Output the content of specified files or URLs.\n"
+ "usage: cat TARGET[@REV]...\n"
+ "\n"
+ " If specified, REV determines in which revision the target is first\n"
+ " looked up.\n"),
+ {'r'} },
+
+ { "changelist", svn_cl__changelist, {"cl"}, N_
+ ("Associate (or dissociate) changelist CLNAME with the named files.\n"
+ "usage: 1. changelist CLNAME PATH...\n"
+ " 2. changelist --remove PATH...\n"),
+ { 'q', 'R', opt_depth, opt_remove, opt_targets, opt_changelist} },
+
+ { "checkout", svn_cl__checkout, {"co"}, N_
+ ("Check out a working copy from a repository.\n"
+ "usage: checkout URL[@REV]... [PATH]\n"
+ "\n"
+ " If specified, REV determines in which revision the URL is first\n"
+ " looked up.\n"
+ "\n"
+ " If PATH is omitted, the basename of the URL will be used as\n"
+ " the destination. If multiple URLs are given each will be checked\n"
+ " out into a sub-directory of PATH, with the name of the sub-directory\n"
+ " being the basename of the URL.\n"
+ "\n"
+ " If --force is used, unversioned obstructing paths in the working\n"
+ " copy destination do not automatically cause the check out to fail.\n"
+ " If the obstructing path is the same type (file or directory) as the\n"
+ " corresponding path in the repository it becomes versioned but its\n"
+ " contents are left 'as-is' in the working copy. This means that an\n"
+ " obstructing directory's unversioned children may also obstruct and\n"
+ " become versioned. For files, any content differences between the\n"
+ " obstruction and the repository are treated like a local modification\n"
+ " to the working copy. All properties from the repository are applied\n"
+ " to the obstructing path.\n"
+ "\n"
+ " See also 'svn help update' for a list of possible characters\n"
+ " reporting the action taken.\n"),
+ {'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals} },
+
+ { "cleanup", svn_cl__cleanup, {0}, N_
+ ("Recursively clean up the working copy, removing locks, resuming\n"
+ "unfinished operations, etc.\n"
+ "usage: cleanup [WCPATH...]\n"),
+ {opt_merge_cmd} },
+
+ { "commit", svn_cl__commit, {"ci"},
+ N_("Send changes from your working copy to the repository.\n"
+ "usage: commit [PATH...]\n"
+ "\n"
+ " A log message must be provided, but it can be empty. If it is not\n"
+ " given by a --message or --file option, an editor will be started.\n"
+ " If any targets are (or contain) locked items, those will be\n"
+ " unlocked after a successful commit.\n"),
+ {'q', 'N', opt_depth, opt_targets, opt_no_unlock, SVN_CL__LOG_MSG_OPTIONS,
+ opt_changelist, opt_keep_changelists} },
+
+ { "copy", svn_cl__copy, {"cp"}, N_
+ ("Duplicate something in working copy or repository, remembering\n"
+ "history.\n"
+ "usage: copy SRC[@REV]... DST\n"
+ "\n"
+ "When copying multiple sources, they will be added as children of DST,\n"
+ "which must be a directory.\n"
+ "\n"
+ " SRC and DST can each be either a working copy (WC) path or URL:\n"
+ " WC -> WC: copy and schedule for addition (with history)\n"
+ " WC -> URL: immediately commit a copy of WC to URL\n"
+ " URL -> WC: check out URL into WC, schedule for addition\n"
+ " URL -> URL: complete server-side copy; used to branch and tag\n"
+ " All the SRCs must be of the same type.\n"
+ "\n"
+ "WARNING: For compatibility with previous versions of Subversion,\n"
+ "copies performed using two working copy paths (WC -> WC) will not\n"
+ "contact the repository. As such, they may not, by default, be able\n"
+ "to propagate merge tracking information from the source of the copy\n"
+ "to the destination.\n"),
+ {'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
+
+ { "delete", svn_cl__delete, {"del", "remove", "rm"}, N_
+ ("Remove files and directories from version control.\n"
+ "usage: 1. delete PATH...\n"
+ " 2. delete URL...\n"
+ "\n"
+ " 1. Each item specified by a PATH is scheduled for deletion upon\n"
+ " the next commit. Files, and directories that have not been\n"
+ " committed, are immediately removed from the working copy\n"
+ " unless the --keep-local option is given.\n"
+ " PATHs that are, or contain, unversioned or modified items will\n"
+ " not be removed unless the --force or --keep-local option is given.\n"
+ "\n"
+ " 2. Each item specified by a URL is deleted from the repository\n"
+ " via an immediate commit.\n"),
+ {opt_force, 'q', opt_targets, SVN_CL__LOG_MSG_OPTIONS, opt_keep_local} },
+
+ { "diff", svn_cl__diff, {"di"}, N_
+ ("Display the differences between two revisions or paths.\n"
+ "usage: 1. diff [-c M | -r N[:M]] [TARGET[@REV]...]\n"
+ " 2. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
+ " [PATH...]\n"
+ " 3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
+ "\n"
+ " 1. Display the changes made to TARGETs as they are seen in REV between\n"
+ " two revisions. TARGETs may be all working copy paths or all URLs.\n"
+ " If TARGETs are working copy paths, N defaults to BASE and M to the\n"
+ " working copy; if URLs, N must be specified and M defaults to HEAD.\n"
+ " The '-c M' option is equivalent to '-r N:M' where N = M-1.\n"
+ " Using '-c -M' does the reverse: '-r M:N' where N = M-1.\n"
+ "\n"
+ " 2. Display the differences between OLD-TGT as it was seen in OLDREV and\n"
+ " NEW-TGT as it was seen in NEWREV. PATHs, if given, are relative to\n"
+ " OLD-TGT and NEW-TGT and restrict the output to differences for those\n"
+ " paths. OLD-TGT and NEW-TGT may be working copy paths or URL[@REV].\n"
+ " NEW-TGT defaults to OLD-TGT if not specified. -r N makes OLDREV default\n"
+ " to N, -r N:M makes OLDREV default to N and NEWREV default to M.\n"
+ "\n"
+ " 3. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
+ "\n"
+ " Use just 'svn diff' to display local modifications in a working copy.\n"),
+ {'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd,
+ opt_internal_diff, 'x', opt_no_diff_deleted, opt_show_copies_as_adds,
+ opt_notice_ancestry, opt_summarize, opt_changelist, opt_force, opt_xml,
+ opt_use_git_diff_format} },
+ { "export", svn_cl__export, {0}, N_
+ ("Create an unversioned copy of a tree.\n"
+ "usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
+ " 2. export [-r REV] PATH1[@PEGREV] [PATH2]\n"
+ "\n"
+ " 1. Exports a clean directory tree from the repository specified by\n"
+ " URL, at revision REV if it is given, otherwise at HEAD, into\n"
+ " PATH. If PATH is omitted, the last component of the URL is used\n"
+ " for the local directory name.\n"
+ "\n"
+ " 2. Exports a clean directory tree from the working copy specified by\n"
+ " PATH1, at revision REV if it is given, otherwise at WORKING, into\n"
+ " PATH2. If PATH2 is omitted, the last component of the PATH1 is used\n"
+ " for the local directory name. If REV is not specified, all local\n"
+ " changes will be preserved. Files not under version control will\n"
+ " not be copied.\n"
+ "\n"
+ " If specified, PEGREV determines in which revision the target is first\n"
+ " looked up.\n"),
+ {'r', 'q', 'N', opt_depth, opt_force, opt_native_eol, opt_ignore_externals,
+ opt_ignore_keywords} },
+
+ { "help", svn_cl__help, {"?", "h"}, N_
+ ("Describe the usage of this program or its subcommands.\n"
+ "usage: help [SUBCOMMAND...]\n"),
+ {0} },
+ /* This command is also invoked if we see option "--help", "-h" or "-?". */
+
+ { "import", svn_cl__import, {0}, N_
+ ("Commit an unversioned file or tree into the repository.\n"
+ "usage: import [PATH] URL\n"
+ "\n"
+ " Recursively commit a copy of PATH to URL.\n"
+ " If PATH is omitted '.' is assumed.\n"
+ " Parent directories are created as necessary in the repository.\n"
+ " If PATH is a directory, the contents of the directory are added\n"
+ " directly under URL.\n"
+ " Unversionable items such as device files and pipes are ignored\n"
+ " if --force is specified.\n"),
+ {'q', 'N', opt_depth, opt_autoprops, opt_force, opt_no_autoprops,
+ SVN_CL__LOG_MSG_OPTIONS, opt_no_ignore} },
+
+ { "info", svn_cl__info, {0}, N_
+ ("Display information about a local or remote item.\n"
+ "usage: info [TARGET[@REV]...]\n"
+ "\n"
+ " Print information about each TARGET (default: '.').\n"
+ " TARGET may be either a working-copy path or URL. If specified, REV\n"
+ " determines in which revision the target is first looked up.\n"),
+ {'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml, opt_changelist}
+ },
+
+ { "list", svn_cl__list, {"ls"}, N_
+ ("List directory entries in the repository.\n"
+ "usage: list [TARGET[@REV]...]\n"
+ "\n"
+ " List each TARGET file and the contents of each TARGET directory as\n"
+ " they exist in the repository. If TARGET is a working copy path, the\n"
+ " corresponding repository URL will be used. If specified, REV determines\n"
+ " in which revision the target is first looked up.\n"
+ "\n"
+ " The default TARGET is '.', meaning the repository URL of the current\n"
+ " working directory.\n"
+ "\n"
+ " With --verbose, the following fields will be shown for each item:\n"
+ "\n"
+ " Revision number of the last commit\n"
+ " Author of the last commit\n"
+ " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
+ " Size (in bytes)\n"
+ " Date and time of the last commit\n"),
+ {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml} },
+
+ { "lock", svn_cl__lock, {0}, N_
+ ("Lock working copy paths or URLs in the repository, so that\n"
+ "no other user can commit changes to them.\n"
+ "usage: lock TARGET...\n"
+ "\n"
+ " Use --force to steal the lock from another user or working copy.\n"),
+ { opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force },
+ {{'F', N_("read lock comment from file ARG")},
+ {'m', N_("specify lock comment ARG")},
+ {opt_force_log, N_("force validity of lock comment source")}} },
+
+ { "log", svn_cl__log, {0}, N_
+ ("Show the log messages for a set of revision(s) and/or path(s).\n"
+ "usage: 1. log [PATH][@REV]\n"
+ " 2. log URL[@REV] [PATH...]\n"
+ "\n"
+ " 1. Print the log messages for the URL corresponding to PATH\n"
+ " (default: '.'). If specified, REV is the revision in which the\n"
+ " URL is first looked up, and the default revision range is REV:1.\n"
+ " If REV is not specified, the default revision range is BASE:1,\n"
+ " since the URL might not exist in the HEAD revision.\n"
+ "\n"
+ " 2. Print the log messages for the PATHs (default: '.') under URL.\n"
+ " If specified, REV is the revision in which the URL is first\n"
+ " looked up, and the default revision range is REV:1; otherwise,\n"
+ " the URL is looked up in HEAD, and the default revision range is\n"
+ " HEAD:1.\n"
+ "\n"
+ " Multiple '-c' or '-r' options may be specified (but not a\n"
+ " combination of '-c' and '-r' options), and mixing of forward and\n"
+ " reverse ranges is allowed.\n"
+ "\n"
+ " With -v, also print all affected paths with each log message.\n"
+ " With -q, don't print the log message body itself (note that this is\n"
+ " compatible with -v).\n"
+ "\n"
+ " Each log message is printed just once, even if more than one of the\n"
+ " affected paths for that revision were explicitly requested. Logs\n"
+ " follow copy history by default. Use --stop-on-copy to disable this\n"
+ " behavior, which can be useful for determining branchpoints.\n"
+ "\n"
+ " The --depth option is only valid in combination with the --diff option\n"
+ " and limits the scope of the displayed diff to the specified depth.\n"
+
+ "\n"
+ " Examples:\n"
+ " svn log\n"
+ " svn log foo.c\n"
+ " svn log bar.c@42\n"
+ " svn log http://www.example.com/repo/project/foo.c\n"
+ " svn log http://www.example.com/repo/project foo.c bar.c\n"
+ " svn log http://www.example.com/repo/project@50 foo.c bar.c\n"),
+ {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
+ opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,
+ opt_depth, opt_diff, opt_diff_cmd, opt_internal_diff, 'x'},
+ {{opt_with_revprop, N_("retrieve revision property ARG")},
+ {'c', N_("the change made in revision ARG")}} },
+
+ { "merge", svn_cl__merge, {0}, N_
+ ( /* For this large section, let's keep it unindented for easier
+ * viewing/editing. It has been vim-treated with a textwidth=75 and 'gw'
+ * (with quotes and newlines removed). */
+"Merge changes into a working copy.\n"
+"usage: 1. merge SOURCE[@REV] [TARGET_WCPATH]\n"
+" (the 'sync' merge)\n"
+" 2. merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
+" (the 'cherry-pick' merge)\n"
+" 3. merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
+" (the 'reintegrate' merge)\n"
+" 4. merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
+" (the '2-URL' merge)\n"
+"\n"
+" 1. This form is called a 'sync' (or 'catch-up') merge:\n"
+"\n"
+" svn merge SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+" A sync merge is used to fetch all the latest changes made on a parent\n"
+" branch. In other words, the target branch has originally been created\n"
+" by copying the source branch, and any changes committed on the source\n"
+" branch since branching are applied to the target branch. This uses\n"
+" merge tracking to skip all those revisions that have already been\n"
+" merged, so a sync merge can be repeated periodically to stay up-to-\n"
+" date with the source branch.\n"
+"\n"
+" SOURCE specifies the branch from where the changes will be pulled, and\n"
+" TARGET_WCPATH specifies a working copy of the target branch to which\n"
+" the changes will be applied. Normally SOURCE and TARGET_WCPATH should\n"
+" each correspond to the root of a branch. (If you want to merge only a\n"
+" subtree, then the subtree path must be included in both SOURCE and\n"
+" TARGET_WCPATH; this is discouraged, to avoid subtree mergeinfo.)\n"
+"\n"
+" SOURCE is usually a URL. The optional '@REV' specifies both the peg\n"
+" revision of the URL and the latest revision that will be considered\n"
+" for merging; if REV is not specified, the HEAD revision is assumed. If\n"
+" SOURCE is a working copy path, the corresponding URL of the path is\n"
+" used, and the default value of 'REV' is the base revision (usually the\n"
+" revision last updated to).\n"
+"\n"
+" TARGET_WCPATH is a working copy path; if omitted, '.' is assumed.\n"
+"\n"
+" - Sync Merge Example -\n"
+"\n"
+" A feature is being developed on a branch called 'feature', which has\n"
+" originally been a copy of trunk. The feature branch has been regularly\n"
+" synced with trunk to keep up with the changes made there. The previous\n"
+" sync merges are not shown on this diagram, and the last of them was\n"
+" done when HEAD was r100. Currently, HEAD is r200.\n"
+"\n"
+" feature +------------------------o-----\n"
+" / ^\n"
+" / ............ |\n"
+" / . . /\n"
+" trunk ------+------------L--------------R------\n"
+" r100 r200\n"
+"\n"
+" Subversion will locate all the changes on 'trunk' that have not yet\n"
+" been merged into the 'feature' branch. In this case that is a single\n"
+" range, r100:200. In the diagram above, L marks the left side\n"
+" (trunk@100) and R marks the right side (trunk@200) of the merge. The\n"
+" difference between L and R will be applied to the target working copy\n"
+" path. In this case, the working copy is a clean checkout of the entire\n"
+" 'feature' branch.\n"
+"\n"
+" To perform this sync merge, have a clean working copy of the feature\n"
+" branch and run the following command in its top-level directory:\n"
+"\n"
+" svn merge ^/trunk\n"
+"\n"
+" Note that the merge is now only in your local working copy and still\n"
+" needs to be committed to the repository so that it can be seen by\n"
+" others. You can review the changes and you may have to resolve\n"
+" conflicts before you commit the merge.\n"
+"\n"
+"\n"
+" 2. This form is called a 'cherry-pick' merge:\n"
+"\n"
+" svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+" A cherry-pick merge is used to merge specific revisions (or revision\n"
+" ranges) from one branch to another. By default, this uses merge\n"
+" tracking to automatically skip any revisions that have already been\n"
+" merged to the target; you can use the --ignore-ancestry option to\n"
+" disable such skipping.\n"
+"\n"
+" SOURCE is usually a URL. The optional '@REV' specifies only the peg\n"
+" revision of the URL and does not affect the merge range; if REV is not\n"
+" specified, the HEAD revision is assumed. If SOURCE is a working copy\n"
+" path, the corresponding URL of the path is used, and the default value\n"
+" of 'REV' is the base revision (usually the revision last updated to).\n"
+"\n"
+" TARGET_WCPATH is a working copy path; if omitted, '.' is assumed.\n"
+"\n"
+" The revision ranges to be merged are specified by the '-r' and/or '-c'\n"
+" options. '-r N:M' refers to the difference in the history of the\n"
+" source branch between revisions N and M. You can use '-c M' to merge\n"
+" single revisions: '-c M' is equivalent to '-r <M-1>:M'. Each such\n"
+" difference is applied to TARGET_WCPATH.\n"
+"\n"
+" If the mergeinfo in TARGET_WCPATH indicates that revisions within the\n"
+" range were already merged, changes made in those revisions are not\n"
+" merged again. If needed, the range is broken into multiple sub-ranges,\n"
+" and each sub-range is merged separately.\n"
+"\n"
+" A 'reverse range' can be used to undo changes. For example, when\n"
+" source and target refer to the same branch, a previously committed\n"
+" revision can be 'undone'. In a reverse range, N is greater than M in\n"
+" '-r N:M', or the '-c' option is used with a negative number: '-c -M'\n"
+" is equivalent to '-r M:<M-1>'.\n"
+"\n"
+" Multiple '-c' and/or '-r' options may be specified and mixing of\n"
+" forward and reverse ranges is allowed.\n"
+"\n"
+" - Cherry-pick Merge Example -\n"
+"\n"
+" A bug has been fixed on trunk in revision 50. This fix needs to\n"
+" be merged from trunk onto the release branch.\n"
+"\n"
+" 1.x-release +-----------------------o-----\n"
+" / ^\n"
+" / |\n"
+" / |\n"
+" trunk ------+--------------------------LR-----\n"
+" r50\n"
+"\n"
+" In the above diagram, L marks the left side (trunk@49) and R marks the\n"
+" right side (trunk@50) of the merge. The difference between the left\n"
+" and right side is applied to the target working copy path.\n"
+"\n"
+" Note that the difference between revision 49 and 50 is exactly those\n"
+" changes that were committed in revision 50, not including changes\n"
+" committed in revision 49.\n"
+"\n"
+" To perform the merge, have a clean working copy of the release branch\n"
+" and run the following command in its top-level directory; remember\n"
+" that the default target is '.':\n"
+"\n"
+" svn merge -c50 ^/trunk\n"
+"\n"
+" You can also cherry-pick several revisions and/or revision ranges:\n"
+"\n"
+" svn merge -c50,54,60 -r65:68 ^/trunk\n"
+"\n"
+"\n"
+" 3. This form is called a 'reintegrate merge':\n"
+"\n"
+" svn merge --reintegrate SOURCE[@REV] [TARGET_WCPATH]\n"
+"\n"
+" In a reintegrate merge, an (e.g. feature) branch is merged back to its\n"
+" originating branch. In other words, the source branch has originally\n"
+" been created by copying the target branch, development has concluded\n"
+" on the source branch and it should now be merged back into the target\n"
+" branch.\n"
+" \n"
+" SOURCE is the URL of a branch to be merged back. If REV is specified,\n"
+" it is used as the peg revision for SOURCE; if REV is not specified,\n"
+" the HEAD revision is assumed.\n"
+"\n"
+" TARGET_WCPATH is a working copy of the branch the changes will be\n"
+" applied to.\n"
+"\n"
+" - Reintegrate Merge Example -\n"
+"\n"
+" A feature has been developed on a branch called 'feature'. The feature\n"
+" branch started as a copy of trunk@W. Work on the feature has completed\n"
+" and it should be merged back into the trunk.\n"
+"\n"
+" The feature branch was last synced with trunk up to revision X. So the\n"
+" difference between trunk@X and feature@HEAD contains the complete set\n"
+" of changes that implement the feature, and no other changes. These\n"
+" changes are applied to trunk.\n"
+"\n"
+" feature +--------------------------------R\n"
+" / . \\\n"
+" / ............. \\\n"
+" / . v\n"
+" trunk ------+--------------------L------------------o\n"
+" rW rX\n"
+"\n"
+" In the diagram above, L marks the left side (trunk@X) and R marks the\n"
+" right side (feature@HEAD) of the merge. The difference between the\n"
+" left and right side is merged into trunk, the target.\n"
+"\n"
+" To perform the merge, have a clean working copy of trunk and run the\n"
+" following command in its top-level directory:\n"
+"\n"
+" svn merge --reintegrate ^/feature\n"
+"\n"
+" To prevent unnecessary merge conflicts, a reintegrate merge requires\n"
+" that TARGET_WCPATH is not a mixed-revision working copy, has no local\n"
+" modifications, and has no switched subtrees.\n"
+"\n"
+" A reintegrate merge also requires that the source branch is coherently\n"
+" synced with the target -- in the above example, this means that all\n"
+" revisions between the branch point W and the last merged revision X\n"
+" are merged to the feature branch, so that there are no unmerged\n"
+" revisions in-between.\n"
+"\n"
+" After the reintegrate merge, the feature branch cannot be synced to\n"
+" the trunk again without merge conflicts. If further work must be done\n"
+" on the feature branch, it should be deleted and then re-created.\n"
+"\n"
+"\n"
+" 4. This form is called a '2-URL merge':\n"
+"\n"
+" svn merge SOURCE1[@N] SOURCE2[@M] [TARGET_WCPATH]\n"
+"\n"
+" Two source URLs are specified, together with two revisions N and M.\n"
+" The two sources are compared at the specified revisions, and the\n"
+" difference is applied to TARGET_WCPATH, which is a path to a working\n"
+" copy of another branch. The three branches involved can be completely\n"
+" unrelated.\n"
+"\n"
+" You should use this merge variant only if the other variants do not\n"
+" apply to your situation, as this variant can be quite complex to\n"
+" master.\n"
+"\n"
+" If TARGET_WCPATH is omitted, a default value of '.' is assumed.\n"
+" However, in the special case where both sources refer to a file node\n"
+" with the same basename and a similarly named file is also found within\n"
+" '.', the differences will be applied to that local file. The source\n"
+" revisions default to HEAD if omitted.\n"
+"\n"
+" The sources can also be specified as working copy paths, in which case\n"
+" the URLs of the merge sources are derived from the working copies.\n"
+"\n"
+" - 2-URL Merge Example -\n"
+"\n"
+" Two features have been developed on separate branches called 'foo' and\n"
+" 'bar'. It has since become clear that 'bar' should be combined with\n"
+" the 'foo' branch for further development before reintegration.\n"
+"\n"
+" Although both feature branches originate from trunk, they are not\n"
+" directly related -- one is not a direct copy of the other. A 2-URL\n"
+" merge is necessary.\n"
+"\n"
+" The 'bar' branch has been synced with trunk up to revision 500.\n"
+" (If this revision number is not known, it can be located using the\n"
+" 'svn log' and/or 'svn mergeinfo' commands.)\n"
+" The difference between trunk@500 and bar@HEAD contains the complete\n"
+" set of changes related to feature 'bar', and no other changes. These\n"
+" changes are applied to the 'foo' branch.\n"
+"\n"
+" foo +-----------------------------------o\n"
+" / ^\n"
+" / /\n"
+" / r500 /\n"
+" trunk ------+------+-----------------L---------> /\n"
+" \\ . /\n"
+" \\ ............ /\n"
+" \\ . /\n"
+" bar +-----------------------------------R\n"
+"\n"
+" In the diagram above, L marks the left side (trunk@500) and R marks\n"
+" the right side (bar@HEAD) of the merge. The difference between the\n"
+" left and right side is applied to the target working copy path, in\n"
+" this case a working copy of the 'foo' branch.\n"
+"\n"
+" To perform the merge, have a clean working copy of the 'foo' branch\n"
+" and run the following command in its top-level directory:\n"
+"\n"
+" svn merge ^/trunk@500 ^/bar\n"
+"\n"
+" The exact changes applied by a 2-URL merge can be previewed with svn's\n"
+" diff command, which is a good idea to verify if you do not have the\n"
+" luxury of a clean working copy to merge to. In this case:\n"
+"\n"
+" svn diff ^/trunk@500 ^/bar@HEAD\n"
+"\n"
+"\n"
+" The following applies to all types of merges:\n"
+"\n"
+" To prevent unnecessary merge conflicts, svn merge requires that\n"
+" TARGET_WCPATH is not a mixed-revision working copy. Running 'svn update'\n"
+" before starting a merge ensures that all items in the working copy are\n"
+" based on the same revision.\n"
+"\n"
+" If possible, you should have no local modifications in the merge's target\n"
+" working copy prior to the merge, to keep things simpler. It will be\n"
+" easier to revert the merge and to understand the branch's history.\n"
+"\n"
+" Switched sub-paths should also be avoided during merging, as they may\n"
+" cause incomplete merges and create subtree mergeinfo.\n"
+"\n"
+" For each merged item a line will be printed with characters reporting the\n"
+" action taken. These characters have the following meaning:\n"
+"\n"
+" A Added\n"
+" D Deleted\n"
+" U Updated\n"
+" C Conflict\n"
+" G Merged\n"
+" E Existed\n"
+" R Replaced\n"
+"\n"
+" Characters in the first column report about the item itself.\n"
+" Characters in the second column report about properties of the item.\n"
+" A 'C' in the third column indicates a tree conflict, while a 'C' in\n"
+" the first and second columns indicate textual conflicts in files\n"
+" and in property values, respectively.\n"
+"\n"
+" - Merge Tracking -\n"
+"\n"
+" Subversion uses the svn:mergeinfo property to track merge history. This\n"
+" property is considered at the start of a merge to determine what to merge\n"
+" and it is updated at the conclusion of the merge to describe the merge\n"
+" that took place. Mergeinfo is used only if the two sources are on the\n"
+" same line of history -- if the first source is an ancestor of the second,\n"
+" or vice-versa (i.e. if one has originally been created by copying the\n"
+" other). This is verified and enforced when using sync merges and\n"
+" reintegrate merges.\n"
+"\n"
+" The --ignore-ancestry option prevents merge tracking and thus ignores\n"
+" mergeinfo, neither considering it nor recording it.\n"
+"\n"
+" - Merging from foreign repositories -\n"
+"\n"
+" Subversion does support merging from foreign repositories.\n"
+" While all merge source URLs must point to the same repository, the merge\n"
+" target working copy may come from a different repository than the source.\n"
+" However, there are some caveats. Most notably, copies made in the\n"
+" merge source will be transformed into plain additions in the merge\n"
+" target. Also, merge-tracking is not supported for merges from foreign\n"
+" repositories.\n"),
+ {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd,
+ opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate,
+ opt_allow_mixed_revisions} },
+
+ { "mergeinfo", svn_cl__mergeinfo, {0}, N_
+ ("Display merge-related information.\n"
+ "usage: mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
+ "\n"
+ " Display information related to merges (or potential merges) between\n"
+ " SOURCE and TARGET (default: '.'). Display the type of information\n"
+ " specified by the --show-revs option. If --show-revs isn't passed,\n"
+ " it defaults to --show-revs='merged'.\n"
+ "\n"
+ " The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"),
+ {'r', 'R', opt_depth, opt_show_revs} },
+
+ { "mkdir", svn_cl__mkdir, {0}, N_
+ ("Create a new directory under version control.\n"
+ "usage: 1. mkdir PATH...\n"
+ " 2. mkdir URL...\n"
+ "\n"
+ " Create version controlled directories.\n"
+ "\n"
+ " 1. Each directory specified by a working copy PATH is created locally\n"
+ " and scheduled for addition upon the next commit.\n"
+ "\n"
+ " 2. Each directory specified by a URL is created in the repository via\n"
+ " an immediate commit.\n"
+ "\n"
+ " In both cases, all the intermediate directories must already exist,\n"
+ " unless the --parents option is given.\n"),
+ {'q', opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
+
+ { "move", svn_cl__move, {"mv", "rename", "ren"}, N_
+ ("Move and/or rename something in working copy or repository.\n"
+ "usage: move SRC... DST\n"
+ "\n"
+ "When moving multiple sources, they will be added as children of DST,\n"
+ "which must be a directory.\n"
+ "\n"
+ " Note: this subcommand is equivalent to a 'copy' and 'delete'.\n"
+ " Note: the --revision option has no use and is deprecated.\n"
+ "\n"
+ " SRC and DST can both be working copy (WC) paths or URLs:\n"
+ " WC -> WC: move and schedule for addition (with history)\n"
+ " URL -> URL: complete server-side rename.\n"
+ " All the SRCs must be of the same type.\n"),
+ {'r', 'q', opt_force, opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
+
+ { "patch", svn_cl__patch, {0}, N_
+ ("Apply a patch to a working copy.\n"
+ "usage: patch PATCHFILE [WCPATH]\n"
+ "\n"
+ " Apply a unidiff patch in PATCHFILE to the working copy WCPATH.\n"
+ " If WCPATH is omitted, '.' is assumed.\n"
+ "\n"
+ " A unidiff patch suitable for application to a working copy can be\n"
+ " produced with the 'svn diff' command or third-party diffing tools.\n"
+ " Any non-unidiff content of PATCHFILE is ignored.\n"
+ "\n"
+ " Changes listed in the patch will either be applied or rejected.\n"
+ " If a change does not match at its exact line offset, it may be applied\n"
+ " earlier or later in the file if a match is found elsewhere for the\n"
+ " surrounding lines of context provided by the patch.\n"
+ " A change may also be applied with fuzz, which means that one\n"
+ " or more lines of context are ignored when matching the change.\n"
+ " If no matching context can be found for a change, the change conflicts\n"
+ " and will be written to a reject file with the extension .svnpatch.rej.\n"
+ "\n"
+ " For each patched file a line will be printed with characters reporting\n"
+ " the action taken. These characters have the following meaning:\n"
+ "\n"
+ " A Added\n"
+ " D Deleted\n"
+ " U Updated\n"
+ " C Conflict\n"
+ " G Merged (with local uncommitted changes)\n"
+ "\n"
+ " Changes applied with an offset or fuzz are reported on lines starting\n"
+ " with the '>' symbol. You should review such changes carefully.\n"
+ "\n"
+ " If the patch removes all content from a file, that file is scheduled\n"
+ " for deletion. If the patch creates a new file, that file is scheduled\n"
+ " for addition. Use 'svn revert' to undo deletions and additions you\n"
+ " do not agree with.\n"
+ "\n"
+ " Hint: If the patch file was created with Subversion, it will contain\n"
+ " the number of a revision N the patch will cleanly apply to\n"
+ " (look for lines like \"--- foo/bar.txt (revision N)\").\n"
+ " To avoid rejects, first update to the revision N using \n"
+ " 'svn update -r N', apply the patch, and then update back to the\n"
+ " HEAD revision. This way, conflicts can be resolved interactively.\n"
+ ),
+ {'q', opt_dry_run, opt_strip, opt_reverse_diff,
+ opt_ignore_whitespace} },
+
+ { "propdel", svn_cl__propdel, {"pdel", "pd"}, N_
+ ("Remove a property from files, dirs, or revisions.\n"
+ "usage: 1. propdel PROPNAME [PATH...]\n"
+ " 2. propdel PROPNAME --revprop -r REV [TARGET]\n"
+ "\n"
+ " 1. Removes versioned props in working copy.\n"
+ " 2. Removes unversioned remote prop on repos revision.\n"
+ " TARGET only determines which repository to access.\n"),
+ {'q', 'R', opt_depth, 'r', opt_revprop, opt_changelist} },
+
+ { "propedit", svn_cl__propedit, {"pedit", "pe"}, N_
+ ("Edit a property with an external editor.\n"
+ "usage: 1. propedit PROPNAME TARGET...\n"
+ " 2. propedit PROPNAME --revprop -r REV [TARGET]\n"
+ "\n"
+ " 1. Edits versioned prop in working copy or repository.\n"
+ " 2. Edits unversioned remote prop on repos revision.\n"
+ " TARGET only determines which repository to access.\n"
+ "\n"
+ "See 'svn help propset' for more on setting properties.\n"),
+ {'r', opt_revprop, SVN_CL__LOG_MSG_OPTIONS, opt_force} },
+
+ { "propget", svn_cl__propget, {"pget", "pg"}, N_
+ ("Print the value of a property on files, dirs, or revisions.\n"
+ "usage: 1. propget PROPNAME [TARGET[@REV]...]\n"
+ " 2. propget PROPNAME --revprop -r REV [TARGET]\n"
+ "\n"
+ " 1. Prints versioned props. If specified, REV determines in which\n"
+ " revision the target is first looked up.\n"
+ " 2. Prints unversioned remote prop on repos revision.\n"
+ " TARGET only determines which repository to access.\n"
+ "\n"
+ " By default, this subcommand will add an extra newline to the end\n"
+ " of the property values so that the output looks pretty. Also,\n"
+ " whenever there are multiple paths involved, each property value\n"
+ " is prefixed with the path with which it is associated. Use the\n"
+ " --strict option to disable these beautifications (useful when\n"
+ " redirecting a binary property value to a file, but available only\n"
+ " if you supply a single TARGET to a non-recursive propget operation).\n"),
+ {'v', 'R', opt_depth, 'r', opt_revprop, opt_strict, opt_xml,
+ opt_changelist } },
+
+ { "proplist", svn_cl__proplist, {"plist", "pl"}, N_
+ ("List all properties on files, dirs, or revisions.\n"
+ "usage: 1. proplist [TARGET[@REV]...]\n"
+ " 2. proplist --revprop -r REV [TARGET]\n"
+ "\n"
+ " 1. Lists versioned props. If specified, REV determines in which\n"
+ " revision the target is first looked up.\n"
+ " 2. Lists unversioned remote props on repos revision.\n"
+ " TARGET only determines which repository to access.\n"),
+ {'v', 'R', opt_depth, 'r', 'q', opt_revprop, opt_xml, opt_changelist } },
+
+ { "propset", svn_cl__propset, {"pset", "ps"}, N_
+ ("Set the value of a property on files, dirs, or revisions.\n"
+ "usage: 1. propset PROPNAME PROPVAL PATH...\n"
+ " 2. propset PROPNAME --revprop -r REV PROPVAL [TARGET]\n"
+ "\n"
+ " 1. Changes a versioned file or directory property in a working copy.\n"
+ " 2. Changes an unversioned property on a repository revision.\n"
+ " (TARGET only determines which repository to access.)\n"
+ "\n"
+ " The value may be provided with the --file option instead of PROPVAL.\n"
+ "\n"
+ " Note: svn recognizes the following special versioned properties\n"
+ " but will store any arbitrary properties set:\n"
+ " svn:ignore - A newline separated list of file glob patterns to ignore.\n"
+ " svn:keywords - Keywords to be expanded. Valid keywords are:\n"
+ " URL, HeadURL - The URL for the head version of the object.\n"
+ " Author, LastChangedBy - The last person to modify the file.\n"
+ " Date, LastChangedDate - The date/time the object was last modified.\n"
+ " Rev, Revision, - The last revision the object changed.\n"
+ " LastChangedRevision\n"
+ " Id - A compressed summary of the previous\n"
+ " 4 keywords.\n"
+ " Header - Similar to Id but includes the full URL.\n"
+ " svn:executable - If present, make the file executable. Use\n"
+ " 'svn propdel svn:executable PATH...' to clear.\n"
+ " svn:eol-style - One of 'native', 'LF', 'CR', 'CRLF'.\n"
+ " svn:mime-type - The mimetype of the file. Used to determine\n"
+ " whether to merge the file, and how to serve it from Apache.\n"
+ " A mimetype beginning with 'text/' (or an absent mimetype) is\n"
+ " treated as text. Anything else is treated as binary.\n"
+ " svn:externals - A newline separated list of module specifiers,\n"
+ " each of which consists of a URL and a relative directory path,\n"
+ " similar to the syntax of the 'svn checkout' command:\n"
+ " http://example.com/repos/zig foo/bar\n"
+ " A revision to check out can optionally be specified to pin the\n"
+ " external to a known revision:\n"
+ " -r25 http://example.com/repos/zig foo/bar\n"
+ " To unambiguously identify an element at a path which has been\n"
+ " deleted (possibly even deleted multiple times in its history),\n"
+ " an optional peg revision can be appended to the URL:\n"
+ " -r25 http://example.com/repos/zig@42 foo/bar\n"
+ " Relative URLs are indicated by starting the URL with one\n"
+ " of the following strings:\n"
+ " ../ to the parent directory of the extracted external\n"
+ " ^/ to the repository root\n"
+ " // to the scheme\n"
+ " / to the server root\n"
+ " The ambiguous format 'relative_path relative_path' is taken as\n"
+ " 'relative_url relative_path' with peg revision support.\n"
+ " Lines in externals definitions starting with the '#' character\n"
+ " are considered comments and are ignored.\n"
+ " Subversion 1.4 and earlier only support the following formats\n"
+ " where peg revisions can only be specified using a -r modifier\n"
+ " and where URLs cannot be relative:\n"
+ " foo http://example.com/repos/zig\n"
+ " foo/bar -r 1234 http://example.com/repos/zag\n"
+ " Use of these formats is discouraged. They should only be used if\n"
+ " interoperability with 1.4 clients is desired.\n"
+ " svn:needs-lock - If present, indicates that the file should be locked\n"
+ " before it is modified. Makes the working copy file read-only\n"
+ " when it is not locked. Use 'svn propdel svn:needs-lock PATH...'\n"
+ " to clear.\n"
+ "\n"
+ " The svn:keywords, svn:executable, svn:eol-style, svn:mime-type and\n"
+ " svn:needs-lock properties cannot be set on a directory. A non-recursive\n"
+ " attempt will fail, and a recursive attempt will set the property\n"
+ " only on the file children of the directory.\n"),
+ {'F', opt_encoding, 'q', 'r', opt_targets, 'R', opt_depth, opt_revprop,
+ opt_force, opt_changelist },
+ {{'F', N_("read property value from file ARG")}} },
+
+ { "relocate", svn_cl__relocate, {0}, N_
+ ("Relocate the working copy to point to a different repository root URL.\n"
+ "usage: 1. relocate FROM-PREFIX TO-PREFIX [PATH...]\n"
+ " 2. relocate TO-URL [PATH]\n"
+ "\n"
+ " Rewrite working copy URL metadata to reflect a syntactic change only.\n"
+ " This is used when a repository's root URL changes (such as a scheme\n"
+ " or hostname change) but your working copy still reflects the same\n"
+ " directory within the same repository.\n"
+ "\n"
+ " 1. FROM-PREFIX and TO-PREFIX are initial substrings of the working\n"
+ " copy's current and new URLs, respectively. (You may specify the\n"
+ " complete old and new URLs if you wish.) Use 'svn info' to determine\n"
+ " the current working copy URL.\n"
+ "\n"
+ " 2. TO-URL is the (complete) new repository URL to use for PATH.\n"
+ "\n"
+ " Examples:\n"
+ " svn relocate http:// svn:// project1 project2\n"
+ " svn relocate http://www.example.com/repo/project \\\n"
+ " svn://svn.example.com/repo/project\n"),
+ {opt_ignore_externals} },
+
+ { "resolve", svn_cl__resolve, {0}, N_
+ ("Resolve conflicts on working copy files or directories.\n"
+ "usage: resolve --accept=ARG [PATH...]\n"
+ "\n"
+ " Note: the --accept option is currently required.\n"),
+ {opt_targets, 'R', opt_depth, 'q', opt_accept},
+ {{opt_accept, N_("specify automatic conflict resolution source\n"
+ " "
+ "('base', 'working', 'mine-conflict',\n"
+ " "
+ "'theirs-conflict', 'mine-full', 'theirs-full')")}} },
+
+ { "resolved", svn_cl__resolved, {0}, N_
+ ("Remove 'conflicted' state on working copy files or directories.\n"
+ "usage: resolved PATH...\n"
+ "\n"
+ " Note: this subcommand does not semantically resolve conflicts or\n"
+ " remove conflict markers; it merely removes the conflict-related\n"
+ " artifact files and allows PATH to be committed again. It has been\n"
+ " deprecated in favor of running 'svn resolve --accept working'.\n"),
+ {opt_targets, 'R', opt_depth, 'q'} },
+
+ { "revert", svn_cl__revert, {0}, N_
+ ("Restore pristine working copy file (undo most local edits).\n"
+ "usage: revert PATH...\n"
+ "\n"
+ " Note: this subcommand does not require network access, and resolves\n"
+ " any conflicted states.\n"),
+ {opt_targets, 'R', opt_depth, 'q', opt_changelist} },
+
+ { "status", svn_cl__status, {"stat", "st"}, N_
+ ("Print the status of working copy files and directories.\n"
+ "usage: status [PATH...]\n"
+ "\n"
+ " With no args, print only locally modified items (no network access).\n"
+ " With -q, print only summary information about locally modified items.\n"
+ " With -u, add working revision and server out-of-date information.\n"
+ " With -v, print full revision information on every item.\n"
+ "\n"
+ " The first seven columns in the output are each one character wide:\n"
+ " First column: Says if item was added, deleted, or otherwise changed\n"
+ " ' ' no modifications\n"
+ " 'A' Added\n"
+ " 'C' Conflicted\n"
+ " 'D' Deleted\n"
+ " 'I' Ignored\n"
+ " 'M' Modified\n"
+ " 'R' Replaced\n"
+ " 'X' an unversioned directory created by an externals definition\n"
+ " '?' item is not under version control\n"
+ " '!' item is missing (removed by non-svn command) or incomplete\n"
+ " '~' versioned item obstructed by some item of a different kind\n"
+ " Second column: Modifications of a file's or directory's properties\n"
+ " ' ' no modifications\n"
+ " 'C' Conflicted\n"
+ " 'M' Modified\n"
+ " Third column: Whether the working copy directory is locked\n"
+ " ' ' not locked\n"
+ " 'L' locked\n"
+ " Fourth column: Scheduled commit will contain addition-with-history\n"
+ " ' ' no history scheduled with commit\n"
+ " '+' history scheduled with commit\n"
+ " Fifth column: Whether the item is switched or a file external\n"
+ " ' ' normal\n"
+ " 'S' the item has a Switched URL relative to the parent\n"
+ " 'X' a versioned file created by an eXternals definition\n"
+ " Sixth column: Repository lock token\n"
+ " (without -u)\n"
+ " ' ' no lock token\n"
+ " 'K' lock token present\n"
+ " (with -u)\n"
+ " ' ' not locked in repository, no lock token\n"
+ " 'K' locked in repository, lock toKen present\n"
+ " 'O' locked in repository, lock token in some Other working copy\n"
+ " 'T' locked in repository, lock token present but sTolen\n"
+ " 'B' not locked in repository, lock token present but Broken\n"
+ " Seventh column: Whether the item is the victim of a tree conflict\n"
+ " ' ' normal\n"
+ " 'C' tree-Conflicted\n"
+ " If the item is a tree conflict victim, an additional line is printed\n"
+ " after the item's status line, explaining the nature of the conflict.\n"
+ "\n"
+ " The out-of-date information appears in the ninth column (with -u):\n"
+ " '*' a newer revision exists on the server\n"
+ " ' ' the working copy is up to date\n"
+ "\n"
+ " Remaining fields are variable width and delimited by spaces:\n"
+ " The working revision (with -u or -v; '-' if the item is copied)\n"
+ " The last committed revision and last committed author (with -v)\n"
+ " The working copy path is always the final field, so it can\n"
+ " include spaces.\n"
+ "\n"
+ " The presence of a question mark ('?') where a working revision, last\n"
+ " committed revision, or last committed author was expected indicates\n"
+ " that the information is unknown or irrelevant given the state of the\n"
+ " item (for example, when the item is the result of a copy operation).\n"
+ " The question mark serves as a visual placeholder to facilitate parsing.\n"
+ "\n"
+ " Example output:\n"
+ " svn status wc\n"
+ " M wc/bar.c\n"
+ " A + wc/qax.c\n"
+ "\n"
+ " svn status -u wc\n"
+ " M 965 wc/bar.c\n"
+ " * 965 wc/foo.c\n"
+ " A + - wc/qax.c\n"
+ " Status against revision: 981\n"
+ "\n"
+ " svn status --show-updates --verbose wc\n"
+ " M 965 938 kfogel wc/bar.c\n"
+ " * 965 922 sussman wc/foo.c\n"
+ " A + - 687 joe wc/qax.c\n"
+ " 965 687 joe wc/zig.c\n"
+ " Status against revision: 981\n"
+ "\n"
+ " svn status\n"
+ " M wc/bar.c\n"
+ " ! C wc/qaz.c\n"
+ " > local missing, incoming edit upon update\n"
+ " D wc/qax.c\n"),
+ { 'u', 'v', 'N', opt_depth, 'q', opt_no_ignore, opt_incremental, opt_xml,
+ opt_ignore_externals, opt_changelist},
+ {{'q', N_("don't print unversioned items")}} },
+
+ { "switch", svn_cl__switch, {"sw"}, N_
+ ("Update the working copy to a different URL within the same repository.\n"
+ "usage: 1. switch URL[@PEGREV] [PATH]\n"
+ " 2. switch --relocate FROM-PREFIX TO-PREFIX [PATH...]\n"
+ "\n"
+ " 1. Update the working copy to mirror a new URL within the repository.\n"
+ " This behavior is similar to 'svn update', and is the way to\n"
+ " move a working copy to a branch or tag within the same repository.\n"
+ " If specified, PEGREV determines in which revision the target is first\n"
+ " looked up.\n"
+ "\n"
+ " If --force is used, unversioned obstructing paths in the working\n"
+ " copy do not automatically cause a failure if the switch attempts to\n"
+ " add the same path. If the obstructing path is the same type (file\n"
+ " or directory) as the corresponding path in the repository it becomes\n"
+ " versioned but its contents are left 'as-is' in the working copy.\n"
+ " This means that an obstructing directory's unversioned children may\n"
+ " also obstruct and become versioned. For files, any content differences\n"
+ " between the obstruction and the repository are treated like a local\n"
+ " modification to the working copy. All properties from the repository\n"
+ " are applied to the obstructing path.\n"
+ "\n"
+ " Use the --set-depth option to set a new working copy depth on the\n"
+ " targets of this operation.\n"
+ "\n"
+ " By default, Subversion will refuse to switch a working copy path to\n"
+ " a new URL with which it shares no common version control ancestry.\n"
+ " Use the '--ignore-ancestry' option to override this sanity check.\n"
+ "\n"
+ " 2. The '--relocate' option is deprecated. This syntax is equivalent to\n"
+ " 'svn relocate FROM-PREFIX TO-PREFIX [PATH]'.\n"
+ "\n"
+ " See also 'svn help update' for a list of possible characters\n"
+ " reporting the action taken.\n"
+ "\n"
+ " Examples:\n"
+ " svn switch ^/branches/1.x-release\n"
+ " svn switch --relocate http:// svn://\n"
+ " svn switch --relocate http://www.example.com/repo/project \\\n"
+ " svn://svn.example.com/repo/project\n"),
+ { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate,
+ opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept} },
+
+ { "unlock", svn_cl__unlock, {0}, N_
+ ("Unlock working copy paths or URLs.\n"
+ "usage: unlock TARGET...\n"
+ "\n"
+ " Use --force to break the lock.\n"),
+ { opt_targets, opt_force } },
+
+ { "update", svn_cl__update, {"up"}, N_
+ ("Bring changes from the repository into the working copy.\n"
+ "usage: update [PATH...]\n"
+ "\n"
+ " If no revision is given, bring working copy up-to-date with HEAD rev.\n"
+ " Else synchronize working copy to revision given by -r.\n"
+ "\n"
+ " For each updated item a line will be printed with characters reporting\n"
+ " the action taken. These characters have the following meaning:\n"
+ "\n"
+ " A Added\n"
+ " D Deleted\n"
+ " U Updated\n"
+ " C Conflict\n"
+ " G Merged\n"
+ " E Existed\n"
+ " R Replaced\n"
+ "\n"
+ " Characters in the first column report about the item itself.\n"
+ " Characters in the second column report about properties of the item.\n"
+ " A 'B' in the third column signifies that the lock for the file has\n"
+ " been broken or stolen.\n"
+ " A 'C' in the fourth column indicates a tree conflict, while a 'C' in\n"
+ " the first and second columns indicate textual conflicts in files\n"
+ " and in property values, respectively.\n"
+ "\n"
+ " If --force is used, unversioned obstructing paths in the working\n"
+ " copy do not automatically cause a failure if the update attempts to\n"
+ " add the same path. If the obstructing path is the same type (file\n"
+ " or directory) as the corresponding path in the repository it becomes\n"
+ " versioned but its contents are left 'as-is' in the working copy.\n"
+ " This means that an obstructing directory's unversioned children may\n"
+ " also obstruct and become versioned. For files, any content differences\n"
+ " between the obstruction and the repository are treated like a local\n"
+ " modification to the working copy. All properties from the repository\n"
+ " are applied to the obstructing path. Obstructing paths are reported\n"
+ " in the first column with code 'E'.\n"
+ "\n"
+ " If the specified update target is missing from the working copy but its\n"
+ " immediate parent directory is present, checkout the target into its\n"
+ " parent directory at the specified depth. If --parents is specified,\n"
+ " create any missing parent directories of the target by checking them\n"
+ " out, too, at depth=empty.\n"
+ "\n"
+ " Use the --set-depth option to set a new working copy depth on the\n"
+ " targets of this operation.\n"),
+ {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force,
+ opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept,
+ opt_parents} },
+
+ { "upgrade", svn_cl__upgrade, {0}, N_
+ ("Upgrade the metadata storage format for a working copy.\n"
+ "usage: upgrade [WCPATH...]\n"
+ "\n"
+ " Local modifications are preserved.\n"),
+ { 'q' } },
+
+ { NULL, NULL, {0}, NULL, {0} }
+};
+
+
+/* Version compatibility check */
+static svn_error_t *
+check_lib_versions(void)
+{
+ static const svn_version_checklist_t checklist[] =
+ {
+ { "svn_subr", svn_subr_version },
+ { "svn_client", svn_client_version },
+ { "svn_wc", svn_wc_version },
+ { "svn_ra", svn_ra_version },
+ { "svn_delta", svn_delta_version },
+ { "svn_diff", svn_diff_version },
+ { NULL, NULL }
+ };
+
+ SVN_VERSION_DEFINE(my_version);
+ return svn_ver_check_list(&my_version, checklist);
+}
+
+
+/* A flag to see if we've been cancelled by the client or not. */
+static volatile sig_atomic_t cancelled = FALSE;
+
+/* A signal handler to support cancellation. */
+static void
+signal_handler(int signum)
+{
+ apr_signal(signum, SIG_IGN);
+ cancelled = TRUE;
+}
+
+/* Our cancellation callback. */
+svn_error_t *
+svn_cl__check_cancel(void *baton)
+{
+ if (cancelled)
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
+ else
+ return SVN_NO_ERROR;
+}
+
+
+/*** Main. ***/
+
+int
+main(int argc, const char *argv[])
+{
+ svn_error_t *err;
+ apr_allocator_t *allocator;
+ apr_pool_t *pool;
+ int opt_id;
+ apr_getopt_t *os;
+ svn_cl__opt_state_t opt_state = { 0, { 0 } };
+ svn_client_ctx_t *ctx;
+ apr_array_header_t *received_opts;
+ int i;
+ const svn_opt_subcommand_desc2_t *subcommand = NULL;
+ const char *dash_m_arg = NULL, *dash_F_arg = NULL;
+ svn_cl__cmd_baton_t command_baton;
+ svn_auth_baton_t *ab;
+ svn_config_t *cfg_config;
+ svn_boolean_t descend = TRUE;
+ svn_boolean_t interactive_conflicts = FALSE;
+ apr_hash_t *changelists;
+
+ /* Initialize the app. */
+ if (svn_cmdline_init("svn", stderr) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+
+ /* Create our top-level pool. Use a separate mutexless allocator,
+ * given this application is single threaded.
+ */
+ if (apr_allocator_create(&allocator))
+ return EXIT_FAILURE;
+
+ apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
+
+ pool = svn_pool_create_ex(NULL, allocator);
+ apr_allocator_owner_set(allocator, pool);
+
+ received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
+
+ /* Check library versions */
+ err = check_lib_versions();
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+#if defined(WIN32) || defined(__CYGWIN__)
+ /* Set the working copy administrative directory name. */
+ if (getenv("SVN_ASP_DOT_NET_HACK"))
+ {
+ err = svn_wc_set_adm_dir("_svn", pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+#endif
+
+ /* Initialize the RA library. */
+ err = svn_ra_initialize(pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+ /* Init our changelists hash. */
+ changelists = apr_hash_make(pool);
+
+ /* Begin processing arguments. */
+ opt_state.start_revision.kind = svn_opt_revision_unspecified;
+ opt_state.end_revision.kind = svn_opt_revision_unspecified;
+ opt_state.revision_ranges =
+ apr_array_make(pool, 0, sizeof(svn_opt_revision_range_t *));
+ opt_state.depth = svn_depth_unknown;
+ opt_state.set_depth = svn_depth_unknown;
+ opt_state.accept_which = svn_cl__accept_unspecified;
+ opt_state.show_revs = svn_cl__show_revs_merged;
+
+ /* No args? Show usage. */
+ if (argc <= 1)
+ {
+ svn_cl__help(NULL, NULL, pool);
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+
+ /* Else, parse options. */
+ err = svn_cmdline__getopt_init(&os, argc, argv, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+ os->interleave = 1;
+ while (1)
+ {
+ const char *opt_arg;
+ const char *utf8_opt_arg;
+
+ /* Parse the next option. */
+ apr_status_t apr_err = apr_getopt_long(os, svn_cl__options, &opt_id,
+ &opt_arg);
+ if (APR_STATUS_IS_EOF(apr_err))
+ break;
+ else if (apr_err)
+ {
+ svn_cl__help(NULL, NULL, pool);
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+
+ /* Stash the option code in an array before parsing it. */
+ APR_ARRAY_PUSH(received_opts, int) = opt_id;
+
+ switch (opt_id) {
+ case 'l':
+ {
+ err = svn_cstring_atoi(&opt_state.limit, opt_arg);
+ if (err)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, err,
+ _("Non-numeric limit argument given"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ if (opt_state.limit <= 0)
+ {
+ err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Argument to --limit must be positive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+ break;
+ case 'm':
+ /* Note that there's no way here to detect if the log message
+ contains a zero byte -- if it does, then opt_arg will just
+ be shorter than the user intended. Oh well. */
+ opt_state.message = apr_pstrdup(pool, opt_arg);
+ dash_m_arg = opt_arg;
+ break;
+ case 'c':
+ {
+ apr_array_header_t *change_revs =
+ svn_cstring_split(opt_arg, ", \n\r\t\v", TRUE, pool);
+
+ if (opt_state.old_target)
+ {
+ err = svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Can't specify -c with --old"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ for (i = 0; i < change_revs->nelts; i++)
+ {
+ char *end;
+ svn_revnum_t changeno, changeno_end;
+ svn_opt_revision_range_t *range;
+ const char *change_str =
+ APR_ARRAY_IDX(change_revs, i, const char *);
+ const char *s = change_str;
+ svn_boolean_t is_negative;
+
+ /* Check for a leading minus to allow "-c -r42".
+ * The is_negative flag is used to handle "-c -42" and "-c -r42".
+ * The "-c r-42" case is handled by strtol() returning a
+ * negative number. */
+ is_negative = (*s == '-');
+ if (is_negative)
+ s++;
+
+ /* Allow any number of 'r's to prefix a revision number. */
+ while (*s == 'r')
+ s++;
+ changeno = changeno_end = strtol(s, &end, 10);
+ if (end != s && *end == '-')
+ {
+ if (changeno < 0 || is_negative)
+ {
+ err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR,
+ NULL,
+ _("Negative number in range (%s)"
+ " not supported with -c"),
+ change_str);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ s = end + 1;
+ while (*s == 'r')
+ s++;
+ changeno_end = strtol(s, &end, 10);
+ }
+ if (end == change_str || *end != '\0')
+ {
+ err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Non-numeric change argument (%s) "
+ "given to -c"), change_str);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ if (changeno == 0)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("There is no change 0"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ if (is_negative)
+ changeno = -changeno;
+
+ /* Figure out the range:
+ -c N -> -r N-1:N
+ -c -N -> -r N:N-1
+ -c M-N -> -r M-1:N for M < N
+ -c M-N -> -r M:N-1 for M > N
+ -c -M-N -> error (too confusing/no valid use case)
+ */
+ if (changeno > 0)
+ {
+ if (changeno <= changeno_end)
+ changeno--;
+ else
+ changeno_end--;
+ }
+ else
+ {
+ changeno = -changeno;
+ changeno_end = changeno - 1;
+ }
+
+ range = apr_palloc(pool, sizeof(*range));
+ range->start.value.number = changeno;
+ range->end.value.number = changeno_end;
+
+ opt_state.used_change_arg = TRUE;
+ range->start.kind = svn_opt_revision_number;
+ range->end.kind = svn_opt_revision_number;
+ APR_ARRAY_PUSH(opt_state.revision_ranges,
+ svn_opt_revision_range_t *) = range;
+ }
+ }
+ break;
+ case 'r':
+ opt_state.used_revision_arg = TRUE;
+ if (svn_opt_parse_revision_to_range(opt_state.revision_ranges,
+ opt_arg, pool) != 0)
+ {
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+ if (! err)
+ err = svn_error_createf
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Syntax error in revision argument '%s'"),
+ utf8_opt_arg);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ break;
+ case 'v':
+ opt_state.verbose = TRUE;
+ break;
+ case 'u':
+ opt_state.update = TRUE;
+ break;
+ case 'h':
+ case '?':
+ opt_state.help = TRUE;
+ break;
+ case 'q':
+ opt_state.quiet = TRUE;
+ break;
+ case opt_incremental:
+ opt_state.incremental = TRUE;
+ break;
+ case 'F':
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+ if (! err)
+ err = svn_stringbuf_from_file2(&(opt_state.filedata),
+ utf8_opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ dash_F_arg = opt_arg;
+ break;
+ case opt_targets:
+ {
+ svn_stringbuf_t *buffer, *buffer_utf8;
+
+ /* We need to convert to UTF-8 now, even before we divide
+ the targets into an array, because otherwise we wouldn't
+ know what delimiter to use for svn_cstring_split(). */
+
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+
+ if (! err)
+ err = svn_stringbuf_from_file2(&buffer, utf8_opt_arg, pool);
+ if (! err)
+ err = svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ opt_state.targets = svn_cstring_split(buffer_utf8->data, "\n\r",
+ TRUE, pool);
+ }
+ break;
+ case opt_force:
+ opt_state.force = TRUE;
+ break;
+ case opt_force_log:
+ opt_state.force_log = TRUE;
+ break;
+ case opt_dry_run:
+ opt_state.dry_run = TRUE;
+ break;
+ case opt_revprop:
+ opt_state.revprop = TRUE;
+ break;
+ case 'R':
+ opt_state.depth = svn_depth_infinity;
+ break;
+ case 'N':
+ descend = FALSE;
+ break;
+ case opt_depth:
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Error converting depth "
+ "from locale to UTF-8")), pool, "svn: ");
+ opt_state.depth = svn_depth_from_word(utf8_opt_arg);
+ if (opt_state.depth == svn_depth_unknown
+ || opt_state.depth == svn_depth_exclude)
+ {
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid depth; try "
+ "'empty', 'files', 'immediates', "
+ "or 'infinity'"),
+ utf8_opt_arg), pool, "svn: ");
+ }
+ break;
+ case opt_set_depth:
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Error converting depth "
+ "from locale to UTF-8")), pool, "svn: ");
+ opt_state.set_depth = svn_depth_from_word(utf8_opt_arg);
+ /* svn_depth_exclude is okay for --set-depth. */
+ if (opt_state.set_depth == svn_depth_unknown)
+ {
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid depth; try "
+ "'exclude', 'empty', 'files', "
+ "'immediates', or 'infinity'"),
+ utf8_opt_arg), pool, "svn: ");
+ }
+ break;
+ case opt_version:
+ opt_state.version = TRUE;
+ break;
+ case opt_auth_username:
+ err = svn_utf_cstring_to_utf8(&opt_state.auth_username,
+ opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
+ case opt_auth_password:
+ err = svn_utf_cstring_to_utf8(&opt_state.auth_password,
+ opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
+ case opt_encoding:
+ opt_state.encoding = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_xml:
+ opt_state.xml = TRUE;
+ break;
+ case opt_stop_on_copy:
+ opt_state.stop_on_copy = TRUE;
+ break;
+ case opt_strict:
+ opt_state.strict = TRUE;
+ break;
+ case opt_no_ignore:
+ opt_state.no_ignore = TRUE;
+ break;
+ case opt_no_auth_cache:
+ opt_state.no_auth_cache = TRUE;
+ break;
+ case opt_non_interactive:
+ opt_state.non_interactive = TRUE;
+ break;
+ case opt_trust_server_cert:
+ opt_state.trust_server_cert = TRUE;
+ break;
+ case opt_no_diff_deleted:
+ opt_state.no_diff_deleted = TRUE;
+ break;
+ case opt_show_copies_as_adds:
+ opt_state.show_copies_as_adds = TRUE;
+ break;
+ case opt_notice_ancestry:
+ opt_state.notice_ancestry = TRUE;
+ break;
+ case opt_ignore_ancestry:
+ opt_state.ignore_ancestry = TRUE;
+ break;
+ case opt_ignore_externals:
+ opt_state.ignore_externals = TRUE;
+ break;
+ case opt_relocate:
+ opt_state.relocate = TRUE;
+ break;
+ case 'x':
+ err = svn_utf_cstring_to_utf8(&opt_state.extensions, opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
+ case opt_diff_cmd:
+ opt_state.diff_cmd = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_merge_cmd:
+ opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_record_only:
+ opt_state.record_only = TRUE;
+ break;
+ case opt_editor_cmd:
+ opt_state.editor_cmd = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_old_cmd:
+ if (opt_state.used_change_arg)
+ {
+ err = svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Can't specify -c with --old"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ opt_state.old_target = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_new_cmd:
+ opt_state.new_target = apr_pstrdup(pool, opt_arg);
+ break;
+ case opt_config_dir:
+ {
+ const char *path_utf8;
+ err = svn_utf_cstring_to_utf8(&path_utf8, opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ opt_state.config_dir = svn_dirent_internal_style(path_utf8, pool);
+ }
+ break;
+ case opt_config_options:
+ if (!opt_state.config_options)
+ opt_state.config_options =
+ apr_array_make(pool, 1,
+ sizeof(svn_cmdline__config_argument_t*));
+
+ err = svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool);
+ if (!err)
+ err = svn_cmdline__parse_config_option(opt_state.config_options,
+ opt_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
+ case opt_autoprops:
+ opt_state.autoprops = TRUE;
+ break;
+ case opt_no_autoprops:
+ opt_state.no_autoprops = TRUE;
+ break;
+ case opt_native_eol:
+ if ( !strcmp("LF", opt_arg) || !strcmp("CR", opt_arg) ||
+ !strcmp("CRLF", opt_arg))
+ opt_state.native_eol = apr_pstrdup(pool, opt_arg);
+ else
+ {
+ err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
+ if (! err)
+ err = svn_error_createf
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Syntax error in native-eol argument '%s'"),
+ utf8_opt_arg);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ break;
+ case opt_no_unlock:
+ opt_state.no_unlock = TRUE;
+ break;
+ case opt_summarize:
+ opt_state.summarize = TRUE;
+ break;
+ case opt_remove:
+ opt_state.remove = TRUE;
+ break;
+ case opt_changelist:
+ opt_state.changelist = apr_pstrdup(pool, opt_arg);
+ if (opt_state.changelist[0] == '\0')
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Changelist names must not be empty"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ apr_hash_set(changelists, opt_state.changelist,
+ APR_HASH_KEY_STRING, (void *)1);
+ break;
+ case opt_keep_changelists:
+ opt_state.keep_changelists = TRUE;
+ break;
+ case opt_keep_local:
+ opt_state.keep_local = TRUE;
+ break;
+ case opt_with_all_revprops:
+ /* If --with-all-revprops is specified along with one or more
+ * --with-revprops options, --with-all-revprops takes precedence. */
+ opt_state.all_revprops = TRUE;
+ break;
+ case opt_with_no_revprops:
+ opt_state.no_revprops = TRUE;
+ break;
+ case opt_with_revprop:
+ err = svn_opt_parse_revprop(&opt_state.revprop_table, opt_arg, pool);
+ if (err != SVN_NO_ERROR)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ break;
+ case opt_parents:
+ opt_state.parents = TRUE;
+ break;
+ case 'g':
+ opt_state.use_merge_history = TRUE;
+ break;
+ case opt_accept:
+ opt_state.accept_which = svn_cl__accept_from_word(opt_arg);
+ if (opt_state.accept_which == svn_cl__accept_invalid)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid --accept value"),
+ opt_arg),
+ pool, "svn: ");
+ break;
+ case opt_show_revs:
+ opt_state.show_revs = svn_cl__show_revs_from_word(opt_arg);
+ if (opt_state.show_revs == svn_cl__show_revs_invalid)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid --show-revs value"),
+ opt_arg),
+ pool, "svn: ");
+ break;
+ case opt_reintegrate:
+ opt_state.reintegrate = TRUE;
+ break;
+ case opt_strip:
+ {
+ err = svn_cstring_atoi(&opt_state.strip, opt_arg);
+ if (err)
+ {
+ err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
+ _("Invalid strip count '%s'"), opt_arg);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ if (opt_state.strip < 0)
+ {
+ err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Argument to --strip must be positive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+ break;
+ case opt_ignore_keywords:
+ opt_state.ignore_keywords = TRUE;
+ break;
+ case opt_reverse_diff:
+ opt_state.reverse_diff = TRUE;
+ break;
+ case opt_ignore_whitespace:
+ opt_state.ignore_whitespace = TRUE;
+ break;
+ case opt_diff:
+ opt_state.show_diff = TRUE;
+ break;
+ case opt_internal_diff:
+ opt_state.internal_diff = TRUE;
+ break;
+ case opt_use_git_diff_format:
+ opt_state.use_git_diff_format = TRUE;
+ break;
+ case opt_allow_mixed_revisions:
+ opt_state.allow_mixed_rev = TRUE;
+ break;
+ default:
+ /* Hmmm. Perhaps this would be a good place to squirrel away
+ opts that commands like svn diff might need. Hmmm indeed. */
+ break;
+ }
+ }
+
+ /* Turn our hash of changelists into an array of unique ones. */
+ err = svn_hash_keys(&(opt_state.changelists), changelists, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+ /* ### This really belongs in libsvn_client. The trouble is,
+ there's no one place there to run it from, no
+ svn_client_init(). We'd have to add it to all the public
+ functions that a client might call. It's unmaintainable to do
+ initialization from within libsvn_client itself, but it seems
+ burdensome to demand that all clients call svn_client_init()
+ before calling any other libsvn_client function... On the other
+ hand, the alternative is effectively to demand that they call
+ svn_config_ensure() instead, so maybe we should have a generic
+ init function anyway. Thoughts? */
+ err = svn_config_ensure(opt_state.config_dir, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+ /* If the user asked for help, then the rest of the arguments are
+ the names of subcommands to get help on (if any), or else they're
+ just typos/mistakes. Whatever the case, the subcommand to
+ actually run is svn_cl__help(). */
+ if (opt_state.help)
+ subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table, "help");
+
+ /* If we're not running the `help' subcommand, then look for a
+ subcommand in the first argument. */
+ if (subcommand == NULL)
+ {
+ if (os->ind >= os->argc)
+ {
+ if (opt_state.version)
+ {
+ /* Use the "help" subcommand to handle the "--version" option. */
+ static const svn_opt_subcommand_desc2_t pseudo_cmd =
+ { "--version", svn_cl__help, {0}, "",
+ {opt_version, /* must accept its own option */
+ 'q', /* brief output */
+ opt_config_dir /* all commands accept this */
+ } };
+
+ subcommand = &pseudo_cmd;
+ }
+ else
+ {
+ svn_error_clear
+ (svn_cmdline_fprintf(stderr, pool,
+ _("Subcommand argument required\n")));
+ svn_cl__help(NULL, NULL, pool);
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ const char *first_arg = os->argv[os->ind++];
+ subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
+ first_arg);
+ if (subcommand == NULL)
+ {
+ const char *first_arg_utf8;
+ err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ svn_error_clear
+ (svn_cmdline_fprintf(stderr, pool,
+ _("Unknown command: '%s'\n"),
+ first_arg_utf8));
+ svn_cl__help(NULL, NULL, pool);
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ /* Check that the subcommand wasn't passed any inappropriate options. */
+ for (i = 0; i < received_opts->nelts; i++)
+ {
+ opt_id = APR_ARRAY_IDX(received_opts, i, int);
+
+ /* All commands implicitly accept --help, so just skip over this
+ when we see it. Note that we don't want to include this option
+ in their "accepted options" list because it would be awfully
+ redundant to display it in every commands' help text. */
+ if (opt_id == 'h' || opt_id == '?')
+ continue;
+
+ if (! svn_opt_subcommand_takes_option3(subcommand, opt_id,
+ svn_cl__global_options))
+ {
+ const char *optstr;
+ const apr_getopt_option_t *badopt =
+ svn_opt_get_option_from_code2(opt_id, svn_cl__options,
+ subcommand, pool);
+ svn_opt_format_option(&optstr, badopt, FALSE, pool);
+ if (subcommand->name[0] == '-')
+ svn_cl__help(NULL, NULL, pool);
+ else
+ svn_error_clear
+ (svn_cmdline_fprintf
+ (stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n"
+ "Type 'svn help %s' for usage.\n"),
+ subcommand->name, optstr, subcommand->name));
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Only merge and log support multiple revisions/revision ranges. */
+ if (subcommand->cmd_func != svn_cl__merge
+ && subcommand->cmd_func != svn_cl__log)
+ {
+ if (opt_state.revision_ranges->nelts > 1)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Multiple revision arguments "
+ "encountered; can't specify -c twice, "
+ "or both -c and -r"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+
+ /* Disallow simultaneous use of both --depth and --set-depth. */
+ if ((opt_state.depth != svn_depth_unknown)
+ && (opt_state.set_depth != svn_depth_unknown))
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--depth and --set-depth are mutually "
+ "exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Disallow simultaneous use of both --with-all-revprops and
+ --with-no-revprops. */
+ if (opt_state.all_revprops && opt_state.no_revprops)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--with-all-revprops and --with-no-revprops "
+ "are mutually exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Disallow simultaneous use of both --with-revprop and
+ --with-no-revprops. */
+ if (opt_state.revprop_table && opt_state.no_revprops)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--with-revprop and --with-no-revprops "
+ "are mutually exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Disallow simultaneous use of both -m and -F, when they are
+ both used to pass a commit message or lock comment. ('propset'
+ takes the property value, not a commit message, from -F.)
+ */
+ if (opt_state.filedata && opt_state.message
+ && subcommand->cmd_func != svn_cl__propset)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--message (-m) and --file (-F) "
+ "are mutually exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* --trust-server-cert can only be used with --non-interactive */
+ if (opt_state.trust_server_cert && !opt_state.non_interactive)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--trust-server-cert requires "
+ "--non-interactive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Disallow simultaneous use of both --diff-cmd and
+ --internal-diff. */
+ if (opt_state.diff_cmd && opt_state.internal_diff)
+ {
+ err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--diff-cmd and --internal-diff "
+ "are mutually exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Ensure that 'revision_ranges' has at least one item, and make
+ 'start_revision' and 'end_revision' match that item. */
+ if (opt_state.revision_ranges->nelts == 0)
+ {
+ svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
+ range->start.kind = svn_opt_revision_unspecified;
+ range->end.kind = svn_opt_revision_unspecified;
+ APR_ARRAY_PUSH(opt_state.revision_ranges,
+ svn_opt_revision_range_t *) = range;
+ }
+ opt_state.start_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
+ svn_opt_revision_range_t *)->start;
+ opt_state.end_revision = APR_ARRAY_IDX(opt_state.revision_ranges, 0,
+ svn_opt_revision_range_t *)->end;
+
+ /* Create a client context object. */
+ command_baton.opt_state = &opt_state;
+ if ((err = svn_client_create_context(&ctx, pool)))
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ command_baton.ctx = ctx;
+
+ /* If we're running a command that could result in a commit, verify
+ that any log message we were given on the command line makes
+ sense (unless we've also been instructed not to care). */
+ if ((! opt_state.force_log)
+ && (subcommand->cmd_func == svn_cl__commit
+ || subcommand->cmd_func == svn_cl__copy
+ || subcommand->cmd_func == svn_cl__delete
+ || subcommand->cmd_func == svn_cl__import
+ || subcommand->cmd_func == svn_cl__mkdir
+ || subcommand->cmd_func == svn_cl__move
+ || subcommand->cmd_func == svn_cl__lock
+ || subcommand->cmd_func == svn_cl__propedit))
+ {
+ /* If the -F argument is a file that's under revision control,
+ that's probably not what the user intended. */
+ if (dash_F_arg)
+ {
+ svn_node_kind_t kind;
+ const char *local_abspath;
+ const char *fname_utf8 = svn_dirent_internal_style(dash_F_arg, pool);
+
+ err = svn_dirent_get_absolute(&local_abspath, fname_utf8, pool);
+
+ if (!err)
+ {
+ err = svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, FALSE,
+ pool);
+
+ if (!err && kind != svn_node_none && kind != svn_node_unknown)
+ {
+ if (subcommand->cmd_func != svn_cl__lock)
+ {
+ err = svn_error_create(
+ SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
+ _("Log message file is a versioned file; "
+ "use '--force-log' to override"));
+ }
+ else
+ {
+ err = svn_error_create(
+ SVN_ERR_CL_LOG_MESSAGE_IS_VERSIONED_FILE, NULL,
+ _("Lock comment file is a versioned file; "
+ "use '--force-log' to override"));
+ }
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+ svn_error_clear(err);
+ }
+
+ /* If the -m argument is a file at all, that's probably not what
+ the user intended. */
+ if (dash_m_arg)
+ {
+ apr_finfo_t finfo;
+ if (apr_stat(&finfo, dash_m_arg,
+ APR_FINFO_MIN, pool) == APR_SUCCESS)
+ {
+ if (subcommand->cmd_func != svn_cl__lock)
+ {
+ err = svn_error_create
+ (SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
+ _("The log message is a pathname "
+ "(was -F intended?); use '--force-log' to override"));
+ }
+ else
+ {
+ err = svn_error_create
+ (SVN_ERR_CL_LOG_MESSAGE_IS_PATHNAME, NULL,
+ _("The lock comment is a pathname "
+ "(was -F intended?); use '--force-log' to override"));
+ }
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+ }
+
+ /* Relocation is infinite-depth only. */
+ if (opt_state.relocate)
+ {
+ if (opt_state.depth != svn_depth_unknown)
+ {
+ err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--relocate and --depth are mutually "
+ "exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ if (! descend)
+ {
+ err = svn_error_create(
+ SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--relocate and --non-recursive (-N) are mutually "
+ "exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+
+ /* Only a few commands can accept a revision range; the rest can take at
+ most one revision number. */
+ if (subcommand->cmd_func != svn_cl__blame
+ && subcommand->cmd_func != svn_cl__diff
+ && subcommand->cmd_func != svn_cl__log
+ && subcommand->cmd_func != svn_cl__merge)
+ {
+ if (opt_state.end_revision.kind != svn_opt_revision_unspecified)
+ {
+ err = svn_error_create(SVN_ERR_CLIENT_REVISION_RANGE, NULL, NULL);
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+
+ /* -N has a different meaning depending on the command */
+ if (descend == FALSE)
+ {
+ if (subcommand->cmd_func == svn_cl__status)
+ {
+ opt_state.depth = svn_depth_immediates;
+ }
+ else if (subcommand->cmd_func == svn_cl__revert
+ || subcommand->cmd_func == svn_cl__add
+ || subcommand->cmd_func == svn_cl__commit)
+ {
+ /* In pre-1.5 Subversion, some commands treated -N like
+ --depth=empty, so force that mapping here. Anyway, with
+ revert it makes sense to be especially conservative,
+ since revert can lose data. */
+ opt_state.depth = svn_depth_empty;
+ }
+ else
+ {
+ opt_state.depth = svn_depth_files;
+ }
+ }
+
+ err = svn_config_get_config(&(ctx->config),
+ opt_state.config_dir, pool);
+ if (err)
+ {
+ /* Fallback to default config if the config directory isn't readable
+ or is not a directory. */
+ if (APR_STATUS_IS_EACCES(err->apr_err)
+ || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ }
+ else
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
+ APR_HASH_KEY_STRING);
+
+ /* Update the options in the config */
+ if (opt_state.config_options)
+ {
+ svn_error_clear(
+ svn_cmdline__apply_config_options(ctx->config,
+ opt_state.config_options,
+ "svn: ", "--config-option"));
+ }
+
+ /* XXX: Only diff_cmd for now, overlay rest later and stop passing
+ opt_state altogether? */
+ if (opt_state.diff_cmd)
+ svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff_cmd);
+ if (opt_state.merge_cmd)
+ svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
+ if (opt_state.internal_diff)
+ svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_DIFF_CMD, NULL);
+
+ /* Check for mutually exclusive args --auto-props and --no-auto-props */
+ if (opt_state.autoprops && opt_state.no_autoprops)
+ {
+ err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--auto-props and --no-auto-props are "
+ "mutually exclusive"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* The --reintegrate option is mutually exclusive with both
+ --ignore-ancestry and --record-only. */
+ if (opt_state.reintegrate)
+ {
+ if (opt_state.ignore_ancestry)
+ {
+ if (opt_state.record_only)
+ {
+ err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--reintegrate cannot be used with "
+ "--ignore-ancestry or "
+ "--record-only"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ else
+ {
+ err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--reintegrate cannot be used with "
+ "--ignore-ancestry"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+ else if (opt_state.record_only)
+ {
+ err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--reintegrate cannot be used with "
+ "--record-only"));
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+ }
+
+ /* Update auto-props-enable option, and populate the MIME types map,
+ for add/import commands */
+ if (subcommand->cmd_func == svn_cl__add
+ || subcommand->cmd_func == svn_cl__import)
+ {
+ const char *mimetypes_file;
+ svn_config_get(cfg_config, &mimetypes_file,
+ SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_MIMETYPES_FILE, FALSE);
+ if (mimetypes_file && *mimetypes_file)
+ {
+ if ((err = svn_io_parse_mimetypes_file(&(ctx->mimetypes_map),
+ mimetypes_file, pool)))
+ svn_handle_error2(err, stderr, TRUE, "svn: ");
+ }
+
+ if (opt_state.autoprops)
+ {
+ svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, TRUE);
+ }
+ if (opt_state.no_autoprops)
+ {
+ svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE);
+ }
+ }
+
+ /* Update the 'keep-locks' runtime option */
+ if (opt_state.no_unlock)
+ svn_config_set_bool(cfg_config, SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_NO_UNLOCK, TRUE);
+
+ /* Set the log message callback function. Note that individual
+ subcommands will populate the ctx->log_msg_baton3. */
+ ctx->log_msg_func3 = svn_cl__get_log_message;
+
+ /* Set up the notifier. */
+ if (((subcommand->cmd_func != svn_cl__status) && !opt_state.quiet)
+ || ((subcommand->cmd_func == svn_cl__status) && !opt_state.xml))
+ {
+ err = svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2,
+ FALSE, pool);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+ }
+
+ /* Set up our cancellation support. */
+ ctx->cancel_func = svn_cl__check_cancel;
+ apr_signal(SIGINT, signal_handler);
+#ifdef SIGBREAK
+ /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
+ apr_signal(SIGBREAK, signal_handler);
+#endif
+#ifdef SIGHUP
+ apr_signal(SIGHUP, signal_handler);
+#endif
+#ifdef SIGTERM
+ apr_signal(SIGTERM, signal_handler);
+#endif
+
+#ifdef SIGPIPE
+ /* Disable SIGPIPE generation for the platforms that have it. */
+ apr_signal(SIGPIPE, SIG_IGN);
+#endif
+
+#ifdef SIGXFSZ
+ /* Disable SIGXFSZ generation for the platforms that have it, otherwise
+ * working with large files when compiled against an APR that doesn't have
+ * large file support will crash the program, which is uncool. */
+ apr_signal(SIGXFSZ, SIG_IGN);
+#endif
+
+ /* Set up Authentication stuff. */
+ if ((err = svn_cmdline_create_auth_baton(&ab,
+ opt_state.non_interactive,
+ opt_state.auth_username,
+ opt_state.auth_password,
+ opt_state.config_dir,
+ opt_state.no_auth_cache,
+ opt_state.trust_server_cert,
+ cfg_config,
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ pool)))
+ svn_handle_error2(err, stderr, TRUE, "svn: ");
+
+ ctx->auth_baton = ab;
+
+ /* Set up conflict resolution callback. */
+ if ((err = svn_config_get_bool(cfg_config, &interactive_conflicts,
+ SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_INTERACTIVE_CONFLICTS,
+ TRUE))) /* ### interactivity on by default.
+ we can change this. */
+ svn_handle_error2(err, stderr, TRUE, "svn: ");
+
+ if ((opt_state.accept_which == svn_cl__accept_unspecified
+ && (!interactive_conflicts || opt_state.non_interactive))
+ || opt_state.accept_which == svn_cl__accept_postpone)
+ {
+ /* If no --accept option at all and we're non-interactive, we're
+ leaving the conflicts behind, so don't need the callback. Same if
+ the user said to postpone. */
+ ctx->conflict_func = NULL;
+ ctx->conflict_baton = NULL;
+ }
+ else
+ {
+ svn_cmdline_prompt_baton_t *pb = apr_palloc(pool, sizeof(*pb));
+ pb->cancel_func = ctx->cancel_func;
+ pb->cancel_baton = ctx->cancel_baton;
+
+ if (opt_state.non_interactive)
+ {
+ if (opt_state.accept_which == svn_cl__accept_edit)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--accept=%s incompatible with"
+ " --non-interactive"), SVN_CL__ACCEPT_EDIT),
+ pool, "svn: ");
+ if (opt_state.accept_which == svn_cl__accept_launch)
+ return svn_cmdline_handle_exit_error
+ (svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--accept=%s incompatible with"
+ " --non-interactive"),
+ SVN_CL__ACCEPT_LAUNCH),
+ pool, "svn: ");
+ }
+
+ ctx->conflict_func = svn_cl__conflict_handler;
+ ctx->conflict_baton = svn_cl__conflict_baton_make(
+ opt_state.accept_which,
+ ctx->config,
+ opt_state.editor_cmd,
+ pb,
+ pool);
+ }
+
+ /* And now we finally run the subcommand. */
+ err = (*subcommand->cmd_func)(os, &command_baton, pool);
+ if (err)
+ {
+ /* For argument-related problems, suggest using the 'help'
+ subcommand. */
+ if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
+ || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
+ {
+ err = svn_error_quick_wrap(err,
+ _("Try 'svn help' for more info"));
+ }
+ if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
+ {
+ err = svn_error_quick_wrap(err,
+ _("Please see the 'svn upgrade' command"));
+ }
+
+ /* Issue #3014:
+ * Don't print anything on broken pipes. The pipe was likely
+ * closed by the process at the other end. We expect that
+ * process to perform error reporting as necessary.
+ *
+ * ### This assumes that there is only one error in a chain for
+ * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+ if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+ svn_handle_error2(err, stderr, FALSE, "svn: ");
+
+ /* Tell the user about 'svn cleanup' if any error on the stack
+ was about locked working copies. */
+ if (svn_error_find_cause(err, SVN_ERR_WC_LOCKED))
+ svn_error_clear(svn_cmdline_fputs(_("svn: run 'svn cleanup' to "
+ "remove locks (type 'svn help "
+ "cleanup' for details)\n"),
+ stderr, pool));
+
+ svn_error_clear(err);
+ svn_pool_destroy(pool);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ /* Ensure that stdout is flushed, so the user will see any write errors.
+ This makes sure that output is not silently lost. */
+ err = svn_cmdline_fflush(stdout);
+ if (err)
+ return svn_cmdline_handle_exit_error(err, pool, "svn: ");
+
+ svn_pool_destroy(pool);
+ return EXIT_SUCCESS;
+ }
+}
diff --git a/subversion/svn/merge-cmd.c b/subversion/svn/merge-cmd.c
new file mode 100644
index 0000000..57aad9f
--- /dev/null
+++ b/subversion/svn/merge-cmd.c
@@ -0,0 +1,383 @@
+/*
+ * merge-cmd.c -- Merging changes into a working copy.
+ *
+ * ====================================================================
+ * 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_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_types.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__merge(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_array_header_t *targets;
+ const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
+ svn_boolean_t two_sources_specified = TRUE;
+ svn_error_t *err;
+ svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
+ peg_revision2;
+ apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
+
+ /* Merge doesn't support specifying a revision or revision range
+ when using --reintegrate. */
+ if (opt_state->reintegrate
+ && opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("-r and -c can't be used with --reintegrate"));
+ }
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* For now, we require at least one source. That may change in
+ future versions of Subversion, for example if we have support for
+ negated mergeinfo. See this IRC conversation:
+
+ <bhuvan> kfogel: yeah, i think you are correct; we should
+ specify the source url
+
+ <kfogel> bhuvan: I'll change the help output and propose for
+ backport. Thanks.
+
+ <bhuvan> kfogel: np; while we are at it, 'svn merge' simply
+ returns nothing; i think we should say: """svn: Not
+ enough arguments provided; try 'svn help' for more
+ info"""
+
+ <kfogel> good idea
+
+ <kfogel> (in the future, 'svn merge' might actually do
+ something, but that's all the more reason to make
+ sure it errors now)
+
+ <cmpilato> actually, i'm pretty sure 'svn merge' does something
+
+ <cmpilato> it says "please merge any unmerged changes from
+ myself to myself."
+
+ <cmpilato> :-)
+
+ <kfogel> har har
+
+ <cmpilato> kfogel: i was serious.
+
+ <kfogel> cmpilato: urrr, uh. Is that meaningful? Is there
+ ever a reason for a user to run it?
+
+ <cmpilato> kfogel: not while we don't have support for negated
+ mergeinfo.
+
+ <kfogel> cmpilato: do you concur that until it does something
+ useful it should error?
+
+ <cmpilato> kfogel: yup.
+
+ <kfogel> cool
+ */
+ if (targets->nelts < 1)
+ {
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
+ _("Merge source required"));
+ }
+ else /* Parse at least one, and possible two, sources. */
+ {
+ SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
+ APR_ARRAY_IDX(targets, 0, const char *),
+ pool));
+ if (targets->nelts >= 2)
+ SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
+ APR_ARRAY_IDX(targets, 1, const char *),
+ pool));
+ }
+
+ /* We could have one or two sources. Deliberately written to stay
+ correct even if we someday permit implied merge source. */
+ if (targets->nelts <= 1)
+ {
+ two_sources_specified = FALSE;
+ }
+ else if (targets->nelts == 2)
+ {
+ if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
+ two_sources_specified = FALSE;
+ }
+
+ if (opt_state->revision_ranges->nelts > 0)
+ {
+ first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
+ svn_opt_revision_range_t *)->start;
+ first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
+ svn_opt_revision_range_t *)->end;
+ }
+ else
+ {
+ first_range_start.kind = first_range_end.kind =
+ svn_opt_revision_unspecified;
+ }
+
+ /* If revision_ranges has at least one real range at this point, then
+ we know the user must have used the '-r' and/or '-c' switch(es).
+ This means we're *not* doing two distinct sources. */
+ if (first_range_start.kind != svn_opt_revision_unspecified)
+ {
+ /* A revision *range* is required. */
+ if (first_range_end.kind == svn_opt_revision_unspecified)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
+ _("Second revision required"));
+
+ two_sources_specified = FALSE;
+ }
+
+ if (! two_sources_specified) /* TODO: Switch order of if */
+ {
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments given"));
+
+ /* Set the default value for unspecified paths and peg revision. */
+ if (targets->nelts == 0)
+ {
+ peg_revision1.kind = svn_opt_revision_head;
+ }
+ else
+ {
+ /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
+ SOURCE WCPATH") here. */
+ sourcepath2 = sourcepath1;
+
+ if (peg_revision1.kind == svn_opt_revision_unspecified)
+ peg_revision1.kind = svn_path_is_url(sourcepath1)
+ ? svn_opt_revision_head : svn_opt_revision_working;
+
+ if (targets->nelts == 2)
+ {
+ targetpath = APR_ARRAY_IDX(targets, 1, const char *);
+ if (svn_path_is_url(targetpath))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot specify a revision range "
+ "with two URLs"));
+ }
+ }
+ }
+ else /* using @rev syntax */
+ {
+ if (targets->nelts < 2)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
+ if (targets->nelts > 3)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments given"));
+
+ first_range_start = peg_revision1;
+ first_range_end = peg_revision2;
+
+ /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
+ revisions--since it ignores local modifications it may not do what
+ the user expects. Forcing the user to specify a repository
+ revision should avoid any confusion. */
+ if ((first_range_start.kind == svn_opt_revision_unspecified
+ && ! svn_path_is_url(sourcepath1))
+ ||
+ (first_range_end.kind == svn_opt_revision_unspecified
+ && ! svn_path_is_url(sourcepath2)))
+ return svn_error_create
+ (SVN_ERR_CLIENT_BAD_REVISION, 0,
+ _("A working copy merge source needs an explicit revision"));
+
+ /* Default peg revisions to each URL's youngest revision. */
+ if (first_range_start.kind == svn_opt_revision_unspecified)
+ first_range_start.kind = svn_opt_revision_head;
+ if (first_range_end.kind == svn_opt_revision_unspecified)
+ first_range_end.kind = svn_opt_revision_head;
+
+ /* Decide where to apply the delta (defaulting to "."). */
+ if (targets->nelts == 3)
+ targetpath = APR_ARRAY_IDX(targets, 2, const char *);
+ }
+
+ /* If no targetpath was specified, see if we can infer it from the
+ sourcepaths. */
+ if (sourcepath1 && sourcepath2 && strcmp(targetpath, "") == 0)
+ {
+ /* If the sourcepath is a URL, it can only refer to a target in
+ the current working directory. However, if the sourcepath is
+ a local path, it can refer to a target somewhere deeper in
+ the directory structure. */
+ if (svn_path_is_url(sourcepath1))
+ {
+ const char *sp1_basename = svn_uri_basename(sourcepath1, pool);
+ const char *sp2_basename = svn_uri_basename(sourcepath2, pool);
+
+ if (strcmp(sp1_basename, sp2_basename) == 0)
+ {
+ svn_node_kind_t kind;
+
+ SVN_ERR(svn_io_check_path(sp1_basename, &kind, pool));
+ if (kind == svn_node_file)
+ {
+ targetpath = sp1_basename;
+ }
+ }
+ }
+ else if (strcmp(sourcepath1, sourcepath2) == 0)
+ {
+ svn_node_kind_t kind;
+ const char *decoded_path = svn_path_uri_decode(sourcepath1, pool);
+ SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
+ if (kind == svn_node_file)
+ {
+ targetpath = decoded_path;
+ }
+ }
+ }
+
+ if (opt_state->extensions)
+ options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
+ else
+ options = NULL;
+
+ /* More input validation. */
+ if (opt_state->reintegrate)
+ {
+ if (opt_state->depth != svn_depth_unknown)
+ return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--depth cannot be used with "
+ "--reintegrate"));
+
+ if (opt_state->force)
+ return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--force cannot be used with "
+ "--reintegrate"));
+
+ if (two_sources_specified)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--reintegrate can only be used with "
+ "a single merge source"));
+ if (opt_state->allow_mixed_rev)
+ return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--allow-mixed-revisions cannot be used "
+ "with --reintegrate"));
+ }
+
+ if (! two_sources_specified) /* TODO: Switch order of if */
+ {
+ /* If we don't have a source, use the target as the source. */
+ if (! sourcepath1)
+ sourcepath1 = targetpath;
+
+ /* If we don't have at least one valid revision range, pick a
+ good one that spans the entire set of revisions on our
+ source. */
+ if ((first_range_start.kind == svn_opt_revision_unspecified)
+ && (first_range_end.kind == svn_opt_revision_unspecified))
+ {
+ svn_opt_revision_range_t *range = apr_pcalloc(pool, sizeof(*range));
+ ranges_to_merge = apr_array_make(pool, 1, sizeof(range));
+ range->start.kind = svn_opt_revision_number;
+ range->start.value.number = 1;
+ range->end = peg_revision1;
+ APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = range;
+ }
+
+ if (opt_state->reintegrate)
+ err = svn_client_merge_reintegrate(sourcepath1,
+ &peg_revision1,
+ targetpath,
+ opt_state->dry_run,
+ options, ctx, pool);
+ else
+ err = svn_client_merge_peg4(sourcepath1,
+ ranges_to_merge,
+ &peg_revision1,
+ targetpath,
+ opt_state->depth,
+ opt_state->ignore_ancestry,
+ opt_state->force,
+ opt_state->record_only,
+ opt_state->dry_run,
+ opt_state->allow_mixed_rev,
+ options,
+ ctx,
+ pool);
+ }
+ else
+ {
+ if (svn_path_is_url(sourcepath1) != svn_path_is_url(sourcepath2))
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Merge sources must both be "
+ "either paths or URLs"));
+ err = svn_client_merge4(sourcepath1,
+ &first_range_start,
+ sourcepath2,
+ &first_range_end,
+ targetpath,
+ opt_state->depth,
+ opt_state->ignore_ancestry,
+ opt_state->force,
+ opt_state->record_only,
+ opt_state->dry_run,
+ opt_state->allow_mixed_rev,
+ options,
+ ctx,
+ pool);
+ }
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__print_conflict_stats(ctx->notify_baton2, pool));
+
+ if (err)
+ {
+ if(err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
+ {
+ err = svn_error_quick_wrap(
+ err,
+ _("Merge tracking not possible, use --ignore-ancestry or\n"
+ "fix invalid mergeinfo in target with 'svn propset'"));
+ }
+ else if (! opt_state->reintegrate)
+ {
+ return svn_cl__may_need_force(err);
+ }
+ }
+
+ return svn_error_trace(err);
+}
diff --git a/subversion/svn/mergeinfo-cmd.c b/subversion/svn/mergeinfo-cmd.c
new file mode 100644
index 0000000..5875e2d
--- /dev/null
+++ b/subversion/svn/mergeinfo-cmd.c
@@ -0,0 +1,135 @@
+/*
+ * mergeinfo-cmd.c -- Query merge-relative info.
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_cmdline.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_types.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Implements the svn_log_entry_receiver_t interface. */
+static svn_error_t *
+print_log_rev(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *pool)
+{
+ if (log_entry->non_inheritable)
+ SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__mergeinfo(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_array_header_t *targets;
+ const char *source, *target;
+ svn_opt_revision_t src_peg_revision, tgt_peg_revision;
+ /* Default to depth empty. */
+ svn_depth_t depth = opt_state->depth == svn_depth_unknown
+ ? svn_depth_empty : opt_state->depth;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We expect a single source URL followed by a single target --
+ nothing more, nothing less. */
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Not enough arguments given"));
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments given"));
+
+ /* Parse the SOURCE-URL[@REV] argument. */
+ SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source,
+ APR_ARRAY_IDX(targets, 0, const char *), pool));
+
+ /* Parse the TARGET[@REV] argument (if provided). */
+ if (targets->nelts == 2)
+ {
+ SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target,
+ APR_ARRAY_IDX(targets, 1, const char *),
+ pool));
+ }
+ else
+ {
+ target = "";
+ tgt_peg_revision.kind = svn_opt_revision_unspecified;
+ }
+
+ /* If no peg-rev was attached to the source URL, assume HEAD. */
+ if (src_peg_revision.kind == svn_opt_revision_unspecified)
+ src_peg_revision.kind = svn_opt_revision_head;
+
+ /* If no peg-rev was attached to a URL target, then assume HEAD; if
+ no peg-rev was attached to a non-URL target, then assume BASE. */
+ if (tgt_peg_revision.kind == svn_opt_revision_unspecified)
+ {
+ if (svn_path_is_url(target))
+ tgt_peg_revision.kind = svn_opt_revision_head;
+ else
+ tgt_peg_revision.kind = svn_opt_revision_base;
+ }
+
+ /* Do the real work, depending on the requested data flavor. */
+ if (opt_state->show_revs == svn_cl__show_revs_merged)
+ {
+ SVN_ERR(svn_client_mergeinfo_log(TRUE, target, &tgt_peg_revision,
+ source, &src_peg_revision,
+ print_log_rev, NULL,
+ TRUE, depth, NULL, ctx,
+ pool));
+ }
+ else if (opt_state->show_revs == svn_cl__show_revs_eligible)
+ {
+ SVN_ERR(svn_client_mergeinfo_log(FALSE, target, &tgt_peg_revision,
+ source, &src_peg_revision,
+ print_log_rev, NULL,
+ TRUE, depth, NULL, ctx,
+ pool));
+ }
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/mkdir-cmd.c b/subversion/svn/mkdir-cmd.c
new file mode 100644
index 0000000..64cb4f9
--- /dev/null
+++ b/subversion/svn/mkdir-cmd.c
@@ -0,0 +1,104 @@
+/*
+ * mkdir-cmd.c -- Subversion mkdir 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_pools.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__mkdir(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_array_header_t *targets;
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__assert_homogeneous_target_type(targets));
+
+ if (! svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *)))
+ {
+ ctx->log_msg_func3 = NULL;
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+ }
+ else
+ {
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
+ NULL, ctx->config, pool));
+ }
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ err = svn_client_mkdir4(targets, opt_state->parents,
+ opt_state->revprop_table,
+ (opt_state->quiet ? NULL : svn_cl__print_commit_info),
+ NULL, ctx, pool);
+
+ if (ctx->log_msg_func3)
+ err = svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool);
+
+ if (err)
+ {
+ if (err->apr_err == APR_EEXIST)
+ return svn_error_quick_wrap
+ (err, _("Try 'svn add' or 'svn add --non-recursive' instead?"));
+ else if (!(opt_state->parents) &&
+ (APR_STATUS_IS_ENOENT(err->apr_err) || /* in wc */
+ err->apr_err == SVN_ERR_FS_NOT_DIRECTORY ||
+ err->apr_err == SVN_ERR_FS_NOT_FOUND /* all ra layers */))
+ return svn_error_quick_wrap
+ (err, _("Try 'svn mkdir --parents' instead?"));
+ else
+ return svn_error_trace(err);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/move-cmd.c b/subversion/svn/move-cmd.c
new file mode 100644
index 0000000..795870a
--- /dev/null
+++ b/subversion/svn/move-cmd.c
@@ -0,0 +1,101 @@
+/*
+ * move-cmd.c -- Subversion move 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_error.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__move(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_array_header_t *targets;
+ const char *dst_path;
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, TRUE, pool));
+
+ if (targets->nelts < 2)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (opt_state->start_revision.kind != svn_opt_revision_unspecified
+ && opt_state->start_revision.kind != svn_opt_revision_head)
+ {
+ return svn_error_create
+ (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("Cannot specify revisions (except HEAD) with move operations"));
+ }
+
+ dst_path = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
+ apr_array_pop(targets);
+
+ if (! svn_path_is_url(dst_path))
+ {
+ ctx->log_msg_func3 = NULL;
+ if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
+ NULL, ctx->config, pool));
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ err = svn_client_move6(targets, dst_path,
+ TRUE, opt_state->parents, opt_state->revprop_table,
+ (opt_state->quiet ? NULL : svn_cl__print_commit_info),
+ NULL, ctx, pool);
+
+ if (err)
+ err = svn_cl__may_need_force(err);
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
+ else if (err)
+ return svn_error_trace(err);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/notify.c b/subversion/svn/notify.c
new file mode 100644
index 0000000..a785e53
--- /dev/null
+++ b/subversion/svn/notify.c
@@ -0,0 +1,1083 @@
+/*
+ * notify.c: feedback handlers for cmdline client.
+ *
+ * ====================================================================
+ * 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. ***/
+
+#define APR_WANT_STDIO
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include "svn_cmdline.h"
+#include "svn_pools.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/* Baton for notify and friends. */
+struct notify_baton
+{
+ svn_boolean_t received_some_change;
+ svn_boolean_t is_checkout;
+ svn_boolean_t is_export;
+ svn_boolean_t is_wc_to_repos_copy;
+ svn_boolean_t suppress_summary_lines;
+ svn_boolean_t sent_first_txdelta;
+ svn_boolean_t in_external;
+ svn_boolean_t had_print_error; /* Used to not keep printing error messages
+ when we've already had one print error. */
+
+ /* Conflict stats for update and merge. */
+ unsigned int text_conflicts;
+ unsigned int prop_conflicts;
+ unsigned int tree_conflicts;
+ unsigned int skipped_paths;
+
+ /* The cwd, for use in decomposing absolute paths. */
+ const char *path_prefix;
+};
+
+
+svn_error_t *
+svn_cl__print_conflict_stats(void *notify_baton, apr_pool_t *pool)
+{
+ struct notify_baton *nb = notify_baton;
+ unsigned int text_conflicts;
+ unsigned int prop_conflicts;
+ unsigned int tree_conflicts;
+ unsigned int skipped_paths;
+
+ text_conflicts = nb->text_conflicts;
+ prop_conflicts = nb->prop_conflicts;
+ tree_conflicts = nb->tree_conflicts;
+ skipped_paths = nb->skipped_paths;
+
+ if (text_conflicts > 0 || prop_conflicts > 0
+ || tree_conflicts > 0 || skipped_paths > 0)
+ SVN_ERR(svn_cmdline_printf(pool, "%s", _("Summary of conflicts:\n")));
+
+ if (text_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Text conflicts: %u\n"), text_conflicts));
+
+ if (prop_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Property conflicts: %u\n"), prop_conflicts));
+
+ if (tree_conflicts > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Tree conflicts: %u\n"), tree_conflicts));
+
+ if (skipped_paths > 0)
+ SVN_ERR(svn_cmdline_printf
+ (pool, _(" Skipped paths: %u\n"), skipped_paths));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements `svn_wc_notify_func2_t'.
+ * NOTE: This function can't fail, so we just ignore any print errors. */
+static void
+notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool)
+{
+ struct notify_baton *nb = baton;
+ char statchar_buf[5] = " ";
+ const char *path_local;
+ svn_error_t *err;
+
+ if (n->url)
+ path_local = n->url;
+ else
+ {
+ if (n->path_prefix)
+ path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path,
+ pool);
+ else /* skip nb->path_prefix, if it's non-null */
+ path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path,
+ pool);
+ }
+
+ switch (n->action)
+ {
+ case svn_wc_notify_skip:
+ nb->skipped_paths++;
+ if (n->content_state == svn_wc_notify_state_missing)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped missing target: '%s'\n"),
+ path_local)))
+ goto print_error;
+ }
+ else if (n->content_state == svn_wc_notify_state_source_missing)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped target: '%s' -- copy-source is missing\n"),
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, _("Skipped '%s'\n"), path_local)))
+ goto print_error;
+ }
+ break;
+ case svn_wc_notify_update_skip_obstruction:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- An obstructing working copy was found\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_skip_working_only:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Has no versioned parent\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_skip_access_denied:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Access denied\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_skip_conflicted:
+ nb->skipped_paths++;
+ if ((err = svn_cmdline_printf(
+ pool, _("Skipped '%s' -- Node remains in conflict\n"),
+ path_local)))
+ goto print_error;
+ break;
+ case svn_wc_notify_update_delete:
+ case svn_wc_notify_exclude:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "D %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_external_removed:
+ nb->received_some_change = TRUE;
+ if (n->err && n->err->message)
+ {
+ if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n",
+ path_local, n->err->message)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n",
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_update_replace:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "R %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_add:
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ if ((err = svn_cmdline_printf(pool, "C %s\n", path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "A %s\n", path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_exists:
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ statchar_buf[0] = 'C';
+ }
+ else
+ statchar_buf[0] = 'E';
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_merged)
+ statchar_buf[1] = 'G';
+
+ if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_restore:
+ if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revert:
+ if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_revert:
+ if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- "
+ "try updating instead.\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_resolved:
+ if ((err = svn_cmdline_printf(pool,
+ _("Resolved conflicted state of '%s'\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_add:
+ /* We *should* only get the MIME_TYPE if PATH is a file. If we
+ do get it, and the mime-type is not textual, note that this
+ is a binary addition. */
+ if (n->mime_type && (svn_mime_type_is_binary(n->mime_type)))
+ {
+ if ((err = svn_cmdline_printf(pool, "A (bin) %s\n",
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool, "A %s\n",
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_delete:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, "D %s\n",
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_patch:
+ {
+ nb->received_some_change = TRUE;
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ statchar_buf[0] = 'C';
+ }
+ else if (n->kind == svn_node_file)
+ {
+ if (n->content_state == svn_wc_notify_state_merged)
+ statchar_buf[0] = 'G';
+ else if (n->content_state == svn_wc_notify_state_changed)
+ statchar_buf[0] = 'U';
+ }
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_changed)
+ statchar_buf[1] = 'U';
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
+ {
+ if ((err = svn_cmdline_printf(pool, "%s %s\n",
+ statchar_buf, path_local)))
+ goto print_error;
+ }
+ }
+ break;
+
+ case svn_wc_notify_patch_applied_hunk:
+ nb->received_some_change = TRUE;
+ if (n->hunk_original_start != n->hunk_matched_line)
+ {
+ apr_uint64_t off;
+ const char *s;
+ const char *minus;
+
+ if (n->hunk_matched_line > n->hunk_original_start)
+ {
+ off = n->hunk_matched_line - n->hunk_original_start;
+ minus = "";
+ }
+ else
+ {
+ off = n->hunk_original_start - n->hunk_matched_line;
+ minus = "-";
+ }
+
+ /* ### We're creating the localized strings without
+ * ### APR_INT64_T_FMT since it isn't translator-friendly */
+ if (n->hunk_fuzz)
+ {
+
+ if (n->prop_name)
+ {
+ s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with offset %s");
+
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT
+ " and fuzz %lu (%s)\n",
+ (char *)NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->hunk_fuzz,
+ n->prop_name);
+ }
+ else
+ {
+ s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with offset %s");
+
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT
+ " and fuzz %lu\n",
+ (char *)NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->hunk_fuzz);
+ }
+
+ if (err)
+ goto print_error;
+ }
+ else
+ {
+
+ if (n->prop_name)
+ {
+ s = _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with offset %s");
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT" (%s)\n",
+ (char *)NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off, n->prop_name);
+ }
+ else
+ {
+ s = _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with offset %s");
+ err = svn_cmdline_printf(pool,
+ apr_pstrcat(pool, s,
+ "%"APR_UINT64_T_FMT"\n",
+ (char *)NULL),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ minus, off);
+ }
+
+ if (err)
+ goto print_error;
+ }
+ }
+ else if (n->hunk_fuzz)
+ {
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> applied hunk ## -%lu,%lu +%lu,%lu ## "
+ "with fuzz %lu (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->hunk_fuzz,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> applied hunk @@ -%lu,%lu +%lu,%lu @@ "
+ "with fuzz %lu\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->hunk_fuzz);
+ if (err)
+ goto print_error;
+
+ }
+ break;
+
+ case svn_wc_notify_patch_rejected_hunk:
+ nb->received_some_change = TRUE;
+
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> rejected hunk "
+ "## -%lu,%lu +%lu,%lu ## (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> rejected hunk "
+ "@@ -%lu,%lu +%lu,%lu @@\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_patch_hunk_already_applied:
+ nb->received_some_change = TRUE;
+ if (n->prop_name)
+ err = svn_cmdline_printf(pool,
+ _("> hunk "
+ "## -%lu,%lu +%lu,%lu ## "
+ "already applied (%s)\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length,
+ n->prop_name);
+ else
+ err = svn_cmdline_printf(pool,
+ _("> hunk "
+ "@@ -%lu,%lu +%lu,%lu @@ "
+ "already applied\n"),
+ n->hunk_original_start,
+ n->hunk_original_length,
+ n->hunk_modified_start,
+ n->hunk_modified_length);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_update:
+ case svn_wc_notify_merge_record_info:
+ {
+ if (n->content_state == svn_wc_notify_state_conflicted)
+ {
+ nb->text_conflicts++;
+ statchar_buf[0] = 'C';
+ }
+ else if (n->kind == svn_node_file)
+ {
+ if (n->content_state == svn_wc_notify_state_merged)
+ statchar_buf[0] = 'G';
+ else if (n->content_state == svn_wc_notify_state_changed)
+ statchar_buf[0] = 'U';
+ }
+
+ if (n->prop_state == svn_wc_notify_state_conflicted)
+ {
+ nb->prop_conflicts++;
+ statchar_buf[1] = 'C';
+ }
+ else if (n->prop_state == svn_wc_notify_state_merged)
+ statchar_buf[1] = 'G';
+ else if (n->prop_state == svn_wc_notify_state_changed)
+ statchar_buf[1] = 'U';
+
+ if (n->lock_state == svn_wc_notify_lock_state_unlocked)
+ statchar_buf[2] = 'B';
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
+ nb->received_some_change = TRUE;
+
+ if (statchar_buf[0] != ' ' || statchar_buf[1] != ' '
+ || statchar_buf[2] != ' ')
+ {
+ if ((err = svn_cmdline_printf(pool, "%s %s\n",
+ statchar_buf, path_local)))
+ goto print_error;
+ }
+ }
+ break;
+
+ case svn_wc_notify_update_external:
+ /* Remember that we're now "inside" an externals definition. */
+ nb->in_external = TRUE;
+
+ /* Currently this is used for checkouts and switches too. If we
+ want different output, we'll have to add new actions. */
+ if ((err = svn_cmdline_printf(pool,
+ _("\nFetching external item into '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_external:
+ /* If we are currently inside the handling of an externals
+ definition, then we can simply present n->err as a warning
+ and feel confident that after this, we aren't handling that
+ externals definition any longer. */
+ if (nb->in_external)
+ {
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ nb->in_external = FALSE;
+ if ((err = svn_cmdline_printf(pool, "\n")))
+ goto print_error;
+ }
+ /* Otherwise, we'll just print two warnings. Why? Because
+ svn_handle_warning2() only shows the single "best message",
+ but we have two pretty important ones: that the external at
+ '/some/path' didn't pan out, and then the more specific
+ reason why (from n->err). */
+ else
+ {
+ svn_error_t *warn_err =
+ svn_error_createf(SVN_ERR_BASE, NULL,
+ _("Error handling externals definition for '%s':"),
+ path_local);
+ svn_handle_warning2(stderr, warn_err, "svn: ");
+ svn_error_clear(warn_err);
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ }
+ break;
+
+ case svn_wc_notify_update_started:
+ if (! (nb->suppress_summary_lines ||
+ nb->in_external ||
+ nb->is_checkout ||
+ nb->is_export))
+ {
+ if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"),
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_update_completed:
+ {
+ if (! nb->suppress_summary_lines)
+ {
+ if (SVN_IS_VALID_REVNUM(n->revision))
+ {
+ if (nb->is_export)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Exported external at revision %ld.\n")
+ : _("Exported revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else if (nb->is_checkout)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Checked out external at revision %ld.\n")
+ : _("Checked out revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else
+ {
+ if (nb->received_some_change)
+ {
+ nb->received_some_change = FALSE;
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("Updated external to revision %ld.\n")
+ : _("Updated to revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External at revision %ld.\n")
+ : _("At revision %ld.\n"),
+ n->revision)))
+ goto print_error;
+ }
+ }
+ }
+ else /* no revision */
+ {
+ if (nb->is_export)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External export complete.\n")
+ : _("Export complete.\n"))))
+ goto print_error;
+ }
+ else if (nb->is_checkout)
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External checkout complete.\n")
+ : _("Checkout complete.\n"))))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf
+ (pool, nb->in_external
+ ? _("External update complete.\n")
+ : _("Update complete.\n"))))
+ goto print_error;
+ }
+ }
+ }
+ }
+
+ if (nb->in_external)
+ {
+ nb->in_external = FALSE;
+ if ((err = svn_cmdline_printf(pool, "\n")))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_status_external:
+ if ((err = svn_cmdline_printf
+ (pool, _("\nPerforming status on external item at '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_status_completed:
+ if (SVN_IS_VALID_REVNUM(n->revision))
+ if ((err = svn_cmdline_printf(pool,
+ _("Status against revision: %6ld\n"),
+ n->revision)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_modified:
+ /* xgettext: Align the %s's on this and the following 4 messages */
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Sending copy of %s\n")
+ : _("Sending %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_added:
+ case svn_wc_notify_commit_copied:
+ if (n->mime_type && svn_mime_type_is_binary(n->mime_type))
+ {
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Adding copy of (bin) %s\n")
+ : _("Adding (bin) %s\n"),
+ path_local)))
+ goto print_error;
+ }
+ else
+ {
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Adding copy of %s\n")
+ : _("Adding %s\n"),
+ path_local)))
+ goto print_error;
+ }
+ break;
+
+ case svn_wc_notify_commit_deleted:
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Deleting copy of %s\n")
+ : _("Deleting %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_replaced:
+ case svn_wc_notify_commit_copied_replaced:
+ if ((err = svn_cmdline_printf(pool,
+ nb->is_wc_to_repos_copy
+ ? _("Replacing copy of %s\n")
+ : _("Replacing %s\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_commit_postfix_txdelta:
+ if (! nb->sent_first_txdelta)
+ {
+ nb->sent_first_txdelta = TRUE;
+ if ((err = svn_cmdline_printf(pool,
+ _("Transmitting file data "))))
+ goto print_error;
+ }
+
+ if ((err = svn_cmdline_printf(pool, ".")))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_locked:
+ if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
+ path_local, n->lock->owner)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_unlocked:
+ if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_failed_lock:
+ case svn_wc_notify_failed_unlock:
+ svn_handle_warning2(stderr, n->err, "svn: ");
+ break;
+
+ case svn_wc_notify_changelist_set:
+ if ((err = svn_cmdline_printf(pool, "A [%s] %s\n",
+ n->changelist_name, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_changelist_clear:
+ case svn_wc_notify_changelist_moved:
+ if ((err = svn_cmdline_printf(pool,
+ "D [%s] %s\n",
+ n->changelist_name, path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_begin:
+ if (n->merge_range == NULL)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging differences between "
+ "repository URLs into '%s':\n"),
+ path_local);
+ else if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging r%ld through r%ld into "
+ "'%s':\n"),
+ n->merge_range->start + 1,
+ n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging r%ld through r%ld "
+ "into '%s':\n"),
+ n->merge_range->start,
+ n->merge_range->end + 1, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_record_info_begin:
+ if (!n->merge_range)
+ {
+ err = svn_cmdline_printf(pool,
+ _("--- Recording mergeinfo for merge "
+ "between repository URLs into '%s':\n"),
+ path_local);
+ }
+ else
+ {
+ if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for merge of r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"),
+ n->merge_range->start + 1, n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(
+ pool,
+ _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"),
+ n->merge_range->start, n->merge_range->end + 1, path_local);
+ }
+
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_merge_elide_info:
+ if ((err = svn_cmdline_printf(pool,
+ _("--- Eliding mergeinfo from '%s':\n"),
+ path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_foreign_merge_begin:
+ if (n->merge_range == NULL)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging differences between "
+ "foreign repository URLs into '%s':\n"),
+ path_local);
+ else if (n->merge_range->start == n->merge_range->end - 1
+ || n->merge_range->start == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging (from foreign repository) "
+ "r%ld into '%s':\n"),
+ n->merge_range->end, path_local);
+ else if (n->merge_range->start - 1 == n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging (from foreign "
+ "repository) r%ld into '%s':\n"),
+ n->merge_range->start, path_local);
+ else if (n->merge_range->start < n->merge_range->end)
+ err = svn_cmdline_printf(pool,
+ _("--- Merging (from foreign repository) "
+ "r%ld through r%ld into '%s':\n"),
+ n->merge_range->start + 1,
+ n->merge_range->end, path_local);
+ else /* n->merge_range->start > n->merge_range->end - 1 */
+ err = svn_cmdline_printf(pool,
+ _("--- Reverse-merging (from foreign "
+ "repository) r%ld through r%ld into "
+ "'%s':\n"),
+ n->merge_range->start,
+ n->merge_range->end + 1, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_tree_conflict:
+ nb->tree_conflicts++;
+ if ((err = svn_cmdline_printf(pool, " C %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_add:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " A %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_update:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " U %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_update_shadowed_delete:
+ nb->received_some_change = TRUE;
+ if ((err = svn_cmdline_printf(pool, " D %s\n", path_local)))
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_modified:
+ case svn_wc_notify_property_added:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' set on '%s'\n"),
+ n->prop_name, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_deleted:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' deleted from '%s'.\n"),
+ n->prop_name, path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_property_deleted_nonexistent:
+ err = svn_cmdline_printf(pool,
+ _("Attempting to delete nonexistent "
+ "property '%s' on '%s'\n"), n->prop_name,
+ path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revprop_set:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' set on repository revision %ld\n"),
+ n->prop_name, n->revision);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_revprop_deleted:
+ err = svn_cmdline_printf(pool,
+ _("property '%s' deleted from repository revision %ld\n"),
+ n->prop_name, n->revision);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_upgraded_path:
+ err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_url_redirect:
+ err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"),
+ n->url);
+ if (err)
+ goto print_error;
+ break;
+
+ case svn_wc_notify_path_nonexistent:
+ err = svn_cmdline_printf(pool, _("'%s' is not under version control"),
+ path_local);
+ if (err)
+ goto print_error;
+ break;
+
+ default:
+ break;
+ }
+
+ if ((err = svn_cmdline_fflush(stdout)))
+ goto print_error;
+
+ return;
+
+ print_error:
+ /* If we had no errors before, print this error to stderr. Else, don't print
+ anything. The user already knows there were some output errors,
+ so there is no point in flooding her with an error per notification. */
+ if (!nb->had_print_error)
+ {
+ nb->had_print_error = TRUE;
+ /* Issue #3014:
+ * Don't print anything on broken pipes. The pipe was likely
+ * closed by the process at the other end. We expect that
+ * process to perform error reporting as necessary.
+ *
+ * ### This assumes that there is only one error in a chain for
+ * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+ if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+ svn_handle_error2(err, stderr, FALSE, "svn: ");
+ }
+ svn_error_clear(err);
+}
+
+
+svn_error_t *
+svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
+ void **notify_baton_p,
+ svn_boolean_t suppress_summary_lines,
+ apr_pool_t *pool)
+{
+ struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb));
+
+ nb->received_some_change = FALSE;
+ nb->sent_first_txdelta = FALSE;
+ nb->is_checkout = FALSE;
+ nb->is_export = FALSE;
+ nb->is_wc_to_repos_copy = FALSE;
+ nb->suppress_summary_lines = suppress_summary_lines;
+ nb->in_external = FALSE;
+ nb->had_print_error = FALSE;
+ nb->text_conflicts = 0;
+ nb->prop_conflicts = 0;
+ nb->tree_conflicts = 0;
+ nb->skipped_paths = 0;
+ SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
+
+ *notify_func_p = notify;
+ *notify_baton_p = nb;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__notifier_mark_checkout(void *baton)
+{
+ struct notify_baton *nb = baton;
+
+ nb->is_checkout = TRUE;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__notifier_mark_export(void *baton)
+{
+ struct notify_baton *nb = baton;
+
+ nb->is_export = TRUE;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__notifier_mark_wc_to_repos_copy(void *baton)
+{
+ struct notify_baton *nb = baton;
+
+ nb->is_wc_to_repos_copy = TRUE;
+ return SVN_NO_ERROR;
+}
+
+void
+svn_cl__check_externals_failed_notify_wrapper(void *baton,
+ const svn_wc_notify_t *n,
+ apr_pool_t *pool)
+{
+ struct svn_cl__check_externals_failed_notify_baton *nwb = baton;
+
+ if (n->action == svn_wc_notify_failed_external)
+ nwb->had_externals_error = TRUE;
+
+ if (nwb->wrapped_func)
+ nwb->wrapped_func(nwb->wrapped_baton, n, pool);
+}
+
diff --git a/subversion/svn/patch-cmd.c b/subversion/svn/patch-cmd.c
new file mode 100644
index 0000000..24abb47
--- /dev/null
+++ b/subversion/svn/patch-cmd.c
@@ -0,0 +1,98 @@
+/*
+ * patch-cmd.c -- Apply changes to a working copy.
+ *
+ * ====================================================================
+ * 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_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_types.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__patch(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state;
+ svn_client_ctx_t *ctx;
+ apr_array_header_t *targets;
+ const char *abs_patch_path;
+ const char *patch_path;
+ const char *abs_target_path;
+ const char *target_path;
+
+ opt_state = ((svn_cl__cmd_baton_t *)baton)->opt_state;
+ ctx = ((svn_cl__cmd_baton_t *)baton)->ctx;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ patch_path = APR_ARRAY_IDX(targets, 0, const char *);
+
+ SVN_ERR(svn_cl__check_target_is_local_path(patch_path));
+
+ SVN_ERR(svn_dirent_get_absolute(&abs_patch_path, patch_path, pool));
+
+ if (targets->nelts == 1)
+ target_path = ""; /* "" is the canonical form of "." */
+ else
+ {
+ target_path = APR_ARRAY_IDX(targets, 1, const char *);
+
+ SVN_ERR(svn_cl__check_target_is_local_path(target_path));
+ }
+ SVN_ERR(svn_dirent_get_absolute(&abs_target_path, target_path, pool));
+
+ SVN_ERR(svn_client_patch(abs_patch_path, abs_target_path,
+ opt_state->dry_run, opt_state->strip,
+ opt_state->reverse_diff,
+ opt_state->ignore_whitespace,
+ TRUE, NULL, NULL, ctx, pool));
+
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__print_conflict_stats(ctx->notify_baton2, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/propdel-cmd.c b/subversion/svn/propdel-cmd.c
new file mode 100644
index 0000000..28c9597
--- /dev/null
+++ b/subversion/svn/propdel-cmd.c
@@ -0,0 +1,103 @@
+/*
+ * propdel-cmd.c -- Remove property from files/dirs
+ *
+ * ====================================================================
+ * 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_cmdline.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_path.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__propdel(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;
+ const char *pname, *pname_utf8;
+ apr_array_header_t *args, *targets;
+
+ /* Get the property's name (and a UTF-8 version of that name). */
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
+ pname = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
+ /* No need to check svn_prop_name_is_valid for *deleting*
+ properties, and it may even be useful to allow, in case invalid
+ properties sneaked through somehow. */
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+
+ /* Add "." if user passed 0 file arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ if (opt_state->revprop) /* operate on a revprop */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, pool));
+
+ /* Let libsvn_client do the real work. */
+ SVN_ERR(svn_client_revprop_set2(pname_utf8, NULL, NULL,
+ URL, &(opt_state->start_revision),
+ &rev, FALSE, ctx, pool));
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("Cannot specify revision for deleting versioned property '%s'"),
+ pname);
+ }
+ else /* operate on a normal, versioned property (not a revprop) */
+ {
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ /* For each target, remove the property PNAME. */
+ SVN_ERR(svn_client_propset_local(pname_utf8, NULL, targets,
+ opt_state->depth, FALSE,
+ opt_state->changelists, ctx, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/propedit-cmd.c b/subversion/svn/propedit-cmd.c
new file mode 100644
index 0000000..0e36a34
--- /dev/null
+++ b/subversion/svn/propedit-cmd.c
@@ -0,0 +1,351 @@
+/*
+ * propedit-cmd.c -- Edit properties of files/dirs using $EDITOR
+ *
+ * ====================================================================
+ * 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_cmdline.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_props.h"
+#include "cl.h"
+
+#include "private/svn_wc_private.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+struct commit_info_baton
+{
+ const char *pname_utf8;
+ const char *target_local;
+};
+
+static svn_error_t *
+commit_info_handler(const svn_commit_info_t *commit_info,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct commit_info_baton *cib = baton;
+
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Set new value for property '%s' on '%s'\n"),
+ cib->pname_utf8, cib->target_local));
+ SVN_ERR(svn_cl__print_commit_info(commit_info, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__propedit(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;
+ const char *pname, *pname_utf8;
+ apr_array_header_t *args, *targets;
+
+ /* Validate the input and get the property's name (and a UTF-8
+ version of that name). */
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
+ pname = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
+ if (! svn_prop_name_is_valid(pname_utf8))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("'%s' is not a valid Subversion property name"),
+ pname_utf8);
+ if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
+ return svn_error_create
+ (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("--encoding option applies only to textual"
+ " Subversion-controlled properties"));
+
+ /* Suck up all the remaining arguments into a targets array */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* We do our own notifications */
+ ctx->notify_func2 = NULL;
+
+ if (opt_state->revprop) /* operate on a revprop */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+ svn_string_t *propval;
+ svn_string_t original_propval;
+ const char *temp_dir;
+
+ /* Implicit "." is okay for revision properties; it just helps
+ us find the right repository. */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, pool));
+
+ /* Fetch the current property. */
+ SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
+ URL, &(opt_state->start_revision),
+ &rev, ctx, pool));
+
+ if (! propval)
+ {
+ propval = svn_string_create("", pool);
+ /* This is how we signify to svn_client_revprop_set2() that
+ we want it to check that the original value hasn't
+ changed, but that that original value was non-existent: */
+ original_propval.data = NULL; /* and .len is ignored */
+ }
+ else
+ {
+ original_propval = *propval;
+ }
+
+ /* Run the editor on a temporary file which contains the
+ original property value... */
+ SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
+ SVN_ERR(svn_cl__edit_string_externally
+ (&propval, NULL,
+ opt_state->editor_cmd, temp_dir,
+ propval, "svn-prop",
+ ctx->config,
+ svn_prop_needs_translation(pname_utf8),
+ opt_state->encoding, pool));
+
+ /* ...and re-set the property's value accordingly. */
+ if (propval)
+ {
+ SVN_ERR(svn_client_revprop_set2(pname_utf8,
+ propval, &original_propval,
+ URL, &(opt_state->start_revision),
+ &rev, opt_state->force, ctx, pool));
+
+ SVN_ERR
+ (svn_cmdline_printf
+ (pool,
+ _("Set new value for property '%s' on revision %ld\n"),
+ pname_utf8, rev));
+ }
+ else
+ {
+ SVN_ERR(svn_cmdline_printf
+ (pool, _("No changes to property '%s' on revision %ld\n"),
+ pname_utf8, rev));
+ }
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_createf
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot specify revision for editing versioned property '%s'"),
+ pname_utf8);
+ }
+ else /* operate on a normal, versioned property (not a revprop) */
+ {
+ apr_pool_t *subpool = svn_pool_create(pool);
+ struct commit_info_baton cib;
+ int i;
+
+ /* The customary implicit dot rule has been prone to user error
+ * here. For example, Jon Trowbridge <trow@gnu.og> did
+ *
+ * $ svn propedit HACKING
+ *
+ * and then when he closed his editor, he was surprised to see
+ *
+ * Set new value for property 'HACKING' on ''
+ *
+ * ...meaning that the property named 'HACKING' had been set on
+ * the current working directory, with the value taken from the
+ * editor. So we don't do the implicit dot thing anymore; an
+ * explicit target is always required when editing a versioned
+ * property.
+ */
+ if (targets->nelts == 0)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Explicit target argument required"));
+ }
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ cib.pname_utf8 = pname_utf8;
+
+ /* For each target, edit the property PNAME. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ apr_hash_t *props;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_string_t *propval, *edited_propval;
+ const char *base_dir = target;
+ const char *target_local;
+ const char *local_abspath;
+ svn_node_kind_t kind;
+ svn_opt_revision_t peg_revision;
+ svn_revnum_t base_rev = SVN_INVALID_REVNUM;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ if (!svn_path_is_url(target))
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, subpool));
+
+ /* Propedits can only happen on HEAD or the working copy, so
+ the peg revision can be as unspecified. */
+ peg_revision.kind = svn_opt_revision_unspecified;
+
+ /* Fetch the current property. */
+ SVN_ERR(svn_client_propget4(&props, pname_utf8,
+ svn_path_is_url(target)
+ ? target : local_abspath,
+ &peg_revision,
+ &(opt_state->start_revision),
+ &base_rev, svn_depth_empty,
+ NULL, ctx, subpool, subpool));
+
+ /* Get the property value. */
+ propval = apr_hash_get(props,
+ svn_path_is_url(target)
+ ? target : local_abspath,
+ APR_HASH_KEY_STRING);
+ if (! propval)
+ propval = svn_string_create("", subpool);
+
+ if (svn_path_is_url(target))
+ {
+ /* For URLs, put the temporary file in the current directory. */
+ base_dir = ".";
+ }
+ else
+ {
+ if (opt_state->message || opt_state->filedata ||
+ opt_state->revprop_table)
+ {
+ return svn_error_create
+ (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
+ _("Local, non-commit operations do not take a log message "
+ "or revision properties"));
+ }
+
+ /* Split the path if it is a file path. */
+ SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, FALSE,
+ subpool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(
+ SVN_ERR_ENTRY_NOT_FOUND, NULL,
+ _("'%s' does not appear to be a working copy path"), target);
+ if (kind == svn_node_file)
+ base_dir = svn_dirent_dirname(target, subpool);
+ }
+
+ /* Run the editor on a temporary file which contains the
+ original property value... */
+ SVN_ERR(svn_cl__edit_string_externally(&edited_propval, NULL,
+ opt_state->editor_cmd,
+ base_dir,
+ propval,
+ "svn-prop",
+ ctx->config,
+ svn_prop_needs_translation
+ (pname_utf8),
+ opt_state->encoding,
+ subpool));
+
+ target_local = svn_path_is_url(target) ? target
+ : svn_dirent_local_style(target, subpool);
+ cib.target_local = target_local;
+
+ /* ...and re-set the property's value accordingly. */
+ if (edited_propval && !svn_string_compare(propval, edited_propval))
+ {
+ svn_error_t *err = SVN_NO_ERROR;
+
+ svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data,
+ subpool);
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
+ opt_state, NULL, ctx->config,
+ subpool));
+ if (svn_path_is_url(target))
+ {
+ err = svn_client_propset_remote(pname_utf8, edited_propval,
+ target, opt_state->force,
+ base_rev,
+ opt_state->revprop_table,
+ commit_info_handler, &cib,
+ ctx, subpool);
+ }
+ else
+ {
+ apr_array_header_t *targs = apr_array_make(subpool, 1,
+ sizeof(const char *));
+
+ APR_ARRAY_PUSH(targs, const char *) = target;
+ err = svn_client_propset_local(pname_utf8, edited_propval,
+ targs, svn_depth_empty,
+ opt_state->force, NULL,
+ ctx, subpool);
+ }
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
+ err, pool));
+ else if (err)
+ return svn_error_trace(err);
+
+ /* Print a message if we successfully committed or if it
+ was just a wc propset (but not if the user aborted an URL
+ propedit). */
+ if (!svn_path_is_url(target))
+ SVN_ERR(svn_cmdline_printf(
+ subpool, _("Set new value for property '%s' on '%s'\n"),
+ pname_utf8, target_local));
+ }
+ else
+ {
+ SVN_ERR
+ (svn_cmdline_printf
+ (subpool, _("No changes to property '%s' on '%s'\n"),
+ pname_utf8, target_local));
+ }
+ }
+ svn_pool_destroy(subpool);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/propget-cmd.c b/subversion/svn/propget-cmd.c
new file mode 100644
index 0000000..fcb6503
--- /dev/null
+++ b/subversion/svn/propget-cmd.c
@@ -0,0 +1,358 @@
+/*
+ * propget-cmd.c -- Print properties and values of files/dirs
+ *
+ * ====================================================================
+ * 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_cmdline.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_sorts.h"
+#include "svn_subst.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_xml.h"
+#include "cl.h"
+
+#include "private/svn_cmdline_private.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+static svn_error_t *
+stream_write(svn_stream_t *out,
+ const char *data,
+ apr_size_t len)
+{
+ apr_size_t write_len = len;
+
+ /* We're gonna bail on an incomplete write here only because we know
+ that this stream is really stdout, which should never be blocking
+ on us. */
+ SVN_ERR(svn_stream_write(out, data, &write_len));
+ if (write_len != len)
+ return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ _("Error writing to stream"));
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+print_properties_xml(const char *pname,
+ apr_hash_t *props,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *sorted_props;
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
+ for (i = 0; i < sorted_props->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
+ const char *filename = item.key;
+ svn_string_t *propval = item.value;
+ svn_stringbuf_t *sb = NULL;
+
+ svn_pool_clear(iterpool);
+
+ svn_xml_make_open_tag(&sb, iterpool, svn_xml_normal, "target",
+ "path", filename, NULL);
+ svn_cmdline__print_xml_prop(&sb, pname, propval, iterpool);
+ svn_xml_make_close_tag(&sb, iterpool, "target");
+
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Print the properties in PROPS to the stream OUT. PROPS is a hash mapping
+ * (const char *) path to (svn_string_t) property value.
+ * If IS_URL is true, all paths are URLs, else all paths are local paths.
+ * PNAME_UTF8 is the property name of all the properties.
+ * If PRINT_FILENAMES is true, print the item's path before each property.
+ * If OMIT_NEWLINE is true, don't add a newline at the end of each property.
+ * If LIKE_PROPLIST is true, print everything in a more verbose format
+ * like "svn proplist -v" does.
+ * */
+static svn_error_t *
+print_properties(svn_stream_t *out,
+ svn_boolean_t is_url,
+ const char *pname_utf8,
+ apr_hash_t *props,
+ svn_boolean_t print_filenames,
+ svn_boolean_t omit_newline,
+ svn_boolean_t like_proplist,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *sorted_props;
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ const char *path_prefix;
+
+ SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
+
+ sorted_props = svn_sort__hash(props, svn_sort_compare_items_as_paths, pool);
+ for (i = 0; i < sorted_props->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
+ const char *filename = item.key;
+ svn_string_t *propval = item.value;
+
+ svn_pool_clear(iterpool);
+
+ if (print_filenames)
+ {
+ const char *header;
+
+ /* Print the file name. */
+
+ if (! is_url)
+ filename = svn_cl__local_style_skip_ancestor(path_prefix, filename,
+ iterpool);
+
+ /* In verbose mode, print exactly same as "proplist" does;
+ * otherwise, print a brief header. */
+ header = apr_psprintf(iterpool, like_proplist
+ ? _("Properties on '%s':\n")
+ : "%s - ", filename);
+ SVN_ERR(svn_cmdline_cstring_from_utf8(&header, header, iterpool));
+ SVN_ERR(svn_subst_translate_cstring2(header, &header,
+ APR_EOL_STR, /* 'native' eol */
+ FALSE, /* no repair */
+ NULL, /* no keywords */
+ FALSE, /* no expansion */
+ iterpool));
+ SVN_ERR(stream_write(out, header, strlen(header)));
+ }
+
+ if (like_proplist)
+ {
+ /* Print the property name and value just as "proplist -v" does */
+ apr_hash_t *hash = apr_hash_make(iterpool);
+
+ apr_hash_set(hash, pname_utf8, APR_HASH_KEY_STRING, propval);
+ SVN_ERR(svn_cl__print_prop_hash(out, hash, FALSE, iterpool));
+ }
+ else
+ {
+ /* If this is a special Subversion property, it is stored as
+ UTF8, so convert to the native format. */
+ if (svn_prop_needs_translation(pname_utf8))
+ SVN_ERR(svn_subst_detranslate_string(&propval, propval,
+ TRUE, iterpool));
+
+ SVN_ERR(stream_write(out, propval->data, propval->len));
+
+ if (! omit_newline)
+ SVN_ERR(stream_write(out, APR_EOL_STR,
+ strlen(APR_EOL_STR)));
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__propget(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;
+ const char *pname, *pname_utf8;
+ apr_array_header_t *args, *targets;
+ svn_stream_t *out;
+
+ if (opt_state->verbose && (opt_state->revprop || opt_state->strict
+ || opt_state->xml))
+ return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
+ _("--verbose cannot be used with --revprop or "
+ "--strict or --xml"));
+
+ /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version
+ thereof) */
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
+ pname = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
+ if (! svn_prop_name_is_valid(pname_utf8))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("'%s' is not a valid Subversion property name"),
+ pname_utf8);
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+
+ /* Add "." if user passed 0 file arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ /* Open a stream to stdout. */
+ SVN_ERR(svn_stream_for_stdout(&out, pool));
+
+ if (opt_state->revprop) /* operate on a revprop */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+ svn_string_t *propval;
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, pool));
+
+ /* Let libsvn_client do the real work. */
+ SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
+ URL, &(opt_state->start_revision),
+ &rev, ctx, pool));
+
+ if (propval != NULL)
+ {
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb = NULL;
+ char *revstr = apr_psprintf(pool, "%ld", rev);
+
+ SVN_ERR(svn_cl__xml_print_header("properties", pool));
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal,
+ "revprops",
+ "rev", revstr, NULL);
+
+ svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, pool);
+
+ svn_xml_make_close_tag(&sb, pool, "revprops");
+
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ SVN_ERR(svn_cl__xml_print_footer("properties", pool));
+ }
+ else
+ {
+ svn_string_t *printable_val = propval;
+
+ /* If this is a special Subversion property, it is stored as
+ UTF8 and LF, so convert to the native locale and eol-style. */
+
+ if (svn_prop_needs_translation(pname_utf8))
+ SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
+ TRUE, pool));
+
+ SVN_ERR(stream_write(out, printable_val->data,
+ printable_val->len));
+ if (! opt_state->strict)
+ SVN_ERR(stream_write(out, APR_EOL_STR, strlen(APR_EOL_STR)));
+ }
+ }
+ }
+ else /* operate on a normal, versioned property (not a revprop) */
+ {
+ apr_pool_t *subpool = svn_pool_create(pool);
+ int i;
+
+ if (opt_state->xml)
+ SVN_ERR(svn_cl__xml_print_header("properties", subpool));
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ /* Strict mode only makes sense for a single target. So make
+ sure we have only a single target, and that we're not being
+ asked to recurse on that target. */
+ if (opt_state->strict
+ && ((targets->nelts > 1) || (opt_state->depth != svn_depth_empty)))
+ return svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Strict output of property values only available for single-"
+ "target, non-recursive propget operations"));
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ apr_hash_t *props;
+ svn_boolean_t print_filenames;
+ svn_boolean_t omit_newline;
+ svn_boolean_t like_proplist;
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ /* Check for a peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ subpool));
+
+ if (!svn_path_is_url(truepath))
+ SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
+
+ SVN_ERR(svn_client_propget4(&props, pname_utf8, truepath,
+ &peg_revision,
+ &(opt_state->start_revision),
+ NULL, opt_state->depth,
+ opt_state->changelists, ctx, subpool,
+ subpool));
+
+ /* Any time there is more than one thing to print, or where
+ the path associated with a printed thing is not obvious,
+ we'll print filenames. That is, unless we've been told
+ not to do so with the --strict option. */
+ print_filenames = ((opt_state->depth > svn_depth_empty
+ || targets->nelts > 1
+ || apr_hash_count(props) > 1
+ || opt_state->verbose)
+ && (! opt_state->strict));
+ omit_newline = opt_state->strict;
+ like_proplist = opt_state->verbose && !opt_state->strict;
+
+ if (opt_state->xml)
+ SVN_ERR(print_properties_xml(pname_utf8, props, subpool));
+ else
+ SVN_ERR(print_properties(out, svn_path_is_url(target), pname_utf8,
+ props, print_filenames, omit_newline,
+ like_proplist, subpool));
+ }
+
+ if (opt_state->xml)
+ SVN_ERR(svn_cl__xml_print_footer("properties", subpool));
+
+ svn_pool_destroy(subpool);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/proplist-cmd.c b/subversion/svn/proplist-cmd.c
new file mode 100644
index 0000000..64cb055
--- /dev/null
+++ b/subversion/svn/proplist-cmd.c
@@ -0,0 +1,249 @@
+/*
+ * proplist-cmd.c -- List properties of files/dirs
+ *
+ * ====================================================================
+ * 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_cmdline.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_xml.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+typedef struct proplist_baton_t
+{
+ svn_cl__opt_state_t *opt_state;
+ svn_boolean_t is_url;
+} proplist_baton_t;
+
+
+/*** Code. ***/
+
+/* This implements the svn_proplist_receiver_t interface, printing XML to
+ stdout. */
+static svn_error_t *
+proplist_receiver_xml(void *baton,
+ const char *path,
+ apr_hash_t *prop_hash,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((proplist_baton_t *)baton)->opt_state;
+ svn_boolean_t is_url = ((proplist_baton_t *)baton)->is_url;
+ svn_stringbuf_t *sb = NULL;
+ const char *name_local;
+
+ if (! is_url)
+ name_local = svn_dirent_local_style(path, pool);
+ else
+ name_local = path;
+
+ /* "<target ...>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target",
+ "path", name_local, NULL);
+
+ SVN_ERR(svn_cl__print_xml_prop_hash(&sb, prop_hash, (! opt_state->verbose),
+ pool));
+
+ /* "</target>" */
+ svn_xml_make_close_tag(&sb, pool, "target");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* This implements the svn_proplist_receiver_t interface. */
+static svn_error_t *
+proplist_receiver(void *baton,
+ const char *path,
+ apr_hash_t *prop_hash,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((proplist_baton_t *)baton)->opt_state;
+ svn_boolean_t is_url = ((proplist_baton_t *)baton)->is_url;
+ const char *name_local;
+
+ if (! is_url)
+ name_local = svn_dirent_local_style(path, pool);
+ else
+ name_local = path;
+
+ if (!opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool, _("Properties on '%s':\n"), name_local));
+ return svn_cl__print_prop_hash(NULL, prop_hash, (! opt_state->verbose),
+ pool);
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__proplist(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_array_header_t *errors = apr_array_make(scratch_pool, 0,
+ sizeof(apr_status_t));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* Add "." if user passed 0 file arguments */
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ if (opt_state->revprop) /* operate on revprops */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+ apr_hash_t *proplist;
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, scratch_pool));
+
+ /* Let libsvn_client do the real work. */
+ SVN_ERR(svn_client_revprop_list(&proplist,
+ URL, &(opt_state->start_revision),
+ &rev, ctx, scratch_pool));
+
+ if (opt_state->xml)
+ {
+ svn_stringbuf_t *sb = NULL;
+ char *revstr = apr_psprintf(scratch_pool, "%ld", rev);
+
+ SVN_ERR(svn_cl__xml_print_header("properties", scratch_pool));
+
+ svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal,
+ "revprops",
+ "rev", revstr, NULL);
+ SVN_ERR(svn_cl__print_xml_prop_hash
+ (&sb, proplist, (! opt_state->verbose), scratch_pool));
+ svn_xml_make_close_tag(&sb, scratch_pool, "revprops");
+
+ SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
+ SVN_ERR(svn_cl__xml_print_footer("properties", scratch_pool));
+ }
+ else
+ {
+ SVN_ERR
+ (svn_cmdline_printf(scratch_pool,
+ _("Unversioned properties on revision %ld:\n"),
+ rev));
+
+ SVN_ERR(svn_cl__print_prop_hash
+ (NULL, proplist, (! opt_state->verbose), scratch_pool));
+ }
+ }
+ else /* operate on normal, versioned properties (not revprops) */
+ {
+ int i;
+ apr_pool_t *iterpool;
+ svn_proplist_receiver_t pl_receiver;
+
+ if (opt_state->xml)
+ {
+ SVN_ERR(svn_cl__xml_print_header("properties", scratch_pool));
+ pl_receiver = proplist_receiver_xml;
+ }
+ else
+ {
+ pl_receiver = proplist_receiver;
+ }
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ proplist_baton_t pl_baton;
+ const char *truepath;
+ svn_opt_revision_t peg_revision;
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ pl_baton.is_url = svn_path_is_url(target);
+ pl_baton.opt_state = opt_state;
+
+ /* Check for a peg revision. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
+ iterpool));
+
+ SVN_ERR(svn_cl__try(
+ svn_client_proplist3(truepath, &peg_revision,
+ &(opt_state->start_revision),
+ opt_state->depth,
+ opt_state->changelists,
+ pl_receiver, &pl_baton,
+ ctx, iterpool),
+ errors, opt_state->quiet,
+ SVN_ERR_UNVERSIONED_RESOURCE,
+ SVN_ERR_ENTRY_NOT_FOUND,
+ SVN_NO_ERROR));
+ }
+ svn_pool_destroy(iterpool);
+
+ if (opt_state->xml)
+ SVN_ERR(svn_cl__xml_print_footer("properties", scratch_pool));
+
+ /* Error out *after* we closed the XML element */
+ if (errors->nelts > 0)
+ {
+ svn_error_t *err;
+
+ err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, NULL);
+ for (i = 0; i < errors->nelts; i++)
+ {
+ apr_status_t status = APR_ARRAY_IDX(errors, i, apr_status_t);
+
+ if (status == SVN_ERR_ENTRY_NOT_FOUND)
+ err = svn_error_quick_wrap(err,
+ _("Could not display properties "
+ "of all targets because some "
+ "targets don't exist"));
+ else if (status == SVN_ERR_UNVERSIONED_RESOURCE)
+ err = svn_error_quick_wrap(err,
+ _("Could not display properties "
+ "of all targets because some "
+ "targets are not versioned"));
+ }
+
+ return svn_error_trace(err);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/props.c b/subversion/svn/props.c
new file mode 100644
index 0000000..f08f18c
--- /dev/null
+++ b/subversion/svn/props.c
@@ -0,0 +1,224 @@
+/*
+ * props.c: Utility functions for property handling
+ *
+ * ====================================================================
+ * 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 <apr_hash.h>
+#include "svn_cmdline.h"
+#include "svn_string.h"
+#include "svn_error.h"
+#include "svn_sorts.h"
+#include "svn_subst.h"
+#include "svn_props.h"
+#include "svn_string.h"
+#include "svn_opt.h"
+#include "svn_xml.h"
+#include "svn_base64.h"
+#include "cl.h"
+
+#include "private/svn_cmdline_private.h"
+
+#include "svn_private_config.h"
+
+
+
+svn_error_t *
+svn_cl__revprop_prepare(const svn_opt_revision_t *revision,
+ const apr_array_header_t *targets,
+ const char **URL,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ const char *target;
+
+ if (revision->kind != svn_opt_revision_number
+ && revision->kind != svn_opt_revision_date
+ && revision->kind != svn_opt_revision_head)
+ return svn_error_create
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Must specify the revision as a number, a date or 'HEAD' "
+ "when operating on a revision property"));
+
+ /* There must be exactly one target at this point. If it was optional and
+ unspecified by the user, the caller has already added the implicit '.'. */
+ if (targets->nelts != 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Wrong number of targets specified"));
+
+ /* (The docs say the target must be either a URL or implicit '.', but
+ explicit WC targets are also accepted.) */
+ target = APR_ARRAY_IDX(targets, 0, const char *);
+ SVN_ERR(svn_client_url_from_path2(URL, target, ctx, pool, pool));
+ if (*URL == NULL)
+ return svn_error_create
+ (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
+ _("Either a URL or versioned item is required"));
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_cl__print_prop_hash(svn_stream_t *out,
+ apr_hash_t *prop_hash,
+ svn_boolean_t names_only,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *sorted_props;
+ int i;
+
+ sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
+ pool);
+ for (i = 0; i < sorted_props->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
+ const char *pname = item.key;
+ svn_string_t *propval = item.value;
+ const char *pname_stdout;
+ apr_size_t len;
+
+ if (svn_prop_needs_translation(pname))
+ SVN_ERR(svn_subst_detranslate_string(&propval, propval,
+ TRUE, pool));
+
+ SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_stdout, pname, pool));
+
+ if (out)
+ {
+ pname_stdout = apr_psprintf(pool, " %s\n", pname_stdout);
+ SVN_ERR(svn_subst_translate_cstring2(pname_stdout, &pname_stdout,
+ APR_EOL_STR, /* 'native' eol */
+ FALSE, /* no repair */
+ NULL, /* no keywords */
+ FALSE, /* no expansion */
+ pool));
+
+ len = strlen(pname_stdout);
+ SVN_ERR(svn_stream_write(out, pname_stdout, &len));
+ }
+ else
+ {
+ /* ### We leave these printfs for now, since if propval wasn't
+ translated above, we don't know anything about its encoding.
+ In fact, it might be binary data... */
+ printf(" %s\n", pname_stdout);
+ }
+
+ if (!names_only)
+ {
+ /* Add an extra newline to the value before indenting, so that
+ * every line of output has the indentation whether the value
+ * already ended in a newline or not. */
+ const char *newval = apr_psprintf(pool, "%s\n", propval->data);
+ const char *indented_newval = svn_cl__indent_string(newval,
+ " ",
+ pool);
+ if (out)
+ {
+ len = strlen(indented_newval);
+ SVN_ERR(svn_stream_write(out, indented_newval, &len));
+ }
+ else
+ {
+ printf("%s", indented_newval);
+ }
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__print_xml_prop_hash(svn_stringbuf_t **outstr,
+ apr_hash_t *prop_hash,
+ svn_boolean_t names_only,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *sorted_props;
+ int i;
+
+ if (*outstr == NULL)
+ *outstr = svn_stringbuf_create("", pool);
+
+ sorted_props = svn_sort__hash(prop_hash, svn_sort_compare_items_lexically,
+ pool);
+ for (i = 0; i < sorted_props->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(sorted_props, i, svn_sort__item_t);
+ const char *pname = item.key;
+ svn_string_t *propval = item.value;
+
+ if (names_only)
+ {
+ svn_xml_make_open_tag(outstr, pool, svn_xml_self_closing, "property",
+ "name", pname, NULL);
+ }
+ else
+ {
+ const char *pname_out;
+
+ if (svn_prop_needs_translation(pname))
+ SVN_ERR(svn_subst_detranslate_string(&propval, propval,
+ TRUE, pool));
+
+ SVN_ERR(svn_cmdline_cstring_from_utf8(&pname_out, pname, pool));
+
+ svn_cmdline__print_xml_prop(outstr, pname_out, propval, pool);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+void
+svn_cl__check_boolean_prop_val(const char *propname, const char *propval,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *propbuf;
+
+ if (!svn_prop_is_boolean(propname))
+ return;
+
+ propbuf = svn_stringbuf_create(propval, pool);
+ svn_stringbuf_strip_whitespace(propbuf);
+
+ if (propbuf->data[0] == '\0'
+ || strcmp(propbuf->data, "no") == 0
+ || strcmp(propbuf->data, "off") == 0
+ || strcmp(propbuf->data, "false") == 0)
+ {
+ svn_error_t *err = svn_error_createf
+ (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
+ _("To turn off the %s property, use 'svn propdel';\n"
+ "setting the property to '%s' will not turn it off."),
+ propname, propval);
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ }
+}
+
diff --git a/subversion/svn/propset-cmd.c b/subversion/svn/propset-cmd.c
new file mode 100644
index 0000000..33b4b5d
--- /dev/null
+++ b/subversion/svn/propset-cmd.c
@@ -0,0 +1,183 @@
+/*
+ * propset-cmd.c -- Set property values on files/dirs
+ *
+ * ====================================================================
+ * 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_cmdline.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_subst.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__propset(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;
+ const char *pname, *pname_utf8;
+ svn_string_t *propval = NULL;
+ svn_boolean_t propval_came_from_cmdline;
+ apr_array_header_t *args, *targets;
+
+ /* PNAME and PROPVAL expected as first 2 arguments if filedata was
+ NULL, else PNAME alone will precede the targets. Get a UTF-8
+ version of the name, too. */
+ SVN_ERR(svn_opt_parse_num_args(&args, os,
+ opt_state->filedata ? 1 : 2, scratch_pool));
+ pname = APR_ARRAY_IDX(args, 0, const char *);
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, scratch_pool));
+ if (! svn_prop_name_is_valid(pname_utf8))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("'%s' is not a valid Subversion property name"),
+ pname_utf8);
+
+ /* Get the PROPVAL from either an external file, or from the command
+ line. */
+ if (opt_state->filedata)
+ {
+ propval = svn_string_create_from_buf(opt_state->filedata, scratch_pool);
+ propval_came_from_cmdline = FALSE;
+ }
+ else
+ {
+ propval = svn_string_create(APR_ARRAY_IDX(args, 1, const char *),
+ scratch_pool);
+ propval_came_from_cmdline = TRUE;
+ }
+
+ /* We only want special Subversion property values to be in UTF-8
+ and LF line endings. All other propvals are taken literally. */
+ if (svn_prop_needs_translation(pname_utf8))
+ SVN_ERR(svn_subst_translate_string2(&propval, NULL, NULL, propval,
+ opt_state->encoding, FALSE,
+ scratch_pool, scratch_pool));
+ else if (opt_state->encoding)
+ return svn_error_create
+ (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("--encoding option applies only to textual"
+ " Subversion-controlled properties"));
+
+ /* Suck up all the remaining arguments into a targets array */
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* Implicit "." is okay for revision properties; it just helps
+ us find the right repository. */
+ if (opt_state->revprop)
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ if (opt_state->revprop) /* operate on a revprop */
+ {
+ svn_revnum_t rev;
+ const char *URL;
+
+ SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
+ &URL, ctx, scratch_pool));
+
+ /* Let libsvn_client do the real work. */
+ SVN_ERR(svn_client_revprop_set2(pname_utf8, propval, NULL,
+ URL, &(opt_state->start_revision),
+ &rev, opt_state->force, ctx,
+ scratch_pool));
+ }
+ else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
+ {
+ return svn_error_createf
+ (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Cannot specify revision for setting versioned property '%s'"),
+ pname);
+ }
+ else /* operate on a normal, versioned property (not a revprop) */
+ {
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ /* The customary implicit dot rule has been prone to user error
+ * here. People would do intuitive things like
+ *
+ * $ svn propset svn:executable script
+ *
+ * and then be surprised to get an error like:
+ *
+ * svn: Illegal target for the requested operation
+ * svn: Cannot set svn:executable on a directory ()
+ *
+ * So we don't do the implicit dot thing anymore. A * target
+ * must always be explicitly provided when setting a versioned
+ * property. See
+ *
+ * http://subversion.tigris.org/issues/show_bug.cgi?id=924
+ *
+ * for more details.
+ */
+
+ if (targets->nelts == 0)
+ {
+ if (propval_came_from_cmdline)
+ {
+ return svn_error_createf
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Explicit target required ('%s' interpreted as prop value)"),
+ propval->data);
+ }
+ else
+ {
+ return svn_error_create
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Explicit target argument required"));
+ }
+ }
+
+ SVN_ERR(svn_client_propset_local(pname_utf8, propval, targets,
+ opt_state->depth, opt_state->force,
+ opt_state->changelists, ctx,
+ scratch_pool));
+
+ if (! opt_state->quiet)
+ svn_cl__check_boolean_prop_val(pname_utf8, propval->data, scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/relocate-cmd.c b/subversion/svn/relocate-cmd.c
new file mode 100644
index 0000000..f07d4ae
--- /dev/null
+++ b/subversion/svn/relocate-cmd.c
@@ -0,0 +1,120 @@
+/*
+ * relocate-cmd.c -- Update working tree administrative data to
+ * account for repository change-of-address.
+ *
+ * ====================================================================
+ * 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_wc.h"
+#include "svn_client.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__relocate(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;
+ svn_boolean_t ignore_externals = opt_state->ignore_externals;
+ apr_array_header_t *targets;
+ const char *from = NULL, *to = NULL, *path;
+
+ /* We've got two different syntaxes to support:
+
+ 1. relocate FROM-PREFIX TO-PREFIX [PATH ...]
+ 2. relocate TO-URL [PATH]
+ */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ /* If we have a single target, we're in form #2. If we have two
+ targets and the first is a URL and the second is not, we're also
+ in form #2. */
+ if ((targets->nelts == 1) ||
+ ((targets->nelts == 2)
+ && (svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *)))
+ && (! svn_path_is_url(APR_ARRAY_IDX(targets, 1, const char *)))))
+
+ {
+ to = APR_ARRAY_IDX(targets, 0, const char *);
+ path = (targets->nelts == 2) ? APR_ARRAY_IDX(targets, 1, const char *)
+ : "";
+
+ SVN_ERR(svn_client_url_from_path2(&from, path, ctx,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_relocate2(path, from, to, ignore_externals,
+ ctx, scratch_pool));
+ }
+ /* ... Everything else is form #1. */
+ else
+ {
+ from = APR_ARRAY_IDX(targets, 0, const char *);
+ to = APR_ARRAY_IDX(targets, 1, const char *);
+
+ if (targets->nelts == 2)
+ {
+ SVN_ERR(svn_client_relocate2("", from, to, ignore_externals,
+ ctx, scratch_pool));
+ }
+ else
+ {
+ apr_pool_t *subpool = svn_pool_create(scratch_pool);
+ int i;
+
+ /* Target working copy root dir must be local. */
+ for (i = 2; i < targets->nelts; i++)
+ {
+ path = APR_ARRAY_IDX(targets, i, const char *);
+ SVN_ERR(svn_cl__check_target_is_local_path(path));
+ }
+
+ for (i = 2; i < targets->nelts; i++)
+ {
+ svn_pool_clear(subpool);
+ path = APR_ARRAY_IDX(targets, i, const char *);
+ SVN_ERR(svn_client_relocate2(path, from, to, ignore_externals,
+ ctx, subpool));
+ }
+ svn_pool_destroy(subpool);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/resolve-cmd.c b/subversion/svn/resolve-cmd.c
new file mode 100644
index 0000000..4ce57be
--- /dev/null
+++ b/subversion/svn/resolve-cmd.c
@@ -0,0 +1,119 @@
+/*
+ * resolve-cmd.c -- Subversion resolve subcommand
+ *
+ * ====================================================================
+ * 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. ***/
+#define APR_WANT_STDIO
+#include <apr_want.h>
+
+#include "svn_path.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__resolve(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;
+ svn_wc_conflict_choice_t conflict_choice;
+ svn_error_t *err;
+ apr_array_header_t *targets;
+ int i;
+ apr_pool_t *iterpool;
+
+ switch (opt_state->accept_which)
+ {
+ case svn_cl__accept_working:
+ conflict_choice = svn_wc_conflict_choose_merged;
+ break;
+ case svn_cl__accept_base:
+ conflict_choice = svn_wc_conflict_choose_base;
+ break;
+ case svn_cl__accept_theirs_conflict:
+ conflict_choice = svn_wc_conflict_choose_theirs_conflict;
+ break;
+ case svn_cl__accept_mine_conflict:
+ conflict_choice = svn_wc_conflict_choose_mine_conflict;
+ break;
+ case svn_cl__accept_theirs_full:
+ conflict_choice = svn_wc_conflict_choose_theirs_full;
+ break;
+ case svn_cl__accept_mine_full:
+ conflict_choice = svn_wc_conflict_choose_mine_full;
+ break;
+ case svn_cl__accept_unspecified:
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("missing --accept option"));
+ default:
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("invalid 'accept' ARG"));
+ }
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ err = svn_client_resolve(target,
+ opt_state->depth, conflict_choice,
+ ctx,
+ iterpool);
+ if (err)
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/resolved-cmd.c b/subversion/svn/resolved-cmd.c
new file mode 100644
index 0000000..0db3b4b
--- /dev/null
+++ b/subversion/svn/resolved-cmd.c
@@ -0,0 +1,91 @@
+/*
+ * resolved-cmd.c -- Subversion resolved subcommand
+ *
+ * ====================================================================
+ * 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. ***/
+#define APR_WANT_STDIO
+#include <apr_want.h>
+
+#include "svn_path.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__resolved(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;
+ svn_error_t *err;
+ apr_array_header_t *targets;
+ apr_pool_t *iterpool;
+ int i;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ err = svn_client_resolve(target,
+ opt_state->depth,
+ svn_wc_conflict_choose_merged,
+ ctx,
+ iterpool);
+ if (err)
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/revert-cmd.c b/subversion/svn/revert-cmd.c
new file mode 100644
index 0000000..d17aeb6
--- /dev/null
+++ b/subversion/svn/revert-cmd.c
@@ -0,0 +1,81 @@
+/*
+ * revert-cmd.c -- Subversion revert 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_path.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__revert(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 = NULL;
+ svn_error_t *err;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* Revert has no implicit dot-target `.', so don't you put that code here! */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ /* Revert is especially conservative, by default it is as
+ nonrecursive as possible. */
+ if (opt_state->depth == svn_depth_unknown)
+ opt_state->depth = svn_depth_empty;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ err = svn_client_revert2(targets, opt_state->depth,
+ opt_state->changelists, ctx, scratch_pool);
+ if (err
+ && (err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH)
+ && (! SVN_DEPTH_IS_RECURSIVE(opt_state->depth)))
+ {
+ err = svn_error_quick_wrap
+ (err, _("Try 'svn revert --depth infinity' instead?"));
+ }
+
+ return svn_error_trace(err);
+}
diff --git a/subversion/svn/schema/blame.rnc b/subversion/svn/schema/blame.rnc
new file mode 100644
index 0000000..b6a1e41
--- /dev/null
+++ b/subversion/svn/schema/blame.rnc
@@ -0,0 +1,42 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn blame"
+
+
+include "common.rnc"
+
+start = blame
+
+blame = element blame { target* }
+
+## Information for one blamed file.
+target = element target { attlist.target, entry* }
+attlist.target &= attribute path { target.type }
+
+## Information for one line of a blamed file.
+## NOTE: The order of entries in a target element is insignificant.
+entry = element entry { attlist.entry, commit?, merged? }
+attlist.entry &=
+ ## Line number.
+ attribute line-number { xsd:integer { minInclusive = "1" } }
+
+## The merged commit
+merged = element merged { attlist.merged, commit }
+attlist.merged &= attribute path { string }
diff --git a/subversion/svn/schema/common.rnc b/subversion/svn/schema/common.rnc
new file mode 100644
index 0000000..95729e3
--- /dev/null
+++ b/subversion/svn/schema/common.rnc
@@ -0,0 +1,77 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# Common declarations
+
+# Data types.
+
+## A revision number.
+revnum.type = xsd:nonNegativeInteger
+
+## A user name.
+username.type = string
+
+## A path or URL.
+target.type = string | xsd:anyURI
+
+## An UUID.
+uuid.type = string
+
+## An MD5 checksum.
+md5sum.type = xsd:hexBinary { length = "16" }
+
+# Common elements
+
+## Commit info.
+commit = element commit { attlist.commit, author?, date? }
+attlist.commit &= attribute revision { revnum.type }
+
+author = element author { username.type }
+
+date = element date { xsd:dateTime }
+
+## Lock info stored in repository or working copy.
+lock =
+ element lock {
+ \token, owner, comment?, created, expires?
+ }
+
+## Lock token.
+\token = element token { xsd:anyURI }
+
+## Lock owner.
+owner = element owner { username.type }
+
+## Lock comment.
+comment = element comment { text }
+
+## Creation date.
+created = element created { xsd:dateTime }
+
+## Expiration date.
+expires = element expires { xsd:dateTime }
+
+## Node and revision properties.
+property = element property { attlist.property, text }
+attlist.property &=
+ ## The property name
+ attribute name { string },
+ ## The encoding of the element content. If not present, the value
+ ## is the raw content of the element.
+ attribute encoding { "base64" }?
diff --git a/subversion/svn/schema/diff.rnc b/subversion/svn/schema/diff.rnc
new file mode 100644
index 0000000..7ac51da
--- /dev/null
+++ b/subversion/svn/schema/diff.rnc
@@ -0,0 +1,39 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn diff --summarize --xml"
+
+include "common.rnc"
+
+start = diff
+
+diff = element diff { paths }
+
+paths = element paths { path* }
+
+## A path entry
+path = element path { attlist.path, text }
+attlist.path &=
+ ## The props of the entry.
+ attribute props { "none" | "modified" },
+ ## The kind of the entry.
+ attribute kind { "dir" | "file" },
+ ## The action performed against this path. This terminology
+ ## was chosen for consistencey from 'svn list'.
+ attribute item { "none" | "added" | "modified" | "deleted" }
diff --git a/subversion/svn/schema/info.rnc b/subversion/svn/schema/info.rnc
new file mode 100644
index 0000000..6ed2643
--- /dev/null
+++ b/subversion/svn/schema/info.rnc
@@ -0,0 +1,124 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn info"
+
+include "common.rnc"
+
+start = info
+
+info = element info { entry* }
+
+entry =
+ element entry {
+ attlist.entry, url?, repository?, wc-info?, commit?, conflict?, lock?,
+ tree-conflict?
+ }
+attlist.entry &=
+ ## Local path.
+ attribute path { string },
+ ## Path type.
+ attribute kind { "file" | "dir" },
+ ## Revision number of path/URL.
+ attribute revision { revnum.type }
+
+## URL of this item in the repository.
+url = element url { xsd:anyURI }
+
+## Information of this item's repository.
+repository = element repository { root?, uuid? }
+
+## URL of the repository.
+root = element root { xsd:anyURI }
+
+## UUID of the repository.
+uuid = element uuid { uuid.type }
+
+## Info in the working copy entry.
+wc-info =
+ element wc-info {
+ wcroot-abspath?,
+ schedule?,
+ changelist?,
+ copy-from-url?,
+ copy-from-rev?,
+ depth?,
+ text-updated?,
+ prop-updated?,
+ checksum?
+ }
+
+wcroot-abspath = element wcroot-abspath { string }
+
+schedule =
+ element schedule { "normal" | "add" | "delete" | "replace" | "none" }
+
+## The name of the changelist that the path may be a member of.
+changelist = element changelist { string }
+
+copy-from-url = element copy-from-url { xsd:anyURI }
+
+copy-from-rev = element copy-from-rev { revnum.type }
+
+# Date when text was last updated.
+text-updated = element text-updated { xsd:dateTime }
+
+# Date when properties were last updated.
+prop-updated = element prop-updated { xsd:dateTime }
+
+checksum = element checksum { md5sum.type }
+
+conflict =
+ element conflict {
+ prev-base-file,
+ prev-wc-file?,
+ cur-base-file,
+ prop-file?
+ }
+
+## Previous base file.
+prev-base-file = element prev-base-file { string }
+
+## Previous WC file.
+prev-wc-file = element prev-wc-file { string }
+
+## Current base file.
+cur-base-file = element cur-base-file { string }
+
+## Current properties file.
+prop-file = element prop-file { string }
+
+## Depth of this directory, always "infinity" for non-directories
+depth = element depth { "infinity" | "immediates" | "files" | "empty" }
+
+tree-conflict =
+ element tree-conflict { attlist.tree-conflict }
+
+attlist.tree-conflict &=
+ ## Local path to the original victim.
+ attribute victim { string },
+ ## Path type.
+ attribute kind { "file" | "dir" },
+ ## Operation causing the tree conflict.
+ attribute operation { "update" | "merge" | "switch" },
+ ## Operation's action on the victim.
+ attribute action { "edit" | "add" | "delete" },
+ ## Local reason for the conflict.
+ attribute reason { "edit" | "obstruction" | "delete" | "add" |
+ "missing" | "unversioned" }
diff --git a/subversion/svn/schema/list.rnc b/subversion/svn/schema/list.rnc
new file mode 100644
index 0000000..13d5897
--- /dev/null
+++ b/subversion/svn/schema/list.rnc
@@ -0,0 +1,45 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn list"
+
+include "common.rnc"
+
+start = lists
+
+lists = element lists { \list+ }
+
+## A target to the list command.
+\list = element list { attlist.list, entry* }
+attlist.list &=
+ ## Local path or repository URL.
+ attribute path { target.type }
+
+## A directory entry.
+entry = element entry { attlist.entry, name, size?, commit, lock? }
+attlist.entry &=
+ ## The kind of the entry.
+ attribute kind { "dir" | "file" }
+
+## Name of the file or directory.
+name = element name { string }
+
+## File size in bytes.
+size = element size { xsd:nonNegativeInteger }
+
diff --git a/subversion/svn/schema/log.rnc b/subversion/svn/schema/log.rnc
new file mode 100644
index 0000000..14a8b7e
--- /dev/null
+++ b/subversion/svn/schema/log.rnc
@@ -0,0 +1,55 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn log"
+
+include "common.rnc"
+
+start = log
+
+log = element log { logentry* }
+
+logentry =
+ element logentry { attlist.logentry, author?, date?, paths?, msg?, revprops?, logentry* }
+attlist.logentry &=
+ attribute revision { revnum.type }
+
+## Changed paths information.
+paths = element paths { path+ }
+
+## Path within repository.
+path = element path { attlist.path, text }
+attlist.path &=
+ ## "action code": A)dd, D)elete, R)eplace or M)odify
+ attribute action { "A" | "D" | "R" | "M" },
+ ## kind is "" when repository was < 1.6 when committing
+ attribute kind { "file" | "dir" | "" },
+ attribute text-mods { "true" | "false" }?,
+ attribute prop-mods { "true" | "false" }?,
+ (
+ ## The copyfrom path within repository.
+ attribute copyfrom-path { text },
+ ## Copyfrom revision number.
+ attribute copyfrom-rev { revnum.type })?
+
+## Log message.
+msg = element msg { text }
+
+## Revision properties.
+revprops = element revprops { property+ }
diff --git a/subversion/svn/schema/props.rnc b/subversion/svn/schema/props.rnc
new file mode 100644
index 0000000..260c93e
--- /dev/null
+++ b/subversion/svn/schema/props.rnc
@@ -0,0 +1,36 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn proplist"
+
+include "common.rnc"
+
+start = properties
+
+properties = element properties { target* | revprops }
+
+target = element target { attlist.target, property* }
+attlist.target &=
+ ## The target path.
+ attribute path { string }
+
+revprops = element revprops { attlist.revprops, property*}
+attlist.revprops &=
+ ## The revision
+ attribute rev { revnum.type }
diff --git a/subversion/svn/schema/status.rnc b/subversion/svn/schema/status.rnc
new file mode 100644
index 0000000..6e55fa1
--- /dev/null
+++ b/subversion/svn/schema/status.rnc
@@ -0,0 +1,88 @@
+# 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.
+#
+#
+# XML RELAX NG schema for Subversion command-line client output
+# For "svn status"
+
+# The DTD compatibility annotations namespace, used for adding default
+# attribute values.
+namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
+
+include "common.rnc"
+
+start = status
+
+status = element status { (target | changelist)* }
+
+target = element target { attlist.target, entry*, against? }
+attlist.target &=
+ ## The target path.
+ attribute path { string }
+
+changelist = element changelist { attlist.changelist, entry*, against? }
+attlist.changelist &=
+ ## The changelist name.
+ attribute name { string }
+
+## Status information for a path under the target.
+entry = element entry { attlist.entry, wc-status, repos-status? }
+attlist.entry &=
+ ## Path inside the target.
+ attribute path { text }
+
+## Status of the entry in the working copy.
+wc-status = element wc-status { attlist.wc-status, commit?, lock? }
+
+attlist.wc-status &=
+ ## Item/text status.
+ attribute item {
+ "added" | "conflicted" | "deleted" | "external" | "ignored" |
+ "incomplete" | "merged" | "missing" | "modified" | "none" |
+ "normal" | "obstructed" | "replaced" | "unversioned"
+ },
+ ## Properties status.
+ attribute props { "conflicted" | "modified" | "normal" | "none" },
+ ## Base revision number.
+ attribute revision { revnum.type }?,
+ ## WC directory locked.
+ [ a:defaultValue = "false" ]
+ attribute wc-locked { "true" | "false" }?,
+ ## Add with history.
+ [ a:defaultValue = "false" ]
+ attribute copied { "true" | "false" }?,
+ # Item switched relative to its parent.
+ [ a:defaultValue = "false" ]
+ attribute switched { "true" | "false" }?,
+ ## Tree-conflict status of the item.
+ [ a:defaultValue = "false" ]
+ attribute tree-conflicted { "true" | "false" }?
+
+## Status in repository (if --update was specified).
+repos-status = element repos-status { attlist.repos-status, lock? }
+attlist.repos-status &=
+ ## Text/item status in the repository.
+ attribute item {
+ "added" | "deleted" | "modified" | "replaced" | "none"
+ },
+ ## Properties status in repository.
+ attribute props { "modified" | "none" }
+
+against = element against { attlist.against, empty }
+attlist.against &=
+ ## Revision number at which the repository information was obtained.
+ attribute revision { revnum.type }
diff --git a/subversion/svn/status-cmd.c b/subversion/svn/status-cmd.c
new file mode 100644
index 0000000..74d2847
--- /dev/null
+++ b/subversion/svn/status-cmd.c
@@ -0,0 +1,408 @@
+/*
+ * 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_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. */
+ 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("", 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("", 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(path, status, sb->ctx, pool);
+ else
+ return svn_cl__print_status(path, status, 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 =
+ apr_hash_get(sb->cached_changelists, cl_key, APR_HASH_KEY_STRING);
+ if (path_array == NULL)
+ {
+ path_array = apr_array_make(sb->cl_pool, 1,
+ sizeof(struct status_cache *));
+ apr_hash_set(sb->cached_changelists, cl_key,
+ APR_HASH_KEY_STRING, 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"));
+ }
+
+ 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_NO_ERROR));
+
+ 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("", 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_set(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_set(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;
+}
diff --git a/subversion/svn/status.c b/subversion/svn/status.c
new file mode 100644
index 0000000..0c5bddb
--- /dev/null
+++ b/subversion/svn/status.c
@@ -0,0 +1,444 @@
+/*
+ * status.c: the command-line's portion of the "svn status" 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_cmdline.h"
+#include "svn_wc.h"
+#include "svn_dirent_uri.h"
+#include "svn_xml.h"
+#include "svn_time.h"
+#include "cl.h"
+#include "svn_private_config.h"
+#include "tree-conflicts.h"
+#include "private/svn_wc_private.h"
+
+/* Return the single character representation of STATUS */
+static char
+generate_status_code(enum svn_wc_status_kind status)
+{
+ switch (status)
+ {
+ case svn_wc_status_none: return ' ';
+ case svn_wc_status_normal: return ' ';
+ case svn_wc_status_added: return 'A';
+ case svn_wc_status_missing: return '!';
+ case svn_wc_status_incomplete: return '!';
+ case svn_wc_status_deleted: return 'D';
+ case svn_wc_status_replaced: return 'R';
+ case svn_wc_status_modified: return 'M';
+ case svn_wc_status_conflicted: return 'C';
+ case svn_wc_status_obstructed: return '~';
+ case svn_wc_status_ignored: return 'I';
+ case svn_wc_status_external: return 'X';
+ case svn_wc_status_unversioned: return '?';
+ default: return '?';
+ }
+}
+
+/* Return the combined STATUS as shown in 'svn status' based
+ on the node status and text status */
+static enum svn_wc_status_kind
+combined_status(const svn_client_status_t *status)
+{
+ enum svn_wc_status_kind new_status = status->node_status;
+
+ switch (status->node_status)
+ {
+ case svn_wc_status_conflicted:
+ if (!status->versioned && status->conflicted)
+ {
+ /* Report unversioned tree conflict victims as missing: '!' */
+ new_status = svn_wc_status_missing;
+ break;
+ }
+ /* fall through */
+ case svn_wc_status_modified:
+ /* This value might be the property status */
+ new_status = status->text_status;
+ break;
+ default:
+ break;
+ }
+
+ return new_status;
+}
+
+/* Return the combined repository STATUS as shown in 'svn status' based
+ on the repository node status and repository text status */
+static enum svn_wc_status_kind
+combined_repos_status(const svn_client_status_t *status)
+{
+ if (status->repos_node_status == svn_wc_status_modified)
+ return status->repos_text_status;
+
+ return status->repos_node_status;
+}
+
+/* Return the single character representation of the switched column
+ status. */
+static char
+generate_switch_column_code(const svn_client_status_t *status)
+{
+ if (status->switched)
+ return 'S';
+ else if (status->file_external)
+ return 'X';
+ else
+ return ' ';
+}
+
+/* Return the detailed string representation of STATUS */
+static const char *
+generate_status_desc(enum svn_wc_status_kind status)
+{
+ switch (status)
+ {
+ case svn_wc_status_none: return "none";
+ case svn_wc_status_normal: return "normal";
+ case svn_wc_status_added: return "added";
+ case svn_wc_status_missing: return "missing";
+ case svn_wc_status_incomplete: return "incomplete";
+ case svn_wc_status_deleted: return "deleted";
+ case svn_wc_status_replaced: return "replaced";
+ case svn_wc_status_modified: return "modified";
+ case svn_wc_status_conflicted: return "conflicted";
+ case svn_wc_status_obstructed: return "obstructed";
+ case svn_wc_status_ignored: return "ignored";
+ case svn_wc_status_external: return "external";
+ case svn_wc_status_unversioned: return "unversioned";
+ default:
+ SVN_ERR_MALFUNCTION_NO_RETURN();
+ }
+}
+
+
+/* Print STATUS and PATH in a format determined by DETAILED and
+ SHOW_LAST_COMMITTED. */
+static svn_error_t *
+print_status(const char *path,
+ svn_boolean_t detailed,
+ svn_boolean_t show_last_committed,
+ svn_boolean_t repos_locks,
+ const svn_client_status_t *status,
+ unsigned int *text_conflicts,
+ unsigned int *prop_conflicts,
+ unsigned int *tree_conflicts,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ enum svn_wc_status_kind node_status = status->node_status;
+ enum svn_wc_status_kind prop_status = status->prop_status;
+ char tree_status_code = ' ';
+ const char *tree_desc_line = "";
+
+ /* For historic reasons svn ignores the property status for added nodes, even
+ if these nodes were copied and have local property changes.
+
+ Note that it doesn't do this on replacements, or children of copies.
+
+ ### Our test suite would catch more errors if we reported property
+ changes on copies. */
+ if (node_status == svn_wc_status_added)
+ prop_status = svn_wc_status_none;
+
+ /* To indicate this node is the victim of a tree conflict, we show
+ 'C' in the tree-conflict column, overriding any other status.
+ We also print a separate line describing the nature of the tree
+ conflict. */
+ if (status->conflicted)
+ {
+ const char *desc;
+ const char *local_abspath = status->local_abspath;
+ svn_boolean_t text_conflicted;
+ svn_boolean_t prop_conflicted;
+ svn_boolean_t tree_conflicted;
+
+ if (status->versioned)
+ {
+ svn_error_t *err;
+
+ err = svn_wc_conflicted_p3(&text_conflicted,
+ &prop_conflicted,
+ &tree_conflicted, ctx->wc_ctx,
+ local_abspath, pool);
+
+ if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
+ {
+ svn_error_clear(err);
+ text_conflicted = FALSE;
+ prop_conflicted = FALSE;
+ tree_conflicted = FALSE;
+ }
+ else
+ SVN_ERR(err);
+ }
+ else
+ {
+ text_conflicted = FALSE;
+ prop_conflicted = FALSE;
+ tree_conflicted = TRUE;
+ }
+
+ if (tree_conflicted)
+ {
+ const svn_wc_conflict_description2_t *tree_conflict;
+ SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, ctx->wc_ctx,
+ local_abspath, pool, pool));
+ SVN_ERR_ASSERT(tree_conflict != NULL);
+
+ tree_status_code = 'C';
+ SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
+ &desc, tree_conflict, pool));
+ tree_desc_line = apr_psprintf(pool, "\n > %s", desc);
+ (*tree_conflicts)++;
+ }
+ else if (text_conflicted)
+ (*text_conflicts)++;
+ else if (prop_conflicted)
+ (*prop_conflicts)++;
+ }
+
+ if (detailed)
+ {
+ char ood_status, lock_status;
+ const char *working_rev;
+
+ if (! status->versioned)
+ working_rev = "";
+ else if (status->copied
+ || ! SVN_IS_VALID_REVNUM(status->revision))
+ working_rev = "-";
+ else
+ working_rev = apr_psprintf(pool, "%ld", status->revision);
+
+ if (status->repos_node_status != svn_wc_status_none)
+ ood_status = '*';
+ else
+ ood_status = ' ';
+
+ if (repos_locks)
+ {
+ if (status->repos_lock)
+ {
+ if (status->lock)
+ {
+ if (strcmp(status->repos_lock->token, status->lock->token)
+ == 0)
+ lock_status = 'K';
+ else
+ lock_status = 'T';
+ }
+ else
+ lock_status = 'O';
+ }
+ else if (status->lock)
+ lock_status = 'B';
+ else
+ lock_status = ' ';
+ }
+ else
+ lock_status = (status->lock) ? 'K' : ' ';
+
+ if (show_last_committed)
+ {
+ const char *commit_rev;
+ const char *commit_author;
+
+ if (SVN_IS_VALID_REVNUM(status->changed_rev))
+ commit_rev = apr_psprintf(pool, "%ld", status->changed_rev);
+ else if (status->versioned)
+ commit_rev = " ? ";
+ else
+ commit_rev = "";
+
+ if (status->changed_author)
+ commit_author = status->changed_author;
+ else if (status->versioned)
+ commit_author = " ? ";
+ else
+ commit_author = "";
+
+ SVN_ERR
+ (svn_cmdline_printf(pool,
+ "%c%c%c%c%c%c%c %c %6s %6s %-12s %s%s\n",
+ generate_status_code(combined_status(status)),
+ generate_status_code(prop_status),
+ status->wc_is_locked ? 'L' : ' ',
+ status->copied ? '+' : ' ',
+ generate_switch_column_code(status),
+ lock_status,
+ tree_status_code,
+ ood_status,
+ working_rev,
+ commit_rev,
+ commit_author,
+ path,
+ tree_desc_line));
+ }
+ else
+ SVN_ERR(
+ svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %6s %s%s\n",
+ generate_status_code(combined_status(status)),
+ generate_status_code(prop_status),
+ status->wc_is_locked ? 'L' : ' ',
+ status->copied ? '+' : ' ',
+ generate_switch_column_code(status),
+ lock_status,
+ tree_status_code,
+ ood_status,
+ working_rev,
+ path,
+ tree_desc_line));
+ }
+ else
+ SVN_ERR(
+ svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %s%s\n",
+ generate_status_code(combined_status(status)),
+ generate_status_code(prop_status),
+ status->wc_is_locked ? 'L' : ' ',
+ status->copied ? '+' : ' ',
+ generate_switch_column_code(status),
+ ((status->lock)
+ ? 'K' : ' '),
+ tree_status_code,
+ path,
+ tree_desc_line));
+
+ return svn_cmdline_fflush(stdout);
+}
+
+
+svn_error_t *
+svn_cl__print_status_xml(const char *path,
+ const svn_client_status_t *status,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+ apr_hash_t *att_hash;
+ const char *local_abspath = status->local_abspath;
+ svn_boolean_t tree_conflicted = FALSE;
+
+ if (status->node_status == svn_wc_status_none
+ && status->repos_node_status == svn_wc_status_none)
+ return SVN_NO_ERROR;
+
+ if (status->conflicted)
+ SVN_ERR(svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
+ ctx->wc_ctx, local_abspath, pool));
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "path", svn_dirent_local_style(path, pool), NULL);
+
+ att_hash = apr_hash_make(pool);
+ apr_hash_set(att_hash, "item", APR_HASH_KEY_STRING,
+ generate_status_desc(combined_status(status)));
+
+ apr_hash_set(att_hash, "props", APR_HASH_KEY_STRING,
+ generate_status_desc(
+ (status->node_status != svn_wc_status_deleted)
+ ? status->prop_status
+ : svn_wc_status_none));
+ if (status->wc_is_locked)
+ apr_hash_set(att_hash, "wc-locked", APR_HASH_KEY_STRING, "true");
+ if (status->copied)
+ apr_hash_set(att_hash, "copied", APR_HASH_KEY_STRING, "true");
+ if (status->switched)
+ apr_hash_set(att_hash, "switched", APR_HASH_KEY_STRING, "true");
+ if (status->file_external)
+ apr_hash_set(att_hash, "file-external", APR_HASH_KEY_STRING, "true");
+ if (status->versioned && ! status->copied)
+ apr_hash_set(att_hash, "revision", APR_HASH_KEY_STRING,
+ apr_psprintf(pool, "%ld", status->revision));
+ if (tree_conflicted)
+ apr_hash_set(att_hash, "tree-conflicted", APR_HASH_KEY_STRING,
+ "true");
+ svn_xml_make_open_tag_hash(&sb, pool, svn_xml_normal, "wc-status",
+ att_hash);
+
+ if (SVN_IS_VALID_REVNUM(status->changed_rev))
+ {
+ svn_cl__print_xml_commit(&sb, status->changed_rev,
+ status->changed_author,
+ svn_time_to_cstring(status->changed_date,
+ pool),
+ pool);
+ }
+
+ if (status->lock)
+ svn_cl__print_xml_lock(&sb, status->lock, pool);
+
+ svn_xml_make_close_tag(&sb, pool, "wc-status");
+
+ if (status->repos_node_status != svn_wc_status_none
+ || status->repos_lock)
+ {
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repos-status",
+ "item",
+ generate_status_desc(combined_repos_status(status)),
+ "props",
+ generate_status_desc(status->repos_prop_status),
+ NULL);
+ if (status->repos_lock)
+ svn_cl__print_xml_lock(&sb, status->repos_lock, pool);
+
+ svn_xml_make_close_tag(&sb, pool, "repos-status");
+ }
+
+ svn_xml_make_close_tag(&sb, pool, "entry");
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+/* Called by status-cmd.c */
+svn_error_t *
+svn_cl__print_status(const char *path,
+ const svn_client_status_t *status,
+ svn_boolean_t detailed,
+ svn_boolean_t show_last_committed,
+ svn_boolean_t skip_unrecognized,
+ svn_boolean_t repos_locks,
+ unsigned int *text_conflicts,
+ unsigned int *prop_conflicts,
+ unsigned int *tree_conflicts,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ if (! status
+ || (skip_unrecognized
+ && !(status->versioned
+ || status->conflicted
+ || status->node_status == svn_wc_status_external))
+ || (status->node_status == svn_wc_status_none
+ && status->repos_node_status == svn_wc_status_none))
+ return SVN_NO_ERROR;
+
+ return print_status(svn_dirent_local_style(path, pool),
+ detailed, show_last_committed, repos_locks, status,
+ text_conflicts, prop_conflicts, tree_conflicts,
+ ctx, pool);
+}
diff --git a/subversion/svn/svn.1 b/subversion/svn/svn.1
new file mode 100644
index 0000000..f4ad251
--- /dev/null
+++ b/subversion/svn/svn.1
@@ -0,0 +1,47 @@
+.\"
+.\"
+.\" 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.
+.\"
+.\"
+.\" You can view this file with:
+.\" nroff -man [filename]
+.\"
+.TH svn 1
+.SH NAME
+svn \- Subversion command line client tool
+.SH SYNOPSIS
+.TP
+\fBsvn\fP \fIcommand\fP [\fIoptions\fP] [\fIargs\fP]
+.SH OVERVIEW
+Subversion is a version control system, which allows you to keep old
+versions of files and directories (usually source code), keep a log of
+who, when, and why changes occurred, etc., like CVS, RCS or SCCS.
+\fBSubversion\fP keeps a single copy of the master sources. This copy
+is called the source ``repository''; it contains all the information
+to permit extracting previous versions of those files at any time.
+
+For more information about the Subversion project, visit
+http://subversion.apache.org.
+
+Documentation for Subversion and its tools, including detailed usage
+explanations of the \fBsvn\fP, \fBsvnadmin\fP, \fBsvnserve\fP and
+\fBsvnlook\fP programs, historical background, philosophical
+approaches and reasonings, etc., can be found at
+http://svnbook.red-bean.com/.
+
+Run `svn help' to access the built-in tool documentation.
diff --git a/subversion/svn/switch-cmd.c b/subversion/svn/switch-cmd.c
new file mode 100644
index 0000000..9c3bc14
--- /dev/null
+++ b/subversion/svn/switch-cmd.c
@@ -0,0 +1,187 @@
+/*
+ * switch-cmd.c -- Bring work tree in sync with a different URL
+ *
+ * ====================================================================
+ * 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_wc.h"
+#include "svn_client.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+/*** Code. ***/
+
+static svn_error_t *
+rewrite_urls(const apr_array_header_t *targets,
+ svn_boolean_t ignore_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool;
+ const char *from;
+ const char *to;
+
+ if (targets->nelts < 2)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ from = APR_ARRAY_IDX(targets, 0, const char *);
+ to = APR_ARRAY_IDX(targets, 1, const char *);
+
+ /* "--relocate http https" and "--relocate http://foo svn://bar" are OK,
+ but things like "--relocate http://foo svn" are not */
+ if (svn_path_is_url(from) != svn_path_is_url(to))
+ return svn_error_createf
+ (SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("'%s' to '%s' is not a valid relocation"), from, to);
+
+ subpool = svn_pool_create(pool);
+
+ if (targets->nelts == 2)
+ {
+ SVN_ERR(svn_client_relocate2("", from, to, ignore_externals,
+ ctx, pool));
+ }
+ else
+ {
+ int i;
+
+ for (i = 2; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_pool_clear(subpool);
+ SVN_ERR(svn_client_relocate2(target, from, to,
+ ignore_externals, ctx, subpool));
+ }
+ }
+
+ svn_pool_destroy(subpool);
+ return SVN_NO_ERROR;
+}
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__switch(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ 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;
+ const char *target, *switch_url;
+ svn_opt_revision_t peg_revision;
+ svn_depth_t depth;
+ svn_boolean_t depth_is_sticky;
+ struct svn_cl__check_externals_failed_notify_baton nwb;
+
+ /* This command should discover (or derive) exactly two cmdline
+ arguments: a local path to update ("target"), and a new url to
+ switch to ("switch_url"). */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* handle only-rewrite case specially */
+ if (opt_state->relocate)
+ return rewrite_urls(targets, opt_state->ignore_externals,
+ ctx, scratch_pool);
+
+ if (targets->nelts < 1)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+ if (targets->nelts > 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ /* Get the required SWITCH_URL and its optional PEG_REVISION, and the
+ * optional TARGET argument. */
+ SVN_ERR(svn_opt_parse_path(&peg_revision, &switch_url,
+ APR_ARRAY_IDX(targets, 0, const char *),
+ scratch_pool));
+ if (targets->nelts == 1)
+ target = "";
+ else
+ target = APR_ARRAY_IDX(targets, 1, const char *);
+
+ /* Validate the switch_url */
+ if (! svn_path_is_url(switch_url))
+ return svn_error_createf(SVN_ERR_BAD_URL, NULL,
+ _("'%s' does not appear to be a URL"), switch_url);
+
+ SVN_ERR(svn_cl__check_target_is_local_path(target));
+
+ /* Deal with depthstuffs. */
+ if (opt_state->set_depth != svn_depth_unknown)
+ {
+ depth = opt_state->set_depth;
+ depth_is_sticky = TRUE;
+ }
+ else
+ {
+ depth = opt_state->depth;
+ depth_is_sticky = FALSE;
+ }
+
+ nwb.wrapped_func = ctx->notify_func2;
+ nwb.wrapped_baton = ctx->notify_baton2;
+ nwb.had_externals_error = FALSE;
+ ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
+ ctx->notify_baton2 = &nwb;
+
+ /* Do the 'switch' update. */
+ err = svn_client_switch3(NULL, target, switch_url, &peg_revision,
+ &(opt_state->start_revision), depth,
+ depth_is_sticky, opt_state->ignore_externals,
+ opt_state->force, opt_state->ignore_ancestry,
+ ctx, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
+ return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, err,
+ _("Path '%s' does not share common version "
+ "control ancestry with the requested switch "
+ "location. Use --ignore-ancestry to "
+ "disable this check."),
+ svn_dirent_local_style(target,
+ scratch_pool));
+ return err;
+ }
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cl__print_conflict_stats(nwb.wrapped_baton, scratch_pool));
+
+ if (nwb.had_externals_error)
+ return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL,
+ _("Failure occurred processing one or more "
+ "externals definitions"));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/tree-conflicts.c b/subversion/svn/tree-conflicts.c
new file mode 100644
index 0000000..b374924
--- /dev/null
+++ b/subversion/svn/tree-conflicts.c
@@ -0,0 +1,196 @@
+/*
+ * tree-conflicts.c: Tree conflicts.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#include "tree-conflicts.h"
+#include "svn_xml.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "private/svn_token.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/* A map for svn_wc_conflict_action_t values to human-readable strings */
+static const svn_token_map_t map_conflict_action_human[] =
+{
+ { N_("edit"), svn_wc_conflict_action_edit },
+ { N_("delete"), svn_wc_conflict_action_delete },
+ { N_("add"), svn_wc_conflict_action_add },
+ { N_("replace"), svn_wc_conflict_action_replace },
+ { NULL, 0 }
+};
+
+/* A map for svn_wc_conflict_action_t values to XML strings */
+static const svn_token_map_t map_conflict_action_xml[] =
+{
+ { "edit", svn_wc_conflict_action_edit },
+ { "delete", svn_wc_conflict_action_delete },
+ { "add", svn_wc_conflict_action_add },
+ { "replace", svn_wc_conflict_action_replace },
+ { NULL, 0 }
+};
+
+/* A map for svn_wc_conflict_reason_t values to human-readable strings */
+static const svn_token_map_t map_conflict_reason_human[] =
+{
+ { N_("edit"), svn_wc_conflict_reason_edited },
+ { N_("delete"), svn_wc_conflict_reason_deleted },
+ { N_("missing"), svn_wc_conflict_reason_missing },
+ { N_("obstruction"), svn_wc_conflict_reason_obstructed },
+ { N_("add"), svn_wc_conflict_reason_added },
+ { N_("replace"), svn_wc_conflict_reason_replaced },
+ { N_("unversioned"), svn_wc_conflict_reason_unversioned },
+ { NULL, 0 }
+};
+
+/* A map for svn_wc_conflict_reason_t values to XML strings */
+static const svn_token_map_t map_conflict_reason_xml[] =
+{
+ { "edit", svn_wc_conflict_reason_edited },
+ { "delete", svn_wc_conflict_reason_deleted },
+ { "missing", svn_wc_conflict_reason_missing },
+ { "obstruction", svn_wc_conflict_reason_obstructed },
+ { "add", svn_wc_conflict_reason_added },
+ { "replace", svn_wc_conflict_reason_replaced },
+ { "unversioned", svn_wc_conflict_reason_unversioned },
+ { NULL, 0 }
+};
+
+/* Return a localized string representation of CONFLICT->action. */
+static const char *
+action_str(const svn_wc_conflict_description2_t *conflict)
+{
+ return _(svn_token__to_word(map_conflict_action_human, conflict->action));
+}
+
+/* Return a localized string representation of CONFLICT->reason. */
+static const char *
+reason_str(const svn_wc_conflict_description2_t *conflict)
+{
+ return _(svn_token__to_word(map_conflict_reason_human, conflict->reason));
+}
+
+
+svn_error_t *
+svn_cl__get_human_readable_tree_conflict_description(
+ const char **desc,
+ const svn_wc_conflict_description2_t *conflict,
+ apr_pool_t *pool)
+{
+ const char *action, *reason, *operation;
+ reason = reason_str(conflict);
+ action = action_str(conflict);
+ operation = svn_cl__operation_str_human_readable(conflict->operation, pool);
+ SVN_ERR_ASSERT(action && reason);
+ *desc = apr_psprintf(pool, _("local %s, incoming %s upon %s"),
+ reason, action, operation);
+ return SVN_NO_ERROR;
+}
+
+
+/* Helper for svn_cl__append_tree_conflict_info_xml().
+ * Appends the attributes of the given VERSION to ATT_HASH.
+ * SIDE is the content of the version tag's side="..." attribute,
+ * currently one of "source-left" or "source-right".*/
+static svn_error_t *
+add_conflict_version_xml(svn_stringbuf_t **pstr,
+ const char *side,
+ const svn_wc_conflict_version_t *version,
+ apr_pool_t *pool)
+{
+ apr_hash_t *att_hash = apr_hash_make(pool);
+
+
+ apr_hash_set(att_hash, "side", APR_HASH_KEY_STRING, side);
+
+ if (version->repos_url)
+ apr_hash_set(att_hash, "repos-url", APR_HASH_KEY_STRING,
+ version->repos_url);
+
+ if (version->path_in_repos)
+ apr_hash_set(att_hash, "path-in-repos", APR_HASH_KEY_STRING,
+ version->path_in_repos);
+
+ if (SVN_IS_VALID_REVNUM(version->peg_rev))
+ apr_hash_set(att_hash, "revision", APR_HASH_KEY_STRING,
+ apr_ltoa(pool, version->peg_rev));
+
+ if (version->node_kind != svn_node_unknown)
+ apr_hash_set(att_hash, "kind", APR_HASH_KEY_STRING,
+ svn_cl__node_kind_str_xml(version->node_kind));
+
+ svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
+ "version", att_hash);
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_cl__append_tree_conflict_info_xml(
+ svn_stringbuf_t *str,
+ const svn_wc_conflict_description2_t *conflict,
+ apr_pool_t *pool)
+{
+ apr_hash_t *att_hash = apr_hash_make(pool);
+ const char *tmp;
+
+ apr_hash_set(att_hash, "victim", APR_HASH_KEY_STRING,
+ svn_dirent_basename(conflict->local_abspath, pool));
+
+ apr_hash_set(att_hash, "kind", APR_HASH_KEY_STRING,
+ svn_cl__node_kind_str_xml(conflict->node_kind));
+
+ apr_hash_set(att_hash, "operation", APR_HASH_KEY_STRING,
+ svn_cl__operation_str_xml(conflict->operation, pool));
+
+ tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
+ apr_hash_set(att_hash, "action", APR_HASH_KEY_STRING, tmp);
+
+ tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
+ apr_hash_set(att_hash, "reason", APR_HASH_KEY_STRING, tmp);
+
+ /* Open the tree-conflict tag. */
+ svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
+ "tree-conflict", att_hash);
+
+ /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
+
+ if (conflict->src_left_version)
+ SVN_ERR(add_conflict_version_xml(&str,
+ "source-left",
+ conflict->src_left_version,
+ pool));
+
+ if (conflict->src_right_version)
+ SVN_ERR(add_conflict_version_xml(&str,
+ "source-right",
+ conflict->src_right_version,
+ pool));
+
+ svn_xml_make_close_tag(&str, pool, "tree-conflict");
+
+ return SVN_NO_ERROR;
+}
+
diff --git a/subversion/svn/tree-conflicts.h b/subversion/svn/tree-conflicts.h
new file mode 100644
index 0000000..7a01604
--- /dev/null
+++ b/subversion/svn/tree-conflicts.h
@@ -0,0 +1,68 @@
+/*
+ * tree-conflicts.h: Tree conflicts.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+
+
+#ifndef SVN_TREE_CONFLICTS_H
+#define SVN_TREE_CONFLICTS_H
+
+/*** Includes. ***/
+#include <apr_pools.h>
+
+#include "svn_types.h"
+#include "svn_string.h"
+#include "svn_wc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/**
+ * Return in @a desc a possibly localized human readable
+ * description of a tree conflict described by @a conflict.
+ *
+ * Allocate the result in @a pool.
+ */
+svn_error_t *
+svn_cl__get_human_readable_tree_conflict_description(
+ const char **desc,
+ const svn_wc_conflict_description2_t *conflict,
+ apr_pool_t *pool);
+
+/**
+ * Append to @a str an XML representation of the tree conflict data
+ * for @a conflict, in a format suitable for 'svn info --xml'.
+ */
+svn_error_t *
+svn_cl__append_tree_conflict_info_xml(
+ svn_stringbuf_t *str,
+ const svn_wc_conflict_description2_t *conflict,
+ apr_pool_t *pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_TREE_CONFLICTS_H */
diff --git a/subversion/svn/unlock-cmd.c b/subversion/svn/unlock-cmd.c
new file mode 100644
index 0000000..0f94d2a
--- /dev/null
+++ b/subversion/svn/unlock-cmd.c
@@ -0,0 +1,68 @@
+/*
+ * unlock-cmd.c -- Unlock a working copy path.
+ *
+ * ====================================================================
+ * 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_path.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_cmdline.h"
+#include "cl.h"
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__unlock(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;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE,
+ scratch_pool));
+
+ /* We don't support unlock on directories, so "." is not relevant. */
+ if (! targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__assert_homogeneous_target_type(targets));
+
+ return svn_error_trace(
+ svn_client_unlock(targets, opt_state->force, ctx, scratch_pool));
+}
diff --git a/subversion/svn/update-cmd.c b/subversion/svn/update-cmd.c
new file mode 100644
index 0000000..68d01f0
--- /dev/null
+++ b/subversion/svn/update-cmd.c
@@ -0,0 +1,181 @@
+/*
+ * update-cmd.c -- Bring work tree in sync with repository
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_path.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Print an update summary when there's more than one target to report
+ about. Each (const char *) path in TARGETS is an absolute or relative
+ dirent, and each (svn_revnum_t) entry in RESULT_REVS is the corresponding
+ updated revision, or SVN_INVALID_REVNUM if not a valid target. */
+static svn_error_t *
+print_update_summary(apr_array_header_t *targets,
+ apr_array_header_t *result_revs,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ const char *path_prefix;
+ apr_pool_t *iterpool;
+
+ if (targets->nelts < 2)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, _("Summary of updates:\n")));
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(targets, i, const char *);
+ svn_revnum_t rev = SVN_INVALID_REVNUM;
+
+ svn_pool_clear(iterpool);
+
+ /* PATH shouldn't be a URL. */
+ SVN_ERR_ASSERT(! svn_path_is_url(path));
+
+ /* Grab the result revision from the corresponding slot in our
+ RESULT_REVS array. */
+ if (i < result_revs->nelts)
+ rev = APR_ARRAY_IDX(result_revs, i, svn_revnum_t);
+
+ /* No result rev? We must have skipped this path. At any rate,
+ nothing to report here. */
+ if (! SVN_IS_VALID_REVNUM(rev))
+ continue;
+
+ /* Convert to an absolute path if it's not already. */
+ if (! svn_dirent_is_absolute(path))
+ SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
+
+ /* Print an update summary for this target, removing the current
+ working directory prefix from PATH (if PATH is at or under
+ $CWD), and converting the path to local style for display. */
+ SVN_ERR(svn_cmdline_printf(iterpool, _(" Updated '%s' to r%ld.\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, path, iterpool),
+ rev));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__update(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;
+ svn_depth_t depth;
+ svn_boolean_t depth_is_sticky;
+ struct svn_cl__check_externals_failed_notify_baton nwb;
+ apr_array_header_t *result_revs;
+
+ 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__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ /* If using changelists, convert targets into a set of paths that
+ match the specified changelist(s). */
+ if (opt_state->changelists)
+ {
+ svn_depth_t cl_depth = opt_state->depth;
+ if (cl_depth == svn_depth_unknown)
+ cl_depth = svn_depth_infinity;
+ SVN_ERR(svn_cl__changelist_paths(&targets,
+ opt_state->changelists, targets,
+ cl_depth, ctx, scratch_pool,
+ scratch_pool));
+ }
+
+ /* Deal with depthstuffs. */
+ if (opt_state->set_depth != svn_depth_unknown)
+ {
+ depth = opt_state->set_depth;
+ depth_is_sticky = TRUE;
+ }
+ else
+ {
+ depth = opt_state->depth;
+ depth_is_sticky = FALSE;
+ }
+
+ nwb.wrapped_func = ctx->notify_func2;
+ nwb.wrapped_baton = ctx->notify_baton2;
+ nwb.had_externals_error = FALSE;
+ ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
+ ctx->notify_baton2 = &nwb;
+
+ SVN_ERR(svn_client_update4(&result_revs, targets,
+ &(opt_state->start_revision),
+ depth, depth_is_sticky,
+ opt_state->ignore_externals,
+ opt_state->force, TRUE /* adds_as_modification */,
+ opt_state->parents,
+ ctx, scratch_pool));
+
+ if (! opt_state->quiet)
+ {
+ SVN_ERR(print_update_summary(targets, result_revs, scratch_pool));
+
+ /* ### Layering problem: This call assumes that the baton we're
+ * passing is the one that was originally provided by
+ * svn_cl__get_notifier(), but that isn't promised. */
+ SVN_ERR(svn_cl__print_conflict_stats(nwb.wrapped_baton, scratch_pool));
+ }
+
+ if (nwb.had_externals_error)
+ return svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL,
+ _("Failure occurred processing one or more "
+ "externals definitions"));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/upgrade-cmd.c b/subversion/svn/upgrade-cmd.c
new file mode 100644
index 0000000..e2df143
--- /dev/null
+++ b/subversion/svn/upgrade-cmd.c
@@ -0,0 +1,78 @@
+/*
+ * upgrade-cmd.c -- Upgrade a working copy.
+ *
+ * ====================================================================
+ * 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_pools.h"
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "cl.h"
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__upgrade(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;
+ int i;
+
+ 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__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+ SVN_ERR(svn_client_upgrade(target, ctx, scratch_pool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/util.c b/subversion/svn/util.c
new file mode 100644
index 0000000..be8de9a
--- /dev/null
+++ b/subversion/svn/util.c
@@ -0,0 +1,1421 @@
+/*
+ * util.c: Subversion command line client utility functions. Any
+ * functions that need to be shared across subcommands should be put
+ * in here.
+ *
+ * ====================================================================
+ * 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 <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <apr_env.h>
+#include <apr_errno.h>
+#include <apr_file_info.h>
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_general.h>
+#include <apr_lib.h>
+
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_ctype.h"
+#include "svn_client.h"
+#include "svn_cmdline.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_hash.h"
+#include "svn_io.h"
+#include "svn_utf.h"
+#include "svn_subst.h"
+#include "svn_config.h"
+#include "svn_xml.h"
+#include "svn_time.h"
+#include "svn_private_config.h"
+#include "cl.h"
+
+#include "private/svn_token.h"
+#include "private/svn_opt_private.h"
+#include "private/svn_client_private.h"
+#include "private/svn_string_private.h"
+
+
+
+
+svn_error_t *
+svn_cl__print_commit_info(const svn_commit_info_t *commit_info,
+ void *baton,
+ apr_pool_t *pool)
+{
+ if (SVN_IS_VALID_REVNUM(commit_info->revision))
+ SVN_ERR(svn_cmdline_printf(pool, _("\nCommitted revision %ld%s.\n"),
+ commit_info->revision,
+ commit_info->revision == 42 &&
+ getenv("SVN_I_LOVE_PANGALACTIC_GARGLE_BLASTERS")
+ ? _(" (the answer to life, the universe, "
+ "and everything)")
+ : ""));
+
+ /* Writing to stdout, as there maybe systems that consider the
+ * presence of stderr as an indication of commit failure.
+ * OTOH, this is only of informational nature to the user as
+ * the commit has succeeded. */
+ if (commit_info->post_commit_err)
+ SVN_ERR(svn_cmdline_printf(pool, _("\nWarning: %s\n"),
+ commit_info->post_commit_err));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Helper for the next two functions. Set *EDITOR to some path to an
+ editor binary. Sources to search include: the EDITOR_CMD argument
+ (if not NULL), $SVN_EDITOR, the runtime CONFIG variable (if CONFIG
+ is not NULL), $VISUAL, $EDITOR. Return
+ SVN_ERR_CL_NO_EXTERNAL_EDITOR if no binary can be found. */
+static svn_error_t *
+find_editor_binary(const char **editor,
+ const char *editor_cmd,
+ apr_hash_t *config)
+{
+ const char *e;
+ struct svn_config_t *cfg;
+
+ /* Use the editor specified on the command line via --editor-cmd, if any. */
+ e = editor_cmd;
+
+ /* Otherwise look for the Subversion-specific environment variable. */
+ if (! e)
+ e = getenv("SVN_EDITOR");
+
+ /* If not found then fall back on the config file. */
+ if (! e)
+ {
+ cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
+ APR_HASH_KEY_STRING) : NULL;
+ svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
+ }
+
+ /* If not found yet then try general purpose environment variables. */
+ if (! e)
+ e = getenv("VISUAL");
+ if (! e)
+ e = getenv("EDITOR");
+
+#ifdef SVN_CLIENT_EDITOR
+ /* If still not found then fall back on the hard-coded default. */
+ if (! e)
+ e = SVN_CLIENT_EDITOR;
+#endif
+
+ /* Error if there is no editor specified */
+ if (e)
+ {
+ const char *c;
+
+ for (c = e; *c; c++)
+ if (!svn_ctype_isspace(*c))
+ break;
+
+ if (! *c)
+ return svn_error_create
+ (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
+ _("The EDITOR, SVN_EDITOR or VISUAL environment variable or "
+ "'editor-cmd' run-time configuration option is empty or "
+ "consists solely of whitespace. Expected a shell command."));
+ }
+ else
+ return svn_error_create
+ (SVN_ERR_CL_NO_EXTERNAL_EDITOR, NULL,
+ _("None of the environment variables SVN_EDITOR, VISUAL or EDITOR are "
+ "set, and no 'editor-cmd' run-time configuration option was found"));
+
+ *editor = e;
+ return SVN_NO_ERROR;
+}
+
+
+/* Use the visual editor to edit files. This requires that the file name itself
+ be shell-safe, although the path to reach that file need not be shell-safe.
+ */
+svn_error_t *
+svn_cl__edit_file_externally(const char *path,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ const char *editor, *cmd, *base_dir, *file_name, *base_dir_apr;
+ char *old_cwd;
+ int sys_err;
+ apr_status_t apr_err;
+
+ svn_dirent_split(&base_dir, &file_name, path, pool);
+
+ SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
+
+ apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
+
+ /* APR doesn't like "" directories */
+ if (base_dir[0] == '\0')
+ base_dir_apr = ".";
+ else
+ SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
+
+ apr_err = apr_filepath_set(base_dir_apr, pool);
+ if (apr_err)
+ return svn_error_wrap_apr
+ (apr_err, _("Can't change working directory to '%s'"), base_dir);
+
+ cmd = apr_psprintf(pool, "%s %s", editor, file_name);
+ sys_err = system(cmd);
+
+ apr_err = apr_filepath_set(old_cwd, pool);
+ if (apr_err)
+ svn_handle_error2(svn_error_wrap_apr
+ (apr_err, _("Can't restore working directory")),
+ stderr, TRUE /* fatal */, "svn: ");
+
+ if (sys_err)
+ /* Extracting any meaning from sys_err is platform specific, so just
+ use the raw value. */
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("system('%s') returned %d"), cmd, sys_err);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__merge_file_externally(const char *base_path,
+ const char *their_path,
+ const char *my_path,
+ const char *merged_path,
+ const char *wc_path,
+ apr_hash_t *config,
+ svn_boolean_t *remains_in_conflict,
+ apr_pool_t *pool)
+{
+ char *merge_tool;
+ /* Error if there is no editor specified */
+ if (apr_env_get(&merge_tool, "SVN_MERGE", pool) != APR_SUCCESS)
+ {
+ struct svn_config_t *cfg;
+ merge_tool = NULL;
+ cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
+ APR_HASH_KEY_STRING) : NULL;
+ /* apr_env_get wants char **, this wants const char ** */
+ svn_config_get(cfg, (const char **)&merge_tool,
+ SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_MERGE_TOOL_CMD, NULL);
+ }
+
+ if (merge_tool)
+ {
+ const char *c;
+
+ for (c = merge_tool; *c; c++)
+ if (!svn_ctype_isspace(*c))
+ break;
+
+ if (! *c)
+ return svn_error_create
+ (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
+ _("The SVN_MERGE environment variable is empty or "
+ "consists solely of whitespace. Expected a shell command.\n"));
+ }
+ else
+ return svn_error_create
+ (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL,
+ _("The environment variable SVN_MERGE and the merge-tool-cmd run-time "
+ "configuration option were not set.\n"));
+
+ {
+ const char *arguments[7] = { 0 };
+ char *cwd;
+ int exitcode;
+
+ apr_status_t status = apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, pool);
+ if (status != 0)
+ return svn_error_wrap_apr(status, NULL);
+
+ arguments[0] = merge_tool;
+ arguments[1] = base_path;
+ arguments[2] = their_path;
+ arguments[3] = my_path;
+ arguments[4] = merged_path;
+ arguments[5] = wc_path;
+ arguments[6] = NULL;
+
+ SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), merge_tool,
+ arguments, &exitcode, NULL, TRUE, NULL, NULL, NULL,
+ pool));
+ /* Exit code 0 means the merge was successful.
+ * Exit code 1 means the file was left in conflict but it
+ * is OK to continue with the merge.
+ * Any other exit code means there was a real problem. */
+ if (exitcode != 0 && exitcode != 1)
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("The external merge tool exited with exit code %d"), exitcode);
+ else if (remains_in_conflict)
+ *remains_in_conflict = exitcode == 1;
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__edit_string_externally(svn_string_t **edited_contents /* UTF-8! */,
+ const char **tmpfile_left /* UTF-8! */,
+ const char *editor_cmd,
+ const char *base_dir /* UTF-8! */,
+ const svn_string_t *contents /* UTF-8! */,
+ const char *filename,
+ apr_hash_t *config,
+ svn_boolean_t as_text,
+ const char *encoding,
+ apr_pool_t *pool)
+{
+ const char *editor;
+ const char *cmd;
+ apr_file_t *tmp_file;
+ const char *tmpfile_name;
+ const char *tmpfile_native;
+ const char *tmpfile_apr, *base_dir_apr;
+ svn_string_t *translated_contents;
+ apr_status_t apr_err, apr_err2;
+ apr_size_t written;
+ apr_finfo_t finfo_before, finfo_after;
+ svn_error_t *err = SVN_NO_ERROR, *err2;
+ char *old_cwd;
+ int sys_err;
+ svn_boolean_t remove_file = TRUE;
+
+ SVN_ERR(find_editor_binary(&editor, editor_cmd, config));
+
+ /* Convert file contents from UTF-8/LF if desired. */
+ if (as_text)
+ {
+ const char *translated;
+ SVN_ERR(svn_subst_translate_cstring2(contents->data, &translated,
+ APR_EOL_STR, FALSE,
+ NULL, FALSE, pool));
+ translated_contents = svn_string_create("", pool);
+ if (encoding)
+ SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated_contents->data,
+ translated, encoding, pool));
+ else
+ SVN_ERR(svn_utf_cstring_from_utf8(&translated_contents->data,
+ translated, pool));
+ translated_contents->len = strlen(translated_contents->data);
+ }
+ else
+ translated_contents = svn_string_dup(contents, pool);
+
+ /* Move to BASE_DIR to avoid getting characters that need quoting
+ into tmpfile_name */
+ apr_err = apr_filepath_get(&old_cwd, APR_FILEPATH_NATIVE, pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, _("Can't get working directory"));
+
+ /* APR doesn't like "" directories */
+ if (base_dir[0] == '\0')
+ base_dir_apr = ".";
+ else
+ SVN_ERR(svn_path_cstring_from_utf8(&base_dir_apr, base_dir, pool));
+ apr_err = apr_filepath_set(base_dir_apr, pool);
+ if (apr_err)
+ {
+ return svn_error_wrap_apr
+ (apr_err, _("Can't change working directory to '%s'"), base_dir);
+ }
+
+ /*** From here on, any problems that occur require us to cd back!! ***/
+
+ /* Ask the working copy for a temporary file named FILENAME-something. */
+ err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
+ "" /* dirpath */,
+ filename,
+ ".tmp",
+ svn_io_file_del_none, pool, pool);
+
+ if (err && (APR_STATUS_IS_EACCES(err->apr_err) || err->apr_err == EROFS))
+ {
+ const char *temp_dir_apr;
+
+ svn_error_clear(err);
+
+ SVN_ERR(svn_io_temp_dir(&base_dir, pool));
+
+ SVN_ERR(svn_path_cstring_from_utf8(&temp_dir_apr, base_dir, pool));
+ apr_err = apr_filepath_set(temp_dir_apr, pool);
+ if (apr_err)
+ {
+ return svn_error_wrap_apr
+ (apr_err, _("Can't change working directory to '%s'"), base_dir);
+ }
+
+ err = svn_io_open_uniquely_named(&tmp_file, &tmpfile_name,
+ "" /* dirpath */,
+ filename,
+ ".tmp",
+ svn_io_file_del_none, pool, pool);
+ }
+
+ if (err)
+ goto cleanup2;
+
+ /*** From here on, any problems that occur require us to cleanup
+ the file we just created!! ***/
+
+ /* Dump initial CONTENTS to TMP_FILE. */
+ apr_err = apr_file_write_full(tmp_file, translated_contents->data,
+ translated_contents->len, &written);
+
+ apr_err2 = apr_file_close(tmp_file);
+ if (! apr_err)
+ apr_err = apr_err2;
+
+ /* Make sure the whole CONTENTS were written, else return an error. */
+ if (apr_err)
+ {
+ err = svn_error_wrap_apr(apr_err, _("Can't write to '%s'"),
+ tmpfile_name);
+ goto cleanup;
+ }
+
+ err = svn_path_cstring_from_utf8(&tmpfile_apr, tmpfile_name, pool);
+ if (err)
+ goto cleanup;
+
+ /* Get information about the temporary file before the user has
+ been allowed to edit its contents. */
+ apr_err = apr_stat(&finfo_before, tmpfile_apr,
+ APR_FINFO_MTIME, pool);
+ if (apr_err)
+ {
+ err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
+ goto cleanup;
+ }
+
+ /* Backdate the file a little bit in case the editor is very fast
+ and doesn't change the size. (Use two seconds, since some
+ filesystems have coarse granularity.) It's OK if this call
+ fails, so we don't check its return value.*/
+ apr_file_mtime_set(tmpfile_apr, finfo_before.mtime - 2000, pool);
+
+ /* Stat it again to get the mtime we actually set. */
+ apr_err = apr_stat(&finfo_before, tmpfile_apr,
+ APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
+ if (apr_err)
+ {
+ err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
+ goto cleanup;
+ }
+
+ /* Prepare the editor command line. */
+ err = svn_utf_cstring_from_utf8(&tmpfile_native, tmpfile_name, pool);
+ if (err)
+ goto cleanup;
+ cmd = apr_psprintf(pool, "%s %s", editor, tmpfile_native);
+
+ /* If the caller wants us to leave the file around, return the path
+ of the file we'll use, and make a note not to destroy it. */
+ if (tmpfile_left)
+ {
+ *tmpfile_left = svn_dirent_join(base_dir, tmpfile_name, pool);
+ remove_file = FALSE;
+ }
+
+ /* Now, run the editor command line. */
+ sys_err = system(cmd);
+ if (sys_err != 0)
+ {
+ /* Extracting any meaning from sys_err is platform specific, so just
+ use the raw value. */
+ err = svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("system('%s') returned %d"), cmd, sys_err);
+ goto cleanup;
+ }
+
+ /* Get information about the temporary file after the assumed editing. */
+ apr_err = apr_stat(&finfo_after, tmpfile_apr,
+ APR_FINFO_MTIME | APR_FINFO_SIZE, pool);
+ if (apr_err)
+ {
+ err = svn_error_wrap_apr(apr_err, _("Can't stat '%s'"), tmpfile_name);
+ goto cleanup;
+ }
+
+ /* If the file looks changed... */
+ if ((finfo_before.mtime != finfo_after.mtime) ||
+ (finfo_before.size != finfo_after.size))
+ {
+ svn_stringbuf_t *edited_contents_s;
+ err = svn_stringbuf_from_file2(&edited_contents_s, tmpfile_name, pool);
+ if (err)
+ goto cleanup;
+
+ *edited_contents = svn_stringbuf__morph_into_string(edited_contents_s);
+
+ /* Translate back to UTF8/LF if desired. */
+ if (as_text)
+ {
+ err = svn_subst_translate_string2(edited_contents, FALSE, FALSE,
+ *edited_contents, encoding, FALSE,
+ pool, pool);
+ if (err)
+ {
+ err = svn_error_quick_wrap
+ (err,
+ _("Error normalizing edited contents to internal format"));
+ goto cleanup;
+ }
+ }
+ }
+ else
+ {
+ /* No edits seem to have been made */
+ *edited_contents = NULL;
+ }
+
+ cleanup:
+ if (remove_file)
+ {
+ /* Remove the file from disk. */
+ err2 = svn_io_remove_file2(tmpfile_name, FALSE, pool);
+
+ /* Only report remove error if there was no previous error. */
+ if (! err && err2)
+ err = err2;
+ else
+ svn_error_clear(err2);
+ }
+
+ cleanup2:
+ /* If we against all probability can't cd back, all further relative
+ file references would be screwed up, so we have to abort. */
+ apr_err = apr_filepath_set(old_cwd, pool);
+ if (apr_err)
+ {
+ svn_handle_error2(svn_error_wrap_apr
+ (apr_err, _("Can't restore working directory")),
+ stderr, TRUE /* fatal */, "svn: ");
+ }
+
+ return svn_error_trace(err);
+}
+
+
+/* A svn_client_ctx_t's log_msg_baton3, for use with
+ svn_cl__make_log_msg_baton(). */
+struct log_msg_baton
+{
+ const char *editor_cmd; /* editor specified via --editor-cmd, else NULL */
+ const char *message; /* the message. */
+ const char *message_encoding; /* the locale/encoding of the message. */
+ const char *base_dir; /* the base directory for an external edit. UTF-8! */
+ const char *tmpfile_left; /* the tmpfile left by an external edit. UTF-8! */
+ svn_boolean_t non_interactive; /* if true, don't pop up an editor */
+ apr_hash_t *config; /* client configuration hash */
+ svn_boolean_t keep_locks; /* Keep repository locks? */
+ apr_pool_t *pool; /* a pool. */
+};
+
+
+svn_error_t *
+svn_cl__make_log_msg_baton(void **baton,
+ svn_cl__opt_state_t *opt_state,
+ const char *base_dir /* UTF-8! */,
+ apr_hash_t *config,
+ apr_pool_t *pool)
+{
+ struct log_msg_baton *lmb = apr_palloc(pool, sizeof(*lmb));
+
+ if (opt_state->filedata)
+ {
+ if (strlen(opt_state->filedata->data) < opt_state->filedata->len)
+ {
+ /* The data contains a zero byte, and therefore can't be
+ represented as a C string. Punt now; it's probably not
+ a deliberate encoding, and even if it is, we still
+ can't handle it. */
+ return svn_error_create(SVN_ERR_CL_BAD_LOG_MESSAGE, NULL,
+ _("Log message contains a zero byte"));
+ }
+ lmb->message = opt_state->filedata->data;
+ }
+ else
+ {
+ lmb->message = opt_state->message;
+ }
+
+ lmb->editor_cmd = opt_state->editor_cmd;
+ if (opt_state->encoding)
+ {
+ lmb->message_encoding = opt_state->encoding;
+ }
+ else if (config)
+ {
+ svn_config_t *cfg = apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
+ APR_HASH_KEY_STRING);
+ svn_config_get(cfg, &(lmb->message_encoding),
+ SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_LOG_ENCODING,
+ NULL);
+ }
+
+ lmb->base_dir = base_dir ? base_dir : "";
+ lmb->tmpfile_left = NULL;
+ lmb->config = config;
+ lmb->keep_locks = opt_state->no_unlock;
+ lmb->non_interactive = opt_state->non_interactive;
+ lmb->pool = pool;
+ *baton = lmb;
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_cl__cleanup_log_msg(void *log_msg_baton,
+ svn_error_t *commit_err,
+ apr_pool_t *pool)
+{
+ struct log_msg_baton *lmb = log_msg_baton;
+ svn_error_t *err;
+
+ /* If there was no tmpfile left, or there is no log message baton,
+ return COMMIT_ERR. */
+ if ((! lmb) || (! lmb->tmpfile_left))
+ return commit_err;
+
+ /* If there was no commit error, cleanup the tmpfile and return. */
+ if (! commit_err)
+ return svn_io_remove_file2(lmb->tmpfile_left, FALSE, lmb->pool);
+
+ /* There was a commit error; there is a tmpfile. Leave the tmpfile
+ around, and add message about its presence to the commit error
+ chain. Then return COMMIT_ERR. If the conversion from UTF-8 to
+ native encoding fails, we have to compose that error with the
+ commit error chain, too. */
+
+ err = svn_error_createf(commit_err->apr_err, NULL,
+ _(" '%s'"),
+ svn_dirent_local_style(lmb->tmpfile_left, pool));
+ svn_error_compose(commit_err,
+ svn_error_create(commit_err->apr_err, err,
+ _("Your commit message was left in "
+ "a temporary file:")));
+ return commit_err;
+}
+
+
+/* Remove line-starting PREFIX and everything after it from BUFFER.
+ If NEW_LEN is non-NULL, return the new length of BUFFER in
+ *NEW_LEN. */
+static void
+truncate_buffer_at_prefix(apr_size_t *new_len,
+ char *buffer,
+ const char *prefix)
+{
+ char *substring = buffer;
+
+ assert(buffer && prefix);
+
+ /* Initialize *NEW_LEN. */
+ if (new_len)
+ *new_len = strlen(buffer);
+
+ while (1)
+ {
+ /* Find PREFIX in BUFFER. */
+ substring = strstr(substring, prefix);
+ if (! substring)
+ return;
+
+ /* We found PREFIX. Is it really a PREFIX? Well, if it's the first
+ thing in the file, or if the character before it is a
+ line-terminator character, it sure is. */
+ if ((substring == buffer)
+ || (*(substring - 1) == '\r')
+ || (*(substring - 1) == '\n'))
+ {
+ *substring = '\0';
+ if (new_len)
+ *new_len = substring - buffer;
+ }
+ else if (substring)
+ {
+ /* Well, it wasn't really a prefix, so just advance by 1
+ character and continue. */
+ substring++;
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+
+#define EDITOR_EOF_PREFIX _("--This line, and those below, will be ignored--")
+
+svn_error_t *
+svn_cl__get_log_message(const char **log_msg,
+ const char **tmp_file,
+ const apr_array_header_t *commit_items,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *default_msg = NULL;
+ struct log_msg_baton *lmb = baton;
+ svn_stringbuf_t *message = NULL;
+
+ /* Set default message. */
+ default_msg = svn_stringbuf_create(APR_EOL_STR, pool);
+ svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX);
+ svn_stringbuf_appendcstr(default_msg, APR_EOL_STR APR_EOL_STR);
+
+ *tmp_file = NULL;
+ if (lmb->message)
+ {
+ svn_stringbuf_t *log_msg_buf = svn_stringbuf_create(lmb->message, pool);
+ svn_string_t *log_msg_str = apr_pcalloc(pool, sizeof(*log_msg_str));
+
+ /* Trim incoming messages of the EOF marker text and the junk
+ that follows it. */
+ truncate_buffer_at_prefix(&(log_msg_buf->len), log_msg_buf->data,
+ EDITOR_EOF_PREFIX);
+
+ /* Make a string from a stringbuf, sharing the data allocation. */
+ log_msg_str->data = log_msg_buf->data;
+ log_msg_str->len = log_msg_buf->len;
+ SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, FALSE, FALSE,
+ log_msg_str, lmb->message_encoding,
+ FALSE, pool, pool),
+ _("Error normalizing log message to internal format"));
+
+ *log_msg = log_msg_str->data;
+ return SVN_NO_ERROR;
+ }
+
+ if (! commit_items->nelts)
+ {
+ *log_msg = "";
+ return SVN_NO_ERROR;
+ }
+
+ while (! message)
+ {
+ /* We still don't have a valid commit message. Use $EDITOR to
+ get one. Note that svn_cl__edit_externally will still return
+ a UTF-8'ized log message. */
+ int i;
+ svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool);
+ svn_error_t *err = SVN_NO_ERROR;
+ svn_string_t *msg_string = svn_string_create("", pool);
+
+ for (i = 0; i < commit_items->nelts; i++)
+ {
+ svn_client_commit_item3_t *item
+ = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
+ const char *path = item->path;
+ char text_mod = '_', prop_mod = ' ', unlock = ' ';
+
+ if (! path)
+ path = item->url;
+ else if (! *path)
+ path = ".";
+
+ if (! svn_path_is_url(path) && lmb->base_dir)
+ path = svn_dirent_is_child(lmb->base_dir, path, pool);
+
+ /* If still no path, then just use current directory. */
+ if (! path)
+ path = ".";
+
+ if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ text_mod = 'R';
+ else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
+ text_mod = 'A';
+ else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ text_mod = 'D';
+ else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)
+ text_mod = 'M';
+
+ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS)
+ prop_mod = 'M';
+
+ if (! lmb->keep_locks
+ && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)
+ unlock = 'U';
+
+ svn_stringbuf_appendbyte(tmp_message, text_mod);
+ svn_stringbuf_appendbyte(tmp_message, prop_mod);
+ svn_stringbuf_appendbyte(tmp_message, unlock);
+ if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY)
+ /* History included via copy/move. */
+ svn_stringbuf_appendcstr(tmp_message, "+ ");
+ else
+ svn_stringbuf_appendcstr(tmp_message, " ");
+ svn_stringbuf_appendcstr(tmp_message, path);
+ svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR);
+ }
+
+ msg_string->data = tmp_message->data;
+ msg_string->len = tmp_message->len;
+
+ /* Use the external edit to get a log message. */
+ if (! lmb->non_interactive)
+ {
+ err = svn_cl__edit_string_externally(&msg_string, &lmb->tmpfile_left,
+ lmb->editor_cmd, lmb->base_dir,
+ msg_string, "svn-commit",
+ lmb->config, TRUE,
+ lmb->message_encoding,
+ pool);
+ }
+ else /* non_interactive flag says we can't pop up an editor, so error */
+ {
+ return svn_error_create
+ (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("Cannot invoke editor to get log message "
+ "when non-interactive"));
+ }
+
+ /* Dup the tmpfile path into its baton's pool. */
+ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool,
+ lmb->tmpfile_left);
+
+ /* If the edit returned an error, handle it. */
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR)
+ err = svn_error_quick_wrap
+ (err, _("Could not use external editor to fetch log message; "
+ "consider setting the $SVN_EDITOR environment variable "
+ "or using the --message (-m) or --file (-F) options"));
+ return svn_error_trace(err);
+ }
+
+ if (msg_string)
+ message = svn_stringbuf_create_from_string(msg_string, pool);
+
+ /* Strip the prefix from the buffer. */
+ if (message)
+ truncate_buffer_at_prefix(&message->len, message->data,
+ EDITOR_EOF_PREFIX);
+
+ if (message)
+ {
+ /* We did get message, now check if it is anything more than just
+ white space as we will consider white space only as empty */
+ apr_size_t len;
+
+ for (len = 0; len < message->len; len++)
+ {
+ /* FIXME: should really use an UTF-8 whitespace test
+ rather than svn_ctype_isspace, which is ASCII only */
+ if (! svn_ctype_isspace(message->data[len]))
+ break;
+ }
+ if (len == message->len)
+ message = NULL;
+ }
+
+ if (! message)
+ {
+ const char *reply;
+ SVN_ERR(svn_cmdline_prompt_user2
+ (&reply,
+ _("\nLog message unchanged or not specified\n"
+ "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool));
+ if (reply)
+ {
+ int letter = apr_tolower(reply[0]);
+
+ /* If the user chooses to abort, we cleanup the
+ temporary file and exit the loop with a NULL
+ message. */
+ if ('a' == letter)
+ {
+ SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool));
+ *tmp_file = lmb->tmpfile_left = NULL;
+ break;
+ }
+
+ /* If the user chooses to continue, we make an empty
+ message, which will cause us to exit the loop. We
+ also cleanup the temporary file. */
+ if ('c' == letter)
+ {
+ SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool));
+ *tmp_file = lmb->tmpfile_left = NULL;
+ message = svn_stringbuf_create("", pool);
+ }
+
+ /* If the user chooses anything else, the loop will
+ continue on the NULL message. */
+ }
+ }
+ }
+
+ *log_msg = message ? message->data : NULL;
+ return SVN_NO_ERROR;
+}
+
+
+/* ### The way our error wrapping currently works, the error returned
+ * from here will look as though it originates in this source file,
+ * instead of in the caller's source file. This can be a bit
+ * misleading, until one starts debugging. Ideally, there'd be a way
+ * to wrap an error while preserving its FILE/LINE info.
+ */
+svn_error_t *
+svn_cl__may_need_force(svn_error_t *err)
+{
+ if (err
+ && (err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE ||
+ err->apr_err == SVN_ERR_CLIENT_MODIFIED))
+ {
+ /* Should this svn_error_compose a new error number? Probably not,
+ the error hasn't changed. */
+ err = svn_error_quick_wrap
+ (err, _("Use --force to override this restriction (local modifications "
+ "may be lost)"));
+ }
+
+ return svn_error_trace(err);
+}
+
+
+svn_error_t *
+svn_cl__error_checked_fputs(const char *string, FILE* stream)
+{
+ /* On POSIX systems, errno will be set on an error in fputs, but this might
+ not be the case on other platforms. We reset errno and only
+ use it if it was set by the below fputs call. Else, we just return
+ a generic error. */
+ errno = 0;
+
+ if (fputs(string, stream) == EOF)
+ {
+ if (errno)
+ return svn_error_wrap_apr(errno, _("Write error"));
+ else
+ return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_cl__try(svn_error_t *err,
+ apr_array_header_t *errors_seen,
+ svn_boolean_t quiet,
+ ...)
+{
+ if (err)
+ {
+ apr_status_t apr_err;
+ va_list ap;
+
+ va_start(ap, quiet);
+ while ((apr_err = va_arg(ap, apr_status_t)) != SVN_NO_ERROR)
+ {
+ if (errors_seen)
+ {
+ int i;
+ svn_boolean_t add = TRUE;
+
+ /* Don't report duplicate error codes. */
+ for (i = 0; i < errors_seen->nelts; i++)
+ {
+ if (APR_ARRAY_IDX(errors_seen, i,
+ apr_status_t) == err->apr_err)
+ {
+ add = FALSE;
+ break;
+ }
+ }
+ if (add)
+ APR_ARRAY_PUSH(errors_seen, apr_status_t) = err->apr_err;
+ }
+ if (err->apr_err == apr_err)
+ {
+ if (! quiet)
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ }
+ va_end(ap);
+ }
+
+ return svn_error_trace(err);
+}
+
+
+void
+svn_cl__xml_tagged_cdata(svn_stringbuf_t **sb,
+ apr_pool_t *pool,
+ const char *tagname,
+ const char *string)
+{
+ if (string)
+ {
+ svn_xml_make_open_tag(sb, pool, svn_xml_protect_pcdata,
+ tagname, NULL);
+ svn_xml_escape_cdata_cstring(sb, string, pool);
+ svn_xml_make_close_tag(sb, pool, tagname);
+ }
+}
+
+
+void
+svn_cl__print_xml_commit(svn_stringbuf_t **sb,
+ svn_revnum_t revision,
+ const char *author,
+ const char *date,
+ apr_pool_t *pool)
+{
+ /* "<commit ...>" */
+ svn_xml_make_open_tag(sb, pool, svn_xml_normal, "commit",
+ "revision",
+ apr_psprintf(pool, "%ld", revision), NULL);
+
+ /* "<author>xx</author>" */
+ if (author)
+ svn_cl__xml_tagged_cdata(sb, pool, "author", author);
+
+ /* "<date>xx</date>" */
+ if (date)
+ svn_cl__xml_tagged_cdata(sb, pool, "date", date);
+
+ /* "</commit>" */
+ svn_xml_make_close_tag(sb, pool, "commit");
+}
+
+
+void
+svn_cl__print_xml_lock(svn_stringbuf_t **sb,
+ const svn_lock_t *lock,
+ apr_pool_t *pool)
+{
+ /* "<lock>" */
+ svn_xml_make_open_tag(sb, pool, svn_xml_normal, "lock", NULL);
+
+ /* "<token>xx</token>" */
+ svn_cl__xml_tagged_cdata(sb, pool, "token", lock->token);
+
+ /* "<owner>xx</owner>" */
+ svn_cl__xml_tagged_cdata(sb, pool, "owner", lock->owner);
+
+ /* "<comment>xx</comment>" */
+ svn_cl__xml_tagged_cdata(sb, pool, "comment", lock->comment);
+
+ /* "<created>xx</created>" */
+ svn_cl__xml_tagged_cdata(sb, pool, "created",
+ svn_time_to_cstring(lock->creation_date, pool));
+
+ /* "<expires>xx</expires>" */
+ if (lock->expiration_date != 0)
+ svn_cl__xml_tagged_cdata(sb, pool, "expires",
+ svn_time_to_cstring(lock->expiration_date, pool));
+
+ /* "</lock>" */
+ svn_xml_make_close_tag(sb, pool, "lock");
+}
+
+
+svn_error_t *
+svn_cl__xml_print_header(const char *tagname,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+
+ /* <?xml version="1.0" encoding="UTF-8"?> */
+ svn_xml_make_header2(&sb, "UTF-8", pool);
+
+ /* "<TAGNAME>" */
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, tagname, NULL);
+
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+svn_error_t *
+svn_cl__xml_print_footer(const char *tagname,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *sb = svn_stringbuf_create("", pool);
+
+ /* "</TAGNAME>" */
+ svn_xml_make_close_tag(&sb, pool, tagname);
+ return svn_cl__error_checked_fputs(sb->data, stdout);
+}
+
+
+/* A map for svn_node_kind_t values to XML strings */
+static const svn_token_map_t map_node_kind_xml[] =
+{
+ { "none", svn_node_none },
+ { "file", svn_node_file },
+ { "dir", svn_node_dir },
+ { "", svn_node_unknown },
+ { NULL, 0 }
+};
+
+/* A map for svn_node_kind_t values to human-readable strings */
+static const svn_token_map_t map_node_kind_human[] =
+{
+ { N_("none"), svn_node_none },
+ { N_("file"), svn_node_file },
+ { N_("dir"), svn_node_dir },
+ { "", svn_node_unknown },
+ { NULL, 0 }
+};
+
+const char *
+svn_cl__node_kind_str_xml(svn_node_kind_t kind)
+{
+ return svn_token__to_word(map_node_kind_xml, kind);
+}
+
+const char *
+svn_cl__node_kind_str_human_readable(svn_node_kind_t kind)
+{
+ return _(svn_token__to_word(map_node_kind_human, kind));
+}
+
+
+/* A map for svn_wc_operation_t values to XML strings */
+static const svn_token_map_t map_wc_operation_xml[] =
+{
+ { "none", svn_wc_operation_none },
+ { "update", svn_wc_operation_update },
+ { "switch", svn_wc_operation_switch },
+ { "merge", svn_wc_operation_merge },
+ { NULL, 0 }
+};
+
+/* A map for svn_wc_operation_t values to human-readable strings */
+static const svn_token_map_t map_wc_operation_human[] =
+{
+ { N_("none"), svn_wc_operation_none },
+ { N_("update"), svn_wc_operation_update },
+ { N_("switch"), svn_wc_operation_switch },
+ { N_("merge"), svn_wc_operation_merge },
+ { NULL, 0 }
+};
+
+const char *
+svn_cl__operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool)
+{
+ return svn_token__to_word(map_wc_operation_xml, operation);
+}
+
+const char *
+svn_cl__operation_str_human_readable(svn_wc_operation_t operation,
+ apr_pool_t *pool)
+{
+ return _(svn_token__to_word(map_wc_operation_human, operation));
+}
+
+
+svn_error_t *
+svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t keep_last_origpath_on_truepath_collision,
+ apr_pool_t *pool)
+{
+ svn_error_t *err = svn_client_args_to_target_array2(targets,
+ os,
+ known_targets,
+ ctx,
+ keep_last_origpath_on_truepath_collision,
+ pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_RESERVED_FILENAME_SPECIFIED)
+ {
+ svn_handle_error2(err, stderr, FALSE, "svn: Skipping argument: ");
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
+ return SVN_NO_ERROR;
+}
+
+
+/* Helper for svn_cl__get_changelist(); implements
+ svn_changelist_receiver_t. */
+static svn_error_t *
+changelist_receiver(void *baton,
+ const char *path,
+ const char *changelist,
+ apr_pool_t *pool)
+{
+ /* No need to check CHANGELIST; our caller only asked about one of them. */
+ apr_array_header_t *paths = baton;
+ APR_ARRAY_PUSH(paths, const char *) = apr_pstrdup(paths->pool, path);
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_cl__changelist_paths(apr_array_header_t **paths,
+ const apr_array_header_t *changelists,
+ const apr_array_header_t *targets,
+ svn_depth_t depth,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *found;
+ apr_hash_t *paths_hash;
+ apr_pool_t *iterpool;
+ int i;
+
+ if (! (changelists && changelists->nelts))
+ {
+ *paths = (apr_array_header_t *)targets;
+ return SVN_NO_ERROR;
+ }
+
+ found = apr_array_make(scratch_pool, 8, sizeof(const char *));
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_client_get_changelists(target, changelists, depth,
+ changelist_receiver, found,
+ ctx, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ SVN_ERR(svn_hash_from_cstring_keys(&paths_hash, found, result_pool));
+ return svn_error_trace(svn_hash_keys(paths, paths_hash, result_pool));
+}
+
+svn_cl__show_revs_t
+svn_cl__show_revs_from_word(const char *word)
+{
+ if (strcmp(word, SVN_CL__SHOW_REVS_MERGED) == 0)
+ return svn_cl__show_revs_merged;
+ if (strcmp(word, SVN_CL__SHOW_REVS_ELIGIBLE) == 0)
+ return svn_cl__show_revs_eligible;
+ /* word is an invalid flavor. */
+ return svn_cl__show_revs_invalid;
+}
+
+
+svn_error_t *
+svn_cl__time_cstring_to_human_cstring(const char **human_cstring,
+ const char *data,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ apr_time_t when;
+
+ err = svn_time_from_cstring(&when, data, pool);
+ if (err && err->apr_err == SVN_ERR_BAD_DATE)
+ {
+ svn_error_clear(err);
+
+ *human_cstring = _("(invalid date)");
+ return SVN_NO_ERROR;
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ *human_cstring = svn_time_to_human_cstring(when, pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Return a copy, allocated in POOL, of the next line of text from *STR
+ * up to and including a CR and/or an LF. Change *STR to point to the
+ * remainder of the string after the returned part. If there are no
+ * characters to be returned, return NULL; never return an empty string.
+ */
+static const char *
+next_line(const char **str, apr_pool_t *pool)
+{
+ const char *start = *str;
+ const char *p = *str;
+
+ /* n.b. Throughout this fn, we never read any character after a '\0'. */
+ /* Skip over all non-EOL characters, if any. */
+ while (*p != '\r' && *p != '\n' && *p != '\0')
+ p++;
+ /* Skip over \r\n or \n\r or \r or \n, if any. */
+ if (*p == '\r' || *p == '\n')
+ {
+ char c = *p++;
+
+ if ((c == '\r' && *p == '\n') || (c == '\n' && *p == '\r'))
+ p++;
+ }
+
+ /* Now p points after at most one '\n' and/or '\r'. */
+ *str = p;
+
+ if (p == start)
+ return NULL;
+
+ return svn_string_ncreate(start, p - start, pool)->data;
+}
+
+const char *
+svn_cl__indent_string(const char *str,
+ const char *indent,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *out = svn_stringbuf_create("", pool);
+ const char *line;
+
+ while ((line = next_line(&str, pool)))
+ {
+ svn_stringbuf_appendcstr(out, indent);
+ svn_stringbuf_appendcstr(out, line);
+ }
+ return out->data;
+}
+
+const char *
+svn_cl__node_description(const svn_wc_conflict_version_t *node,
+ const char *wc_repos_root_URL,
+ apr_pool_t *pool)
+{
+ const char *root_str = "^";
+ const char *path_str = "...";
+
+ if (!node)
+ /* Printing "(none)" the harder way to ensure conformity (mostly with
+ * translations). */
+ return apr_psprintf(pool, "(%s)",
+ svn_cl__node_kind_str_human_readable(svn_node_none));
+
+ /* Construct a "caret notation" ^/URL if NODE matches WC_REPOS_ROOT_URL.
+ * Otherwise show the complete URL, and if we can't, show dots. */
+
+ if (node->repos_url &&
+ (wc_repos_root_URL == NULL ||
+ strcmp(node->repos_url, wc_repos_root_URL) != 0))
+ root_str = node->repos_url;
+
+ if (node->path_in_repos)
+ path_str = node->path_in_repos;
+
+ return apr_psprintf(pool, "(%s) %s@%ld",
+ svn_cl__node_kind_str_human_readable(node->node_kind),
+ svn_path_url_add_component2(root_str, path_str, pool),
+ node->peg_rev);
+}
+
+svn_error_t *
+svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p,
+ const apr_array_header_t *targets,
+ apr_pool_t *pool)
+{
+ int i;
+ apr_array_header_t *true_targets;
+
+ true_targets = apr_array_make(pool, targets->nelts, sizeof(const char *));
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *true_target;
+
+ SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, NULL,
+ target, pool));
+ APR_ARRAY_PUSH(true_targets, const char *) = true_target;
+ }
+
+ SVN_ERR_ASSERT(true_targets_p);
+ *true_targets_p = true_targets;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__assert_homogeneous_target_type(const apr_array_header_t *targets)
+{
+ svn_error_t *err;
+
+ err = svn_client__assert_homogeneous_target_type(targets);
+ if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
+ _("Cannot mix repository and working copy "
+ "targets"));
+ return err;
+}
+
+svn_error_t *
+svn_cl__check_target_is_local_path(const char *target)
+{
+ if (svn_path_is_url(target))
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a local path"), target);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__check_targets_are_local_paths(const apr_array_header_t *targets)
+{
+ int i;
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ SVN_ERR(svn_cl__check_target_is_local_path(target));
+ }
+ return SVN_NO_ERROR;
+}
+
+const char *
+svn_cl__local_style_skip_ancestor(const char *parent_path,
+ const char *path,
+ apr_pool_t *pool)
+{
+ const char *relpath = NULL;
+
+ if (parent_path)
+ relpath = svn_dirent_skip_ancestor(parent_path, path);
+
+ return svn_dirent_local_style(relpath ? relpath : path, pool);
+}