summaryrefslogtreecommitdiff
path: root/subversion/svn
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn')
-rw-r--r--subversion/svn/auth-cmd.c17
-rw-r--r--subversion/svn/blame-cmd.c42
-rw-r--r--subversion/svn/cl.h158
-rw-r--r--subversion/svn/conflict-callbacks.c162
-rw-r--r--subversion/svn/diff-cmd.c48
-rw-r--r--subversion/svn/filesize.c221
-rw-r--r--subversion/svn/help-cmd.c2
-rw-r--r--subversion/svn/info-cmd.c389
-rw-r--r--subversion/svn/list-cmd.c89
-rw-r--r--subversion/svn/log-cmd.c9
-rw-r--r--subversion/svn/merge-cmd.c40
-rw-r--r--subversion/svn/notify.c2
-rw-r--r--subversion/svn/propset-cmd.c2
-rw-r--r--subversion/svn/resolve-cmd.c2
-rw-r--r--subversion/svn/revert-cmd.c3
-rw-r--r--subversion/svn/shelf-cmd.c1405
-rw-r--r--subversion/svn/shelf-cmd.h49
-rw-r--r--subversion/svn/shelf2-cmd.c1369
-rw-r--r--subversion/svn/shelf2-cmd.h49
-rw-r--r--subversion/svn/shelve-cmd.c369
-rw-r--r--subversion/svn/svn.c1163
-rw-r--r--subversion/svn/util.c2
22 files changed, 4477 insertions, 1115 deletions
diff --git a/subversion/svn/auth-cmd.c b/subversion/svn/auth-cmd.c
index 68ca067e736b7..b064b8a4d5252 100644
--- a/subversion/svn/auth-cmd.c
+++ b/subversion/svn/auth-cmd.c
@@ -455,12 +455,15 @@ svn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
if (b.patterns->nelts == 0)
SVN_ERR(svn_cmdline_printf(pool,
- _("Credentials cache in '%s' contains %d credentials\n"),
+ Q_("Credentials cache in '%s' contains %d credential\n",
+ "Credentials cache in '%s' contains %d credentials\n",
+ b.matches),
svn_dirent_local_style(config_path, pool), b.matches));
else
SVN_ERR(svn_cmdline_printf(pool,
- _("Credentials cache in '%s' contains %d matching "
- "credentials\n"),
+ Q_("Credentials cache in '%s' contains %d matching credential\n",
+ "Credentials cache in '%s' contains %d matching credentials\n",
+ b.matches),
svn_dirent_local_style(config_path, pool), b.matches));
}
@@ -474,9 +477,11 @@ svn_cl__auth(apr_getopt_t *os, void *baton, apr_pool_t *pool)
"no matching credentials"),
svn_dirent_local_style(config_path, pool));
else
- SVN_ERR(svn_cmdline_printf(pool, _("Deleted %d matching credentials "
- "from '%s'\n"), b.matches,
- svn_dirent_local_style(config_path, pool)));
+ SVN_ERR(svn_cmdline_printf(pool,
+ Q_("Deleted %d matching credential from '%s'\n",
+ "Deleted %d matching credentials from '%s'\n",
+ b.matches),
+ b.matches, svn_dirent_local_style(config_path, pool)));
}
return SVN_NO_ERROR;
diff --git a/subversion/svn/blame-cmd.c b/subversion/svn/blame-cmd.c
index 3911a64258f8f..8f955766b7c32 100644
--- a/subversion/svn/blame-cmd.c
+++ b/subversion/svn/blame-cmd.c
@@ -44,6 +44,7 @@ typedef struct blame_baton_t
svn_stream_t *out;
svn_stringbuf_t *sbuf;
+ svn_revnum_t start_revnum, end_revnum;
int rev_maxlength;
} blame_baton_t;
@@ -54,15 +55,13 @@ typedef struct blame_baton_t
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,
+ const svn_string_t *line,
svn_boolean_t local_change,
apr_pool_t *pool)
{
@@ -170,15 +169,13 @@ print_line_info(svn_stream_t *out,
/* 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,
+ const svn_string_t *line,
svn_boolean_t local_change,
apr_pool_t *pool)
{
@@ -188,19 +185,19 @@ blame_receiver(void *baton,
svn_boolean_t use_merged = FALSE;
if (!bb->rev_maxlength)
- {
- svn_revnum_t max_revnum = MAX(start_revnum, end_revnum);
- /* 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. */
-
- bb->rev_maxlength = 6;
- while (max_revnum >= 1000000)
- {
- bb->rev_maxlength++;
- max_revnum = max_revnum / 10;
- }
- }
+ {
+ svn_revnum_t max_revnum = MAX(bb->start_revnum, bb->end_revnum);
+ /* 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. */
+
+ bb->rev_maxlength = 6;
+ while (max_revnum >= 1000000)
+ {
+ bb->rev_maxlength++;
+ max_revnum = max_revnum / 10;
+ }
+ }
if (opt_state->use_merge_history)
{
@@ -237,7 +234,7 @@ blame_receiver(void *baton,
bb->rev_maxlength,
pool));
- return svn_stream_printf(out, pool, "%s%s", line, APR_EOL_STR);
+ return svn_stream_printf(out, pool, "%s%s", line->data, APR_EOL_STR);
}
@@ -333,7 +330,7 @@ svn_cl__blame(apr_getopt_t *os,
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_client_blame_receiver4_t receiver;
svn_pool_clear(subpool);
SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
@@ -368,7 +365,8 @@ svn_cl__blame(apr_getopt_t *os,
else
receiver = blame_receiver;
- err = svn_client_blame5(truepath,
+ err = svn_client_blame6(&bl.start_revnum, &bl.end_revnum,
+ truepath,
&peg_revision,
&opt_state->start_revision,
&opt_state->end_revision,
diff --git a/subversion/svn/cl.h b/subversion/svn/cl.h
index a5b1d9b948ae3..955e3ea8720c3 100644
--- a/subversion/svn/cl.h
+++ b/subversion/svn/cl.h
@@ -32,6 +32,7 @@
#include <apr_tables.h>
#include <apr_getopt.h>
+#include "svn_types.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_string.h"
@@ -126,6 +127,16 @@ typedef enum svn_cl__show_revs_t {
svn_cl__show_revs_t
svn_cl__show_revs_from_word(const char *word);
+
+/* Unit types for file size conversion. */
+typedef enum svn_cl__size_unit_t
+ {
+ SVN_CL__SIZE_UNIT_NONE = 0, /* Default, no conversion. */
+ SVN_CL__SIZE_UNIT_XML = -1, /* Conversion for XML output. */
+ SVN_CL__SIZE_UNIT_BASE_10 = 1000, /* Use base-10 SI units. */
+ SVN_CL__SIZE_UNIT_BASE_2 = 1024 /* Use base-2 SI units. */
+ } svn_cl__size_unit_t;
+
/*** Command dispatch. ***/
@@ -250,13 +261,20 @@ typedef struct svn_cl__opt_state_t
svn_boolean_t mergeinfo_log; /* show log message in mergeinfo command */
svn_boolean_t remove_unversioned;/* remove unversioned items */
svn_boolean_t remove_ignored; /* remove ignored items */
+ svn_boolean_t remove_added; /* reverting added item also removes it */
svn_boolean_t no_newline; /* do not output the trailing newline */
svn_boolean_t show_passwords; /* show cached passwords */
svn_boolean_t pin_externals; /* pin externals to last-changed revisions */
const char *show_item; /* print only the given item */
svn_boolean_t adds_as_modification; /* update 'add vs add' no tree conflict */
svn_boolean_t vacuum_pristines; /* remove unreferenced pristines */
- svn_boolean_t list;
+ svn_boolean_t drop; /* drop shelf after successful unshelve */
+ svn_cl__size_unit_t file_size_unit; /* file size format */
+ enum svn_cl__viewspec_t {
+ svn_cl__viewspec_unspecified = 0 /* default */,
+ svn_cl__viewspec_classic,
+ svn_cl__viewspec_svn11
+ } viewspec; /* value of --x-viewspec */
} svn_cl__opt_state_t;
/* Conflict stats for operations such as update and merge. */
@@ -270,6 +288,103 @@ typedef struct svn_cl__cmd_baton_t
} svn_cl__cmd_baton_t;
+/* 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_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
+ opt_auth_password_from_stdin,
+ opt_auth_username,
+ opt_autoprops,
+ opt_changelist,
+ opt_config_dir,
+ opt_config_options,
+ /* diff options */
+ opt_diff_cmd,
+ opt_internal_diff,
+ opt_no_diff_added,
+ opt_no_diff_deleted,
+ opt_show_copies_as_adds,
+ opt_notice_ancestry,
+ opt_summarize,
+ opt_use_git_diff_format,
+ opt_ignore_properties,
+ opt_properties_only,
+ opt_patch_compatible,
+ /* end of diff options */
+ 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_ignore,
+ opt_no_unlock,
+ opt_non_interactive,
+ opt_force_interactive,
+ opt_old_cmd,
+ opt_record_only,
+ opt_relocate,
+ opt_remove,
+ opt_revprop,
+ opt_stop_on_copy,
+ opt_strict, /* ### DEPRECATED */
+ 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_trust_server_cert_failures,
+ opt_strip,
+ opt_ignore_keywords,
+ opt_reverse_diff,
+ opt_ignore_whitespace,
+ opt_diff,
+ opt_allow_mixed_revisions,
+ opt_include_externals,
+ opt_show_inherited_props,
+ opt_search,
+ opt_search_and,
+ opt_mergeinfo_log,
+ opt_remove_unversioned,
+ opt_remove_ignored,
+ opt_remove_added,
+ opt_no_newline,
+ opt_show_passwords,
+ opt_pin_externals,
+ opt_show_item,
+ opt_adds_as_modification,
+ opt_vacuum_pristines,
+ opt_drop,
+ opt_viewspec,
+} svn_cl__longopt_t;
+
+/* 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
+
+
/* Declare all the command procedures */
svn_opt_subcommand_t
svn_cl__add,
@@ -304,9 +419,6 @@ svn_opt_subcommand_t
svn_cl__revert,
svn_cl__resolve,
svn_cl__resolved,
- svn_cl__shelve,
- svn_cl__unshelve,
- svn_cl__shelves,
svn_cl__status,
svn_cl__switch,
svn_cl__unlock,
@@ -315,7 +427,7 @@ svn_opt_subcommand_t
/* See definition in svn.c for documentation. */
-extern const svn_opt_subcommand_desc2_t svn_cl__cmd_table[];
+extern const svn_opt_subcommand_desc3_t *svn_cl__cmd_table;
/* See definition in svn.c for documentation. */
extern const int svn_cl__global_options[];
@@ -389,7 +501,7 @@ svn_error_t *
svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats,
apr_pool_t *scratch_pool);
-/*
+/*
* Interactively resolve the conflict a @a CONFLICT.
* TODO: more docs
*/
@@ -406,7 +518,7 @@ svn_cl__resolve_conflict(svn_boolean_t *quit,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
-/*
+/*
* Interactively resolve conflicts for all TARGETS.
* TODO: more docs
*/
@@ -711,6 +823,24 @@ svn_cl__node_kind_str_xml(svn_node_kind_t kind);
const char *
svn_cl__node_kind_str_human_readable(svn_node_kind_t kind);
+/* Set *RESULT to the size of a file, formatted according to BASE.
+ For base-10 and base-2 units, the size is constrained to at most
+ three significant digits.
+
+ If LONG_UNITS is TRUE, any unit suffixes will be the whole SI symbol,
+ e.g., KiB, MiB, etc; otherwise only the first letters will be used.
+
+ File sizes are never negative, so we don't handle that case other than
+ making sure that the scale adjustment will work.
+
+ The result will be allocated from RESULT_POOL. */
+svn_error_t *
+svn_cl__format_file_size(const char **result,
+ svn_filesize_t size,
+ svn_cl__size_unit_t base,
+ svn_boolean_t long_units,
+ apr_pool_t *result_pool);
+
/** Provides an XML name for a given OPERATION.
* Note: POOL is currently not used.
@@ -915,6 +1045,20 @@ svn_cl__similarity_check(const char *key,
apr_size_t token_count,
apr_pool_t *scratch_pool);
+/* Return in FUNC_P and BATON_P a callback that prints a summary diff,
+ * according to the options XML and IGNORE_PROPERTIES.
+ *
+ * ANCHOR is a URL or local path to be prefixed to the printed paths.
+ */
+svn_error_t *
+svn_cl__get_diff_summary_writer(svn_client_diff_summarize_func_t *func_p,
+ void **baton_p,
+ svn_boolean_t xml,
+ svn_boolean_t ignore_properties,
+ const char *anchor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/svn/conflict-callbacks.c b/subversion/svn/conflict-callbacks.c
index 278fffcdb8826..1c073e6b1c6a4 100644
--- a/subversion/svn/conflict-callbacks.c
+++ b/subversion/svn/conflict-callbacks.c
@@ -234,7 +234,7 @@ merge_prop_conflict(svn_stream_t *output,
my_propval = svn_string_create_empty(pool);
if (their_propval == NULL)
their_propval = svn_string_create_empty(pool);
-
+
options->ignore_eol_style = TRUE;
SVN_ERR(svn_diff_mem_string_diff3(&diff, base_propval,
merged_propval ?
@@ -361,7 +361,7 @@ edit_prop_conflict(const svn_string_t **merged_propval,
svn_stringbuf_t *buf;
SVN_ERR(svn_stringbuf_from_file2(&buf, file_path, scratch_pool));
- *merged_propval = svn_string_create_from_buf(buf, result_pool);
+ *merged_propval = svn_string_create_from_buf(buf, result_pool);
}
return SVN_NO_ERROR;
@@ -440,6 +440,17 @@ static const resolver_option_t builtin_resolver_options[] =
/* Options for local move vs incoming edit. */
{ "m", svn_client_conflict_option_local_move_file_text_merge },
+ { "m", svn_client_conflict_option_local_move_dir_merge },
+
+ /* Options for local missing vs incoming edit. */
+ { "m", svn_client_conflict_option_sibling_move_file_text_merge },
+ { "m", svn_client_conflict_option_sibling_move_dir_merge },
+
+ /* Options for incoming move vs local move. */
+ { "m", svn_client_conflict_option_both_moved_file_merge },
+ { "M", svn_client_conflict_option_both_moved_file_move_merge },
+ { "m", svn_client_conflict_option_both_moved_dir_merge },
+ { "M", svn_client_conflict_option_both_moved_dir_move_merge },
{ NULL }
};
@@ -889,7 +900,7 @@ handle_text_conflict(svn_boolean_t *resolved,
const char *their_abspath;
const char *merged_abspath = svn_client_conflict_get_local_abspath(conflict);
apr_array_header_t *text_conflict_options;
- svn_client_conflict_option_id_t option_id;
+ svn_client_conflict_option_id_t option_id;
option_id = svn_client_conflict_option_unspecified;
@@ -1529,17 +1540,16 @@ build_tree_conflict_options(
id != svn_client_conflict_option_accept_current_wc_state)
*all_options_are_dumb = FALSE;
- if (id == svn_client_conflict_option_incoming_move_file_text_merge ||
- id == svn_client_conflict_option_incoming_move_dir_merge)
- {
- SVN_ERR(
- svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
- possible_moved_to_repos_relpaths, builtin_option,
- result_pool, iterpool));
- SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
- possible_moved_to_abspaths, builtin_option,
- result_pool, iterpool));
- }
+ if (*possible_moved_to_repos_relpaths == NULL)
+ SVN_ERR(
+ svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+ possible_moved_to_repos_relpaths, builtin_option,
+ result_pool, iterpool));
+
+ if (*possible_moved_to_abspaths == NULL)
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates2(
+ possible_moved_to_abspaths, builtin_option,
+ result_pool, iterpool));
}
svn_pool_destroy(iterpool);
@@ -1549,11 +1559,11 @@ build_tree_conflict_options(
/* Add move target choice options only if there are multiple
* move targets to choose from. */
if (strcmp(o->code, "d") == 0 &&
- (*possible_moved_to_repos_relpaths == NULL ||
+ (*possible_moved_to_repos_relpaths == NULL ||
(*possible_moved_to_repos_relpaths)->nelts <= 1))
continue;
if (strcmp(o->code, "w") == 0 &&
- (*possible_moved_to_abspaths == NULL ||
+ (*possible_moved_to_abspaths == NULL ||
(*possible_moved_to_abspaths)->nelts <= 1))
continue;
@@ -1654,8 +1664,8 @@ prompt_move_target_path(int *preferred_move_target_idx,
{
char buf[1024];
- svn_cmdline_fprintf(stderr, iterpool, "%s\n",
- svn_err_best_message(err, buf, sizeof(buf)));
+ SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n",
+ svn_err_best_message(err, buf, sizeof(buf))));
svn_error_clear(err);
continue;
}
@@ -1670,6 +1680,69 @@ prompt_move_target_path(int *preferred_move_target_idx,
return SVN_NO_ERROR;
}
+static svn_error_t *
+find_conflict_option_with_repos_move_targets(
+ svn_client_conflict_option_t **option_with_move_targets,
+ apr_array_header_t *options,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int i;
+ apr_array_header_t *possible_moved_to_repos_relpaths = NULL;
+
+ *option_with_move_targets = NULL;
+
+ for (i = 0; i < options->nelts; i++)
+ {
+ svn_client_conflict_option_t *option;
+
+ svn_pool_clear(iterpool);
+ option = APR_ARRAY_IDX(options, i, svn_client_conflict_option_t *);
+ SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+ &possible_moved_to_repos_relpaths, option, iterpool, iterpool));
+ if (possible_moved_to_repos_relpaths)
+ {
+ *option_with_move_targets = option;
+ break;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+find_conflict_option_with_working_copy_move_targets(
+ svn_client_conflict_option_t **option_with_move_targets,
+ apr_array_header_t *options,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int i;
+ apr_array_header_t *possible_moved_to_abspaths = NULL;
+
+ *option_with_move_targets = NULL;
+
+ for (i = 0; i < options->nelts; i++)
+ {
+ svn_client_conflict_option_t *option;
+
+ svn_pool_clear(iterpool);
+ option = APR_ARRAY_IDX(options, i, svn_client_conflict_option_t *);
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates2(
+ &possible_moved_to_abspaths, option, scratch_pool,
+ iterpool));
+ if (possible_moved_to_abspaths)
+ {
+ *option_with_move_targets = option;
+ break;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
/* Ask the user what to do about the tree conflict described by CONFLICT
* and either resolve the conflict accordingly or postpone resolution.
* SCRATCH_POOL is used for temporary allocations. */
@@ -1797,7 +1870,7 @@ handle_tree_conflict(svn_boolean_t *resolved,
{
int preferred_move_target_idx;
apr_array_header_t *options;
- svn_client_conflict_option_t *conflict_option;
+ svn_client_conflict_option_t *option;
SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
possible_moved_to_repos_relpaths,
@@ -1810,22 +1883,12 @@ handle_tree_conflict(svn_boolean_t *resolved,
ctx,
iterpool,
iterpool));
- conflict_option =
- svn_client_conflict_option_find_by_id(
- options,
- svn_client_conflict_option_incoming_move_file_text_merge);
- if (conflict_option == NULL)
+ SVN_ERR(find_conflict_option_with_repos_move_targets(
+ &option, options, iterpool));
+ if (option)
{
- conflict_option =
- svn_client_conflict_option_find_by_id(
- options, svn_client_conflict_option_incoming_move_dir_merge);
- }
-
- if (conflict_option)
- {
- SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(
- conflict_option, preferred_move_target_idx,
- ctx, iterpool));
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath2(
+ option, preferred_move_target_idx, ctx, iterpool));
repos_move_target_chosen = TRUE;
wc_move_target_chosen = FALSE;
@@ -1851,7 +1914,7 @@ handle_tree_conflict(svn_boolean_t *resolved,
{
int preferred_move_target_idx;
apr_array_header_t *options;
- svn_client_conflict_option_t *conflict_option;
+ svn_client_conflict_option_t *option;
SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
possible_moved_to_abspaths, TRUE,
@@ -1863,22 +1926,12 @@ handle_tree_conflict(svn_boolean_t *resolved,
ctx,
iterpool,
iterpool));
- conflict_option =
- svn_client_conflict_option_find_by_id(
- options,
- svn_client_conflict_option_incoming_move_file_text_merge);
- if (conflict_option == NULL)
- {
- conflict_option =
- svn_client_conflict_option_find_by_id(
- options, svn_client_conflict_option_incoming_move_dir_merge);
- }
-
- if (conflict_option)
+ SVN_ERR(find_conflict_option_with_working_copy_move_targets(
+ &option, options, iterpool));
+ if (option)
{
- SVN_ERR(svn_client_conflict_option_set_moved_to_abspath(
- conflict_option, preferred_move_target_idx, ctx,
- iterpool));
+ SVN_ERR(svn_client_conflict_option_set_moved_to_abspath2(
+ option, preferred_move_target_idx, ctx, iterpool));
wc_move_target_chosen = TRUE;
/* Update option description. */
@@ -1930,7 +1983,6 @@ resolve_conflict_interactively(svn_boolean_t *resolved,
svn_cmdline_prompt_baton_t *pb,
svn_cl__conflict_stats_t *conflict_stats,
svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t text_conflicted;
@@ -1964,7 +2016,7 @@ resolve_conflict_interactively(svn_boolean_t *resolved,
if (props_conflicted->nelts > 0)
SVN_ERR(handle_prop_conflicts(resolved, postponed, quit, &merged_propval,
path_prefix, pb, editor_cmd, config, conflict,
- conflict_stats, ctx, result_pool, scratch_pool));
+ conflict_stats, ctx, scratch_pool, scratch_pool));
if (tree_conflicted)
SVN_ERR(handle_tree_conflict(resolved, postponed, quit, printed_description,
conflict, path_prefix, pb, conflict_stats, ctx,
@@ -2201,11 +2253,14 @@ svn_cl__resolve_conflict(svn_boolean_t *quit,
svn_boolean_t postponed = FALSE;
svn_boolean_t printed_description = FALSE;
svn_error_t *err;
+ apr_pool_t *iterpool;
*quit = FALSE;
+ iterpool = svn_pool_create(scratch_pool);
while (!resolved && !postponed && !*quit)
{
+ svn_pool_clear(iterpool);
err = resolve_conflict_interactively(&resolved, &postponed, quit,
external_failed,
printed_summary,
@@ -2214,7 +2269,7 @@ svn_cl__resolve_conflict(svn_boolean_t *quit,
editor_cmd, ctx->config,
path_prefix, pb,
conflict_stats, ctx,
- scratch_pool, scratch_pool);
+ iterpool);
if (err && err->apr_err == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
{
/* Conflict resolution has failed. Let the user try again.
@@ -2226,6 +2281,7 @@ svn_cl__resolve_conflict(svn_boolean_t *quit,
}
SVN_ERR(err);
}
+ svn_pool_destroy(iterpool);
}
else if (option_id != svn_client_conflict_option_postpone)
SVN_ERR(mark_conflict_resolved(conflict, option_id,
diff --git a/subversion/svn/diff-cmd.c b/subversion/svn/diff-cmd.c
index 9e389ec856a69..b5f3702033f05 100644
--- a/subversion/svn/diff-cmd.c
+++ b/subversion/svn/diff-cmd.c
@@ -181,6 +181,24 @@ summarize_regular(const svn_client_diff_summarize_t *summary,
return svn_cmdline_fflush(stdout);
}
+svn_error_t *
+svn_cl__get_diff_summary_writer(svn_client_diff_summarize_func_t *func_p,
+ void **baton_p,
+ svn_boolean_t xml,
+ svn_boolean_t ignore_properties,
+ const char *anchor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct summarize_baton_t *b = apr_pcalloc(result_pool, sizeof(*b));
+
+ b->anchor = anchor;
+ b->ignore_properties = ignore_properties;
+ *func_p = xml ? summarize_xml : summarize_regular;
+ *baton_p = b;
+ return SVN_NO_ERROR;
+}
+
/* An svn_opt_subcommand_t to handle the 'diff' command.
This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
@@ -203,9 +221,6 @@ svn_cl__diff(apr_getopt_t *os,
svn_boolean_t ignore_properties =
opt_state->diff.patch_compatible || opt_state->diff.ignore_properties;
int i;
- struct summarize_baton_t summarize_baton;
- 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);
@@ -448,9 +463,13 @@ svn_cl__diff(apr_getopt_t *os,
if (opt_state->diff.summarize)
{
- summarize_baton.anchor = target1;
- summarize_baton.ignore_properties = ignore_properties;
+ svn_client_diff_summarize_func_t summarize_func;
+ void *summarize_baton;
+ SVN_ERR(svn_cl__get_diff_summary_writer(
+ &summarize_func, &summarize_baton,
+ opt_state->xml, ignore_properties, target1,
+ iterpool, iterpool));
SVN_ERR(svn_client_diff_summarize2(
target1,
&opt_state->start_revision,
@@ -459,11 +478,11 @@ svn_cl__diff(apr_getopt_t *os,
opt_state->depth,
! opt_state->diff.notice_ancestry,
opt_state->changelists,
- summarize_func, &summarize_baton,
+ summarize_func, summarize_baton,
ctx, iterpool));
}
else
- SVN_ERR(svn_client_diff6(
+ SVN_ERR(svn_client_diff7(
options,
target1,
&(opt_state->start_revision),
@@ -479,6 +498,7 @@ svn_cl__diff(apr_getopt_t *os,
ignore_properties,
opt_state->diff.properties_only,
opt_state->diff.use_git_diff_format,
+ TRUE /*pretty_print_mergeinfo*/,
svn_cmdline_output_encoding(pool),
outstream,
errstream,
@@ -501,8 +521,13 @@ svn_cl__diff(apr_getopt_t *os,
if (opt_state->diff.summarize)
{
- summarize_baton.anchor = truepath;
- summarize_baton.ignore_properties = ignore_properties;
+ svn_client_diff_summarize_func_t summarize_func;
+ void *summarize_baton;
+
+ SVN_ERR(svn_cl__get_diff_summary_writer(
+ &summarize_func, &summarize_baton,
+ opt_state->xml, ignore_properties, truepath,
+ iterpool, iterpool));
SVN_ERR(svn_client_diff_summarize_peg2(
truepath,
&peg_revision,
@@ -511,11 +536,11 @@ svn_cl__diff(apr_getopt_t *os,
opt_state->depth,
! opt_state->diff.notice_ancestry,
opt_state->changelists,
- summarize_func, &summarize_baton,
+ summarize_func, summarize_baton,
ctx, iterpool));
}
else
- SVN_ERR(svn_client_diff_peg6(
+ SVN_ERR(svn_client_diff_peg7(
options,
truepath,
&peg_revision,
@@ -531,6 +556,7 @@ svn_cl__diff(apr_getopt_t *os,
ignore_properties,
opt_state->diff.properties_only,
opt_state->diff.use_git_diff_format,
+ TRUE /*pretty_print_mergeinfo*/,
svn_cmdline_output_encoding(pool),
outstream,
errstream,
diff --git a/subversion/svn/filesize.c b/subversion/svn/filesize.c
new file mode 100644
index 0000000000000..ba1c35626b611
--- /dev/null
+++ b/subversion/svn/filesize.c
@@ -0,0 +1,221 @@
+/*
+ * filesize.c -- Utilities for displaying file sizes
+ *
+ * ====================================================================
+ * 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 <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <apr_strings.h>
+
+#include "cl.h"
+
+
+/*** Code. ***/
+
+/* The structure that describes the units and their magnitudes. */
+typedef struct filesize_order_t
+{
+ svn_filesize_t mask;
+ const char *suffix;
+ const char *short_suffix;
+} filesize_order_t;
+
+
+/* Get the index of the order of magnitude of the given SIZE.
+ The returned index will be within [0 .. order_size - 1]. */
+static apr_size_t
+get_order_index(svn_filesize_t abs_size,
+ const filesize_order_t *order,
+ apr_size_t order_size)
+{
+ /* It would be sexy to do a binary search here, but with only 7 elements
+ in the arrays ... we should ### FIXME: do the binary search anyway. */
+ apr_size_t index = order_size;
+ while (index > 0)
+ {
+ --index;
+ if (abs_size > order[index].mask)
+ break;
+ }
+ return index;
+}
+
+
+/* Format the adjusted size with the given units. */
+static const char *
+format_size(double human_readable_size,
+ svn_boolean_t long_units,
+ const filesize_order_t *order,
+ apr_size_t index,
+ apr_pool_t *result_pool)
+{
+ /* NOTE: We want to display a locale-specific decimal sepratator, but
+ APR's formatter completely ignores the locale. So we use the
+ good, old, standard, *dangerous* sprintf() to format the size.
+
+ But, on the bright side, we require that the number has no more
+ than 3 non-fractional digits. So the call to sprintf() here
+ should be safe. */
+ const double absolute_human_readable_size = fabs(human_readable_size);
+ const char *const suffix = (long_units ? order[index].suffix
+ : order[index].short_suffix);
+
+ /* 3 digits (or 2 digits and 1 decimal separator)
+ + 1 negative sign (which should not appear under normal circumstances)
+ + 1 nul terminator
+ ---
+ = 5 characters of space needed in the buffer. */
+ char buffer[8];
+
+ assert(absolute_human_readable_size < 1000.0);
+
+ /* When the adjusted size has only one significant digit left of the
+ decimal point, show tenths of a unit, too. */
+ sprintf(buffer, "%.*f",
+ absolute_human_readable_size < 10.0 ? 1 : 0,
+ human_readable_size);
+ return apr_pstrcat(result_pool, buffer, suffix, SVN_VA_NULL);
+}
+
+
+static const char *
+get_base2_unit_file_size(svn_filesize_t size,
+ svn_boolean_t long_units,
+ apr_pool_t *result_pool)
+{
+ static const filesize_order_t order[] =
+ {
+ {APR_INT64_C(0x0000000000000000), " B", "B"}, /* byte */
+ {APR_INT64_C(0x00000000000003FF), " KiB", "K"}, /* kibi */
+ {APR_INT64_C(0x00000000000FFFFF), " MiB", "M"}, /* mibi */
+ {APR_INT64_C(0x000000003FFFFFFF), " GiB", "G"}, /* gibi */
+ {APR_INT64_C(0x000000FFFFFFFFFF), " TiB", "T"}, /* tibi */
+ {APR_INT64_C(0x0003FFFFFFFFFFFF), " EiB", "E"}, /* exbi */
+ {APR_INT64_C(0x0FFFFFFFFFFFFFFF), " PiB", "P"} /* pibi */
+ };
+ static const apr_size_t order_size = sizeof(order) / sizeof(order[0]);
+
+ const svn_filesize_t abs_size = ((size < 0) ? -size : size);
+ apr_size_t index = get_order_index(abs_size, order, order_size);
+ double human_readable_size;
+
+ /* Adjust the size to the given order of magnitude.
+
+ This is division by (order[index].mask + 1), which is the base-2^10
+ magnitude of the size; and that is the same as an arithmetic right
+ shift by (index * 10) bits. But we split it into an integer and a
+ floating-point division, so that we don't overflow the mantissa at
+ very large file sizes. */
+ if ((abs_size >> 10 * index) > 999)
+ {
+ /* This assertion should never fail, because we only have 4 binary
+ digits in the petabyte (all right, "pibibyte") range and so the
+ number of petabytes can't be large enough to cause the program
+ flow to enter this conditional block. */
+ assert(index < order_size - 1);
+ ++index;
+ }
+ human_readable_size = (index == 0 ? (double)size
+ : (size >> 3 * index) / 128.0 / index);
+
+ return format_size(human_readable_size,
+ long_units, order, index, result_pool);
+}
+
+
+static const char *
+get_base10_unit_file_size(svn_filesize_t size,
+ svn_boolean_t long_units,
+ apr_pool_t *result_pool)
+{
+ static const filesize_order_t order[] =
+ {
+ {APR_INT64_C( 0), " B", "B"}, /* byte */
+ {APR_INT64_C( 999), " kB", "k"}, /* kilo */
+ {APR_INT64_C( 999999), " MB", "M"}, /* mega */
+ {APR_INT64_C( 999999999), " GB", "G"}, /* giga */
+ {APR_INT64_C( 999999999999), " TB", "T"}, /* tera */
+ {APR_INT64_C( 999999999999999), " EB", "E"}, /* exa */
+ {APR_INT64_C(999999999999999999), " PB", "P"} /* peta */
+ /* 18446744073709551615 is the maximum value. */
+ };
+ static const apr_size_t order_size = sizeof(order) / sizeof(order[0]);
+
+ const svn_filesize_t abs_size = ((size < 0) ? -size : size);
+ apr_size_t index = get_order_index(abs_size, order, order_size);
+ double human_readable_size;
+
+ /* Adjust the size to the given order of magnitude.
+
+ This is division by (order[index].mask + 1), which is the base-1000
+ magnitude of the size. For large file sizes, we split the operation
+ into an integer and a floating-point division, so that we don't
+ overflow the mantissa. */
+ if (index == 0)
+ human_readable_size = (double)size;
+ else if (index <= 3)
+ human_readable_size = (double)size / (order[index].mask + 1);
+ else
+ {
+ /* [ Keep integer division here! ] */
+ const double divisor = (double)((order[index].mask + 1) / 1000000);
+ human_readable_size = (size / 1000000) / divisor;
+ /* [ And here! ] */
+ }
+
+ return format_size(human_readable_size,
+ long_units, order, index, result_pool);
+}
+
+
+svn_error_t *
+svn_cl__format_file_size(const char **result,
+ svn_filesize_t size,
+ svn_cl__size_unit_t base,
+ svn_boolean_t long_units,
+ apr_pool_t *result_pool)
+{
+ switch (base)
+ {
+ case SVN_CL__SIZE_UNIT_NONE:
+ case SVN_CL__SIZE_UNIT_XML:
+ *result = apr_psprintf(result_pool, "%" SVN_FILESIZE_T_FMT, size);
+ break;
+
+ case SVN_CL__SIZE_UNIT_BASE_2:
+ *result = get_base2_unit_file_size(size, long_units, result_pool);
+ break;
+
+ case SVN_CL__SIZE_UNIT_BASE_10:
+ *result = get_base10_unit_file_size(size, long_units, result_pool);
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/help-cmd.c b/subversion/svn/help-cmd.c
index 3d5375c65a3e8..f812784e77da2 100644
--- a/subversion/svn/help-cmd.c
+++ b/subversion/svn/help-cmd.c
@@ -179,7 +179,7 @@ svn_cl__help(apr_getopt_t *os,
svn_stringbuf_appendcstr(version_footer, "* KWallet (KDE)\n");
#endif
- return svn_opt_print_help4(os,
+ return svn_opt_print_help5(os,
"svn", /* ### erm, derive somehow? */
opt_state ? opt_state->version : FALSE,
opt_state ? opt_state->quiet : FALSE,
diff --git a/subversion/svn/info-cmd.c b/subversion/svn/info-cmd.c
index e0c0041b468a0..44f830f34e5d4 100644
--- a/subversion/svn/info-cmd.c
+++ b/subversion/svn/info-cmd.c
@@ -21,6 +21,10 @@
* ====================================================================
*/
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
+
/* ==================================================================== */
@@ -45,6 +49,254 @@
/*** Code. ***/
+struct layout_list_baton_t
+{
+ svn_boolean_t checkout;
+ const char *target;
+ const char *target_abspath;
+ svn_boolean_t with_revs;
+ int vs_py_format;
+};
+
+/* Output as 'svn' command-line commands.
+ *
+ * Implements svn_client__layout_func_t
+ */
+static svn_error_t *
+output_svn_command_line(void *layout_baton,
+ const char *local_abspath,
+ const char *repos_root_url,
+ svn_boolean_t not_present,
+ svn_boolean_t url_changed,
+ const char *url,
+ svn_boolean_t revision_changed,
+ svn_revnum_t revision,
+ svn_boolean_t depth_changed,
+ svn_depth_t depth,
+ apr_pool_t *scratch_pool)
+{
+ struct layout_list_baton_t *llb = layout_baton;
+ const char *relpath = svn_dirent_skip_ancestor(llb->target_abspath,
+ local_abspath);
+ const char *cmd;
+ const char *depth_str;
+ const char *url_rev_str;
+
+ depth_str = (depth_changed
+ ? apr_psprintf(scratch_pool, " --set-depth=%s",
+ svn_depth_to_word(depth))
+ : "");
+
+ if (llb->checkout)
+ {
+ cmd = "svn checkout";
+ if (depth != svn_depth_infinity)
+ depth_str = apr_psprintf(scratch_pool,
+ " --depth=%s", svn_depth_to_word(depth));
+ url_rev_str = apr_psprintf(scratch_pool, " %s", url);
+ if (llb->with_revs)
+ url_rev_str = apr_psprintf(scratch_pool, "%s@%ld",
+ url_rev_str, revision);
+ llb->checkout = FALSE;
+ }
+ else if (not_present)
+ {
+ /* Easiest way to create a not present node: update to r0 */
+ cmd = "svn update";
+ url_rev_str = " -r0";
+ }
+ else if (url_changed)
+ {
+ cmd = "svn switch";
+ url_rev_str = apr_psprintf(scratch_pool, " ^/%s",
+ svn_uri_skip_ancestor(repos_root_url,
+ url, scratch_pool));
+ if (llb->with_revs)
+ url_rev_str = apr_psprintf(scratch_pool, "%s@%ld",
+ url_rev_str, revision);
+ }
+ else if (llb->with_revs && revision_changed)
+ {
+ cmd = "svn update";
+ url_rev_str = apr_psprintf(scratch_pool, " -r%ld", revision);
+ }
+ else if (depth_changed)
+ {
+ cmd = "svn update";
+ url_rev_str = "";
+ }
+ else
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "%s%-23s%-10s %s\n",
+ cmd, depth_str, url_rev_str,
+ svn_dirent_local_style(
+ svn_dirent_join(llb->target, relpath,
+ scratch_pool), scratch_pool)));
+
+ return SVN_NO_ERROR;
+}
+
+/* */
+static const char *
+depth_to_viewspec_py(svn_depth_t depth,
+ apr_pool_t *result_pool)
+{
+ switch (depth)
+ {
+ case svn_depth_infinity:
+ return "/**";
+ case svn_depth_immediates:
+ return "/*";
+ case svn_depth_files:
+ return "/~";
+ case svn_depth_empty:
+ return "";
+ case svn_depth_exclude:
+ return "!";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/* Output in the format used by 'tools/client-side/viewspec.py'
+ *
+ * Implements svn_client__layout_func_t
+ */
+static svn_error_t *
+output_svn_viewspec_py(void *layout_baton,
+ const char *local_abspath,
+ const char *repos_root_url,
+ svn_boolean_t not_present,
+ svn_boolean_t url_changed,
+ const char *url,
+ svn_boolean_t revision_changed,
+ svn_revnum_t revision,
+ svn_boolean_t depth_changed,
+ svn_depth_t depth,
+ apr_pool_t *scratch_pool)
+{
+ struct layout_list_baton_t *llb = layout_baton;
+ const char *relpath = svn_dirent_skip_ancestor(llb->target_abspath,
+ local_abspath);
+ const char *depth_str;
+ const char *rev_str = "";
+ const char *repos_rel_url = "";
+
+ depth_str = ((depth_changed || llb->checkout)
+ ? depth_to_viewspec_py(depth, scratch_pool)
+ : "");
+ if (! llb->with_revs)
+ revision_changed = FALSE;
+ if (revision_changed)
+ rev_str = apr_psprintf(scratch_pool, "@%ld", revision);
+
+ if (llb->checkout)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "Format: %d\n"
+ "Url: %s\n",
+ llb->vs_py_format, url));
+ if (llb->with_revs)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "Revision: %ld\n",
+ revision));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
+ llb->checkout = FALSE;
+
+ if (depth == svn_depth_empty)
+ return SVN_NO_ERROR;
+ if (depth_str[0] == '/')
+ depth_str++;
+ }
+ else if (not_present)
+ {
+ /* Easiest way to create a not present node: update to r0 */
+ if (llb->vs_py_format < 2)
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("svn-viewspec.py format 1 does not support "
+ "the 'not-present' state found at '%s'"),
+ relpath);
+ rev_str = "@0";
+ }
+ else if (url_changed)
+ {
+ if (llb->vs_py_format < 2)
+ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("svn-viewspec.py format 1 does not support "
+ "the 'switched' state found at '%s'"),
+ relpath);
+ repos_rel_url = svn_uri_skip_ancestor(repos_root_url, url,
+ scratch_pool);
+ repos_rel_url = apr_psprintf(scratch_pool, "^/%s", repos_rel_url);
+ }
+ else if (!(revision_changed || depth_changed))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "%s%s %s%s\n",
+ relpath, depth_str, repos_rel_url, rev_str));
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * Call svn_client__layout_list(), using a receiver function decided
+ * by VIEWSPEC.
+ */
+static svn_error_t *
+cl_layout_list(apr_array_header_t *targets,
+ enum svn_cl__viewspec_t viewspec,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *list_path, *list_abspath;
+ struct layout_list_baton_t llb;
+
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ if (targets->nelts > 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ list_path = APR_ARRAY_IDX(targets, 0, const char *);
+
+ SVN_ERR(svn_cl__check_target_is_local_path(list_path));
+
+ SVN_ERR(svn_dirent_get_absolute(&list_abspath, list_path,
+ scratch_pool));
+
+ llb.checkout = TRUE;
+ llb.target = list_path;
+ llb.target_abspath = list_abspath;
+ llb.with_revs = TRUE;
+
+ switch (viewspec)
+ {
+ case svn_cl__viewspec_classic:
+ /* svn-viewspec.py format */
+ llb.vs_py_format = 2;
+
+ SVN_ERR(svn_client__layout_list(list_abspath,
+ output_svn_viewspec_py, &llb,
+ ctx, scratch_pool));
+ break;
+ case svn_cl__viewspec_svn11:
+ /* svn command-line format */
+ SVN_ERR(svn_client__layout_list(list_abspath,
+ output_svn_command_line, &llb,
+ ctx, scratch_pool));
+ break;
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
svn_cl__info_print_time(apr_time_t atime,
const char *desc,
@@ -100,6 +352,7 @@ typedef enum
info_item_relative_url,
info_item_repos_root_url,
info_item_repos_uuid,
+ info_item_repos_size,
/* Working copy revision or repository HEAD revision */
info_item_revision,
@@ -110,7 +363,10 @@ typedef enum
info_item_last_changed_author,
/* Working copy information */
- info_item_wc_root
+ info_item_wc_root,
+ info_item_schedule,
+ info_item_depth,
+ info_item_changelist
} info_item_t;
/* Mapping between option keywords and info_item_t. */
@@ -128,12 +384,16 @@ static const info_item_map_t info_item_map[] =
{ MAKE_STRING("relative-url"), info_item_relative_url },
{ MAKE_STRING("repos-root-url"), info_item_repos_root_url },
{ MAKE_STRING("repos-uuid"), info_item_repos_uuid },
+ { MAKE_STRING("repos-size"), info_item_repos_size },
{ MAKE_STRING("revision"), info_item_revision },
{ MAKE_STRING("last-changed-revision"),
info_item_last_changed_rev },
{ MAKE_STRING("last-changed-date"), info_item_last_changed_date },
{ MAKE_STRING("last-changed-author"), info_item_last_changed_author },
- { MAKE_STRING("wc-root"), info_item_wc_root }
+ { MAKE_STRING("wc-root"), info_item_wc_root },
+ { MAKE_STRING("schedule"), info_item_schedule },
+ { MAKE_STRING("depth"), info_item_depth },
+ { MAKE_STRING("changelist"), info_item_changelist },
};
#undef MAKE_STRING
@@ -163,6 +423,9 @@ typedef struct print_info_baton_t
/* Did we already print a line of output? */
svn_boolean_t start_new_line;
+ /* Format for file sizes */
+ svn_cl__size_unit_t file_size_unit;
+
/* The client context. */
svn_client_ctx_t *ctx;
} print_info_baton_t;
@@ -251,21 +514,40 @@ print_info_xml(void *baton,
apr_pool_t *pool)
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
- const char *rev_str;
print_info_baton_t *const receiver_baton = 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."));
+ const char *const path_str =
+ svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix, target, pool);
+ const char *const kind_str = svn_cl__node_kind_str_xml(info->kind);
+ const char *const rev_str =
+ (SVN_IS_VALID_REVNUM(info->rev)
+ ? apr_psprintf(pool, "%ld", info->rev)
+ : 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(
- receiver_baton->path_prefix, target, pool),
- "kind", svn_cl__node_kind_str_xml(info->kind),
- "revision", rev_str,
- SVN_VA_NULL);
+ if (info->kind == svn_node_file && info->size != SVN_INVALID_FILESIZE)
+ {
+ const char *size_str;
+ SVN_ERR(svn_cl__format_file_size(&size_str, info->size,
+ SVN_CL__SIZE_UNIT_XML,
+ FALSE, pool));
+
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "path", path_str,
+ "kind", kind_str,
+ "revision", rev_str,
+ "size", size_str,
+ SVN_VA_NULL);
+ }
+ else
+ {
+ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
+ "path", path_str,
+ "kind", kind_str,
+ "revision", rev_str,
+ SVN_VA_NULL);
+ }
/* "<url> xx </url>" */
svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL);
@@ -487,6 +769,16 @@ print_info(void *baton,
break;
}
+ if (info->kind == svn_node_file && info->size != SVN_INVALID_FILESIZE)
+ {
+ const char *sizestr;
+ SVN_ERR(svn_cl__format_file_size(&sizestr, info->size,
+ receiver_baton->file_size_unit,
+ TRUE, pool));
+ SVN_ERR(svn_cmdline_printf(pool, _("Size in Repository: %s\n"),
+ sizestr));
+ }
+
if (info->wc_info)
{
switch (info->wc_info->schedule)
@@ -827,11 +1119,12 @@ print_info_item(void *baton,
apr_pool_t *pool)
{
print_info_baton_t *const receiver_baton = baton;
+ const char *const actual_target_path =
+ (!receiver_baton->target_is_path ? info->URL
+ : svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix, target, pool));
const char *const target_path =
- (!receiver_baton->multiple_targets ? NULL
- : (!receiver_baton->target_is_path ? info->URL
- : svn_cl__local_style_skip_ancestor(
- receiver_baton->path_prefix, target, pool)));
+ (receiver_baton->multiple_targets ? actual_target_path : NULL);
if (receiver_baton->start_new_line)
SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
@@ -860,6 +1153,36 @@ print_info_item(void *baton,
SVN_ERR(print_info_item_string(info->repos_UUID, target_path, pool));
break;
+ case info_item_repos_size:
+ if (info->kind != svn_node_file)
+ {
+ receiver_baton->start_new_line = FALSE;
+ return SVN_NO_ERROR;
+ }
+
+ if (info->size == SVN_INVALID_FILESIZE)
+ {
+ if (receiver_baton->multiple_targets)
+ {
+ receiver_baton->start_new_line = FALSE;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_createf(
+ SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+ _("can't show in-repository size of working copy file '%s'"),
+ actual_target_path);
+ }
+
+ {
+ const char *sizestr;
+ SVN_ERR(svn_cl__format_file_size(&sizestr, info->size,
+ receiver_baton->file_size_unit,
+ TRUE, pool));
+ SVN_ERR(print_info_item_string(sizestr, target_path, pool));
+ }
+ break;
+
case info_item_revision:
SVN_ERR(print_info_item_revision(info->rev, target_path, pool));
break;
@@ -888,6 +1211,27 @@ print_info_item(void *baton,
target_path, pool));
break;
+ case info_item_schedule:
+ SVN_ERR(print_info_item_string(
+ (info->wc_info
+ ? schedule_str(info->wc_info->schedule) : NULL),
+ target_path, pool));
+ break;
+
+ case info_item_depth:
+ SVN_ERR(print_info_item_string(
+ ((info->wc_info && info->kind == svn_node_dir)
+ ? svn_depth_to_word(info->wc_info->depth) : NULL),
+ target_path, pool));
+ break;
+
+ case info_item_changelist:
+ SVN_ERR(print_info_item_string(
+ ((info->wc_info && info->wc_info->changelist)
+ ? info->wc_info->changelist : NULL),
+ target_path, pool));
+ break;
+
default:
SVN_ERR_MALFUNCTION();
}
@@ -918,10 +1262,17 @@ svn_cl__info(apr_getopt_t *os,
opt_state->targets,
ctx, FALSE, pool));
+ if (opt_state->viewspec)
+ {
+ SVN_ERR(cl_layout_list(targets, opt_state->viewspec, baton, ctx, pool));
+ return SVN_NO_ERROR;
+ }
+
/* Add "." if user passed 0 arguments. */
svn_opt_push_implicit_dot_target(targets, pool);
receiver_baton.ctx = ctx;
+ receiver_baton.file_size_unit = opt_state->file_size_unit;
if (opt_state->xml)
{
@@ -935,6 +1286,10 @@ svn_cl__info(apr_getopt_t *os,
return svn_error_create(
SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--no-newline is not valid in --xml mode"));
+ if (opt_state->file_size_unit != SVN_CL__SIZE_UNIT_NONE)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--human-readable is not valid 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
diff --git a/subversion/svn/list-cmd.c b/subversion/svn/list-cmd.c
index da18252243d90..121b08f94b6f2 100644
--- a/subversion/svn/list-cmd.c
+++ b/subversion/svn/list-cmd.c
@@ -40,8 +40,13 @@
/* Baton used when printing directory entries. */
struct print_baton {
- svn_boolean_t verbose;
svn_client_ctx_t *ctx;
+ svn_boolean_t verbose;
+ svn_cl__size_unit_t file_size_unit;
+
+ /* Keep track of the width of the author field. */
+ int author_width;
+ int max_author_width;
/* To keep track of last seen external information. */
const char *last_external_parent_url;
@@ -49,12 +54,23 @@ struct print_baton {
svn_boolean_t in_external;
};
+/* Starting and maximum width of the author field */
+static const int initial_author_width = 8;
+static const int initial_human_readable_author_width = 14;
+static const int maximum_author_width = 16;
+static const int maximum_human_readable_author_width = 22;
+
+/* Width of the size field */
+static const int normal_size_width = 10;
+static const int human_readable_size_width = 4;
+
/* Field flags required for this function */
static const apr_uint32_t print_dirent_fields = SVN_DIRENT_KIND;
static const apr_uint32_t print_dirent_fields_verbose = (
SVN_DIRENT_KIND | SVN_DIRENT_SIZE | SVN_DIRENT_TIME |
SVN_DIRENT_CREATED_REV | SVN_DIRENT_LAST_AUTHOR);
+
/* This implements the svn_client_list_func2_t API, printing a single
directory entry in text format. */
static svn_error_t *
@@ -121,7 +137,11 @@ print_dirent(void *baton,
apr_status_t apr_err;
apr_size_t size;
char timestr[20];
- const char *sizestr, *utf8_timestr;
+ const int sizewidth = (pb->file_size_unit == SVN_CL__SIZE_UNIT_NONE
+ ? normal_size_width
+ : human_readable_size_width);
+ const char *sizestr = "";
+ const char *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
@@ -146,15 +166,33 @@ print_dirent(void *baton,
/* we need it in UTF-8. */
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_timestr, timestr, scratch_pool));
- sizestr = apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT,
- dirent->size);
+ /* We may have to adjust the width of th 'author' field. */
+ if (dirent->last_author)
+ {
+ const int author_width = (int)strlen(dirent->last_author);
+ if (author_width > pb->author_width)
+ {
+ if (author_width < pb->max_author_width)
+ pb->author_width = author_width;
+ else
+ pb->author_width = pb->max_author_width;
+ }
+ }
+
+ if (dirent->kind == svn_node_file)
+ {
+ SVN_ERR(svn_cl__format_file_size(&sizestr, dirent->size,
+ pb->file_size_unit,
+ FALSE, scratch_pool));
+ }
return svn_cmdline_printf
- (scratch_pool, "%7ld %-8.8s %c %10s %12s %s%s\n",
+ (scratch_pool, "%7ld %-*.*s %c %*s %12s %s%s\n",
dirent->created_rev,
+ pb->author_width, pb->author_width,
dirent->last_author ? dirent->last_author : " ? ",
lock ? 'O' : ' ',
- (dirent->kind == svn_node_file) ? sizestr : "",
+ sizewidth, sizestr,
utf8_timestr,
entryname,
(dirent->kind == svn_node_dir) ? "/" : "");
@@ -238,9 +276,11 @@ print_dirent_xml(void *baton,
if (dirent->kind == svn_node_file)
{
- svn_cl__xml_tagged_cdata
- (&sb, scratch_pool, "size",
- apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT, dirent->size));
+ const char *sizestr;
+ SVN_ERR(svn_cl__format_file_size(&sizestr, dirent->size,
+ SVN_CL__SIZE_UNIT_XML,
+ FALSE, scratch_pool));
+ svn_cl__xml_tagged_cdata(&sb, scratch_pool, "size", sizestr);
}
svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal, "commit",
@@ -303,11 +343,17 @@ svn_cl__list(apr_getopt_t *os,
if (opt_state->xml)
{
- /* The XML output contains all the information, so "--verbose"
- does not apply. */
+ /* The XML output contains all the information, so "--verbose" does
+ not apply, and using "--human-readable" with machine-readable
+ output does not make sense. */
if (opt_state->verbose)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("'verbose' option invalid in XML mode"));
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--verbose is not valid in --xml mode"));
+ if (opt_state->file_size_unit != SVN_CL__SIZE_UNIT_NONE)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--human-readable is not valid 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
@@ -318,9 +364,9 @@ svn_cl__list(apr_getopt_t *os,
else
{
if (opt_state->incremental)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("'incremental' option only valid in XML "
- "mode"));
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--incremental is only valid in --xml mode"));
}
if (opt_state->xml)
@@ -332,6 +378,17 @@ svn_cl__list(apr_getopt_t *os,
pb.ctx = ctx;
pb.verbose = opt_state->verbose;
+ pb.file_size_unit = opt_state->file_size_unit;
+ if (pb.file_size_unit == SVN_CL__SIZE_UNIT_NONE)
+ {
+ pb.author_width = initial_author_width;
+ pb.max_author_width = maximum_author_width;
+ }
+ else
+ {
+ pb.author_width = initial_human_readable_author_width;
+ pb.max_author_width = maximum_human_readable_author_width;
+ }
if (opt_state->depth == svn_depth_unknown)
opt_state->depth = svn_depth_immediates;
diff --git a/subversion/svn/log-cmd.c b/subversion/svn/log-cmd.c
index 57f8415069143..87c372846f172 100644
--- a/subversion/svn/log-cmd.c
+++ b/subversion/svn/log-cmd.c
@@ -88,7 +88,7 @@ display_diff(const svn_log_entry_t *log_entry,
end_revision.value.number = log_entry->revision;
SVN_ERR(svn_stream_puts(outstream, "\n"));
- SVN_ERR(svn_client_diff_peg6(diff_options,
+ SVN_ERR(svn_client_diff_peg7(diff_options,
target_path_or_url,
target_peg_revision,
&start_revision, &end_revision,
@@ -102,6 +102,7 @@ display_diff(const svn_log_entry_t *log_entry,
FALSE /* ignore prop diff */,
FALSE /* properties only */,
FALSE /* use git diff format */,
+ TRUE /* pretty_print_mergeinfo */,
svn_cmdline_output_encoding(pool),
outstream,
errstream,
@@ -333,7 +334,7 @@ svn_cl__log_entry_receiver(void *baton,
return SVN_NO_ERROR;
}
- /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=807
+ /* ### See https://issues.apache.org/jira/browse/SVN-807
for more on the fallback fuzzy conversions below. */
if (author == NULL)
@@ -732,10 +733,6 @@ svn_cl__log(apr_getopt_t *os,
"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.diff_cmd && (! opt_state->show_diff))
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("'diff-cmd' option requires 'diff' "
diff --git a/subversion/svn/merge-cmd.c b/subversion/svn/merge-cmd.c
index f5c19198a5b29..140a39fad73f7 100644
--- a/subversion/svn/merge-cmd.c
+++ b/subversion/svn/merge-cmd.c
@@ -545,27 +545,31 @@ retry:
"fix invalid mergeinfo in target with 'svn propset'"));
}
- /* Run the interactive resolver if conflicts were raised. */
- SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
- pool, pool));
- if (conflicted_paths)
+ if (! opt_state->dry_run)
{
- SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
- opt_state, ctx, pool));
- if (merge_err &&
- svn_error_root_cause(merge_err)->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
+ /* Run the interactive resolver if conflicts were raised. */
+ SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths,
+ conflict_stats, pool, pool));
+ if (conflicted_paths)
{
- svn_error_t *err;
-
- /* Check if all conflicts were resolved just now. */
- err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
- conflict_stats, pool, pool);
- if (err)
- merge_err = svn_error_compose_create(merge_err, err);
- else if (conflicted_paths == NULL)
+ SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
+ opt_state, ctx, pool));
+ if (merge_err && svn_error_root_cause(merge_err)->apr_err ==
+ SVN_ERR_WC_FOUND_CONFLICT)
{
- svn_error_clear(merge_err);
- goto retry; /* ### conflicts resolved; continue merging */
+ svn_error_t *err;
+
+ /* Check if all conflicts were resolved just now. */
+ err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
+ conflict_stats,
+ pool, pool);
+ if (err)
+ merge_err = svn_error_compose_create(merge_err, err);
+ else if (conflicted_paths == NULL)
+ {
+ svn_error_clear(merge_err);
+ goto retry; /* ### conflicts resolved; continue merging */
+ }
}
}
}
diff --git a/subversion/svn/notify.c b/subversion/svn/notify.c
index c15301e32b8bf..ea0cc15945b10 100644
--- a/subversion/svn/notify.c
+++ b/subversion/svn/notify.c
@@ -210,7 +210,7 @@ svn_cl__conflict_stats_get_paths(apr_array_header_t **conflicted_paths,
}
}
- svn_hash_keys(conflicted_paths, all_conflicts, result_pool);
+ SVN_ERR(svn_hash_keys(conflicted_paths, all_conflicts, result_pool));
svn_sort__array(*conflicted_paths, svn_sort_compare_paths);
return SVN_NO_ERROR;
diff --git a/subversion/svn/propset-cmd.c b/subversion/svn/propset-cmd.c
index 05087069005ae..e7a818386be74 100644
--- a/subversion/svn/propset-cmd.c
+++ b/subversion/svn/propset-cmd.c
@@ -151,7 +151,7 @@ svn_cl__propset(apr_getopt_t *os,
* must always be explicitly provided when setting a versioned
* property. See
*
- * http://subversion.tigris.org/issues/show_bug.cgi?id=924
+ * https://issues.apache.org/jira/browse/SVN-924
*
* for more details.
*/
diff --git a/subversion/svn/resolve-cmd.c b/subversion/svn/resolve-cmd.c
index bbf9ff548ef5a..efd176dc7cdd0 100644
--- a/subversion/svn/resolve-cmd.c
+++ b/subversion/svn/resolve-cmd.c
@@ -109,7 +109,7 @@ svn_cl__walk_conflicts(apr_array_header_t *targets,
svn_client_conflict_t *conflict;
svn_pool_clear(iterpool);
-
+
SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
diff --git a/subversion/svn/revert-cmd.c b/subversion/svn/revert-cmd.c
index 74d7ff185635a..2ff35560e5000 100644
--- a/subversion/svn/revert-cmd.c
+++ b/subversion/svn/revert-cmd.c
@@ -67,10 +67,11 @@ svn_cl__revert(apr_getopt_t *os,
SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
- err = svn_client_revert3(targets, opt_state->depth,
+ err = svn_client_revert4(targets, opt_state->depth,
opt_state->changelists,
FALSE /* clear_changelists */,
FALSE /* metadata_only */,
+ !opt_state->remove_added /*added_keep_local*/,
ctx, scratch_pool);
if (err
&& (err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH)
diff --git a/subversion/svn/shelf-cmd.c b/subversion/svn/shelf-cmd.c
new file mode 100644
index 0000000000000..db0675b680776
--- /dev/null
+++ b/subversion/svn/shelf-cmd.c
@@ -0,0 +1,1405 @@
+/*
+ * shelf-cmd.c -- Shelving 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.
+ * ====================================================================
+ */
+
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
+
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+#include "svn_utf.h"
+
+#include "shelf-cmd.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_client_private.h"
+#include "private/svn_client_shelf.h"
+
+
+/* Open the newest version of SHELF; error if no versions found. */
+static svn_error_t *
+get_newest_version_existing(svn_client__shelf_version_t **shelf_version_p,
+ svn_client__shelf_t *shelf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_client__shelf_get_newest_version(shelf_version_p, shelf,
+ result_pool, scratch_pool));
+ if (!*shelf_version_p)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Shelf '%s': no versions found"),
+ shelf->name);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Fetch the next argument. */
+static svn_error_t *
+get_next_argument(const char **arg,
+ apr_getopt_t *os,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *args;
+
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(arg,
+ APR_ARRAY_IDX(args, 0, const char *),
+ result_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Parse the remaining arguments as paths relative to a WC.
+ *
+ * TARGETS are relative to current working directory.
+ *
+ * Set *targets_by_wcroot to a hash mapping (char *)wcroot_abspath to
+ * (apr_array_header_t *)array of relpaths relative to that WC root.
+ */
+static svn_error_t *
+targets_relative_to_wcs(apr_hash_t **targets_by_wcroot_p,
+ apr_array_header_t *targets,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *targets_by_wcroot = apr_hash_make(result_pool);
+ int i;
+
+ /* Make each target relative to the WC root. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *wcroot_abspath;
+ apr_array_header_t *paths;
+
+ SVN_ERR(svn_dirent_get_absolute(&target, target, result_pool));
+ SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, target,
+ ctx, result_pool, scratch_pool));
+ paths = svn_hash_gets(targets_by_wcroot, wcroot_abspath);
+ if (! paths)
+ {
+ paths = apr_array_make(result_pool, 0, sizeof(char *));
+ svn_hash_sets(targets_by_wcroot, wcroot_abspath, paths);
+ }
+ target = svn_dirent_skip_ancestor(wcroot_abspath, target);
+
+ if (target)
+ APR_ARRAY_PUSH(paths, const char *) = target;
+ }
+ *targets_by_wcroot_p = targets_by_wcroot;
+ return SVN_NO_ERROR;
+}
+
+/* Return targets relative to a WC. Error if they refer to more than one WC. */
+static svn_error_t *
+targets_relative_to_a_wc(const char **wc_root_abspath_p,
+ apr_array_header_t **paths_p,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *targets;
+ apr_hash_t *targets_by_wcroot;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ known_targets,
+ ctx, FALSE, result_pool));
+ svn_opt_push_implicit_dot_target(targets, result_pool);
+
+ SVN_ERR(targets_relative_to_wcs(&targets_by_wcroot, targets,
+ ctx, result_pool, scratch_pool));
+ if (apr_hash_count(targets_by_wcroot) != 1)
+ return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("All targets must be in the same WC"));
+
+ hi = apr_hash_first(scratch_pool, targets_by_wcroot);
+ *wc_root_abspath_p = apr_hash_this_key(hi);
+ *paths_p = apr_hash_this_val(hi);
+ return SVN_NO_ERROR;
+}
+
+/* Return a human-friendly description of DURATION.
+ */
+static char *
+friendly_age_str(apr_time_t mtime,
+ apr_time_t time_now,
+ apr_pool_t *result_pool)
+{
+ int minutes = (int)((time_now - mtime) / 1000000 / 60);
+ char *s;
+
+ if (minutes >= 60 * 24)
+ s = apr_psprintf(result_pool,
+ Q_("%d day ago", "%d days ago",
+ minutes / 60 / 24),
+ minutes / 60 / 24);
+ else if (minutes >= 60)
+ s = apr_psprintf(result_pool,
+ Q_("%d hour ago", "%d hours ago",
+ minutes / 60),
+ minutes / 60);
+ else
+ s = apr_psprintf(result_pool,
+ Q_("%d minute ago", "%d minutes ago",
+ minutes),
+ minutes);
+ return s;
+}
+
+/* A comparison function for svn_sort__hash(), comparing the mtime of two
+ svn_client_shelf_info_t's. */
+static int
+compare_shelf_infos_by_mtime(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ svn_client__shelf_info_t *a_val = a->value;
+ svn_client__shelf_info_t *b_val = b->value;
+
+ return (a_val->mtime < b_val->mtime)
+ ? -1 : (a_val->mtime > b_val->mtime) ? 1 : 0;
+}
+
+/* Return a list of shelves sorted by their mtime, oldest first.
+ */
+static svn_error_t *
+list_sorted_by_date(apr_array_header_t **list,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *shelf_infos;
+
+ SVN_ERR(svn_client__shelf_list(&shelf_infos, local_abspath,
+ ctx, scratch_pool, scratch_pool));
+ *list = svn_sort__hash(shelf_infos,
+ compare_shelf_infos_by_mtime,
+ scratch_pool);
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+stats(svn_client__shelf_t *shelf,
+ int version,
+ svn_client__shelf_version_t *shelf_version,
+ apr_time_t time_now,
+ svn_boolean_t with_logmsg,
+ apr_pool_t *scratch_pool)
+{
+ char *age_str;
+ char *version_str;
+ apr_hash_t *paths;
+ const char *paths_str = "";
+
+ if (! shelf_version)
+ {
+ return SVN_NO_ERROR;
+ }
+
+ age_str = friendly_age_str(shelf_version->mtime, time_now, scratch_pool);
+ if (version == shelf->max_version)
+ version_str = apr_psprintf(scratch_pool,
+ _("version %d"), version);
+ else
+ version_str = apr_psprintf(scratch_pool,
+ Q_("version %d of %d", "version %d of %d",
+ shelf->max_version),
+ version, shelf->max_version);
+ SVN_ERR(svn_client__shelf_paths_changed(&paths, shelf_version,
+ scratch_pool, scratch_pool));
+ paths_str = apr_psprintf(scratch_pool,
+ Q_("%d path changed", "%d paths changed",
+ apr_hash_count(paths)),
+ apr_hash_count(paths));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "%-30s %s, %s, %s\n",
+ shelf->name, version_str, age_str, paths_str));
+
+ if (with_logmsg)
+ {
+ char *log_message;
+
+ SVN_ERR(svn_client__shelf_get_log_message(&log_message, shelf,
+ scratch_pool));
+ if (log_message)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _(" %.50s\n"),
+ log_message));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Display a list of shelves */
+static svn_error_t *
+shelves_list(const char *local_abspath,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_time_t time_now = apr_time_now();
+ apr_array_header_t *list;
+ int i;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+
+ for (i = 0; i < list->nelts; i++)
+ {
+ const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
+ const char *name = item->key;
+ svn_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
+
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf_get_newest_version(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ if (quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%s\n", shelf->name));
+ else if (!shelf_version)
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%-30s no versions\n",
+ shelf->name));
+ else
+ SVN_ERR(stats(shelf, shelf->max_version, shelf_version, time_now,
+ TRUE /*with_logmsg*/, scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Print info about each checkpoint of the shelf named NAME.
+ */
+static svn_error_t *
+shelf_log(const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_time_t time_now = apr_time_now();
+ svn_client__shelf_t *shelf;
+ apr_array_header_t *versions;
+ int i;
+
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf_get_all_versions(&versions, shelf,
+ scratch_pool, scratch_pool));
+ for (i = 0; i < versions->nelts; i++)
+ {
+ svn_client__shelf_version_t *shelf_version
+ = APR_ARRAY_IDX(versions, i, void *);
+
+ SVN_ERR(stats(shelf, i + 1, shelf_version, time_now,
+ FALSE /*with_logmsg*/, scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Find the name of the youngest shelf.
+ */
+static svn_error_t *
+name_of_youngest(const char **name_p,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *list;
+ const svn_sort__item_t *youngest_item;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+ if (list->nelts == 0)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("No shelves found"));
+
+ youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
+ *name_p = apr_pstrdup(result_pool, youngest_item->key);
+ return SVN_NO_ERROR;
+}
+
+struct status_baton
+{
+ /* These fields correspond to the ones in the
+ svn_cl__print_status() interface. */
+ const char *target_abspath;
+ const char *target_path;
+
+ svn_boolean_t quiet; /* don't display statuses while shelving them */
+ int num_paths_shelved;
+ int num_paths_not_shelved;
+ svn_client_ctx_t *ctx;
+};
+
+/* 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 *scratch_pool)
+{
+ struct status_baton *sb = baton;
+ unsigned int conflicts;
+
+ return svn_cl__print_status(sb->target_abspath, sb->target_path,
+ path, status,
+ TRUE /*suppress_externals_placeholders*/,
+ FALSE /*detailed*/,
+ FALSE /*show_last_committed*/,
+ TRUE /*skip_unrecognized*/,
+ FALSE /*repos_locks*/,
+ &conflicts, &conflicts, &conflicts,
+ sb->ctx,
+ scratch_pool);
+}
+
+/* A callback function for shelved paths. */
+static svn_error_t *
+was_shelved(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct status_baton *sb = baton;
+
+ if (!sb->quiet)
+ {
+ SVN_ERR(print_status(baton, path, status, scratch_pool));
+ }
+
+ ++sb->num_paths_shelved;
+ return SVN_NO_ERROR;
+}
+
+/* A callback function for not-shelved paths. */
+static svn_error_t *
+was_not_shelved(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct status_baton *sb = baton;
+
+ SVN_ERR(print_status(baton, path, status, scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, " > not shelved\n"));
+ ++sb->num_paths_not_shelved;
+ return SVN_NO_ERROR;
+}
+
+/** Shelve/save a new version of changes.
+ *
+ * Shelve in shelf @a name the local modifications found by @a paths,
+ * @a depth, @a changelists. Revert the shelved changes from the WC
+ * unless @a keep_local is true.
+ *
+ * If no local modifications are found, throw an error.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * Report in @a *new_version_p the new version number (or, with dry run,
+ * what it would be).
+ */
+static svn_error_t *
+shelve(int *new_version_p,
+ const char *name,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ apr_hash_t *revprop_table,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__shelf_t *shelf;
+ svn_client__shelf_version_t *previous_version;
+ svn_client__shelf_version_t *new_version;
+ struct status_baton sb;
+
+ SVN_ERR(svn_client__shelf_open_or_create(&shelf,
+ name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf_get_newest_version(&previous_version, shelf,
+ scratch_pool, scratch_pool));
+
+ if (! quiet)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool, keep_local
+ ? _("--- Save a new version of '%s' in WC root '%s'\n")
+ : _("--- Shelve '%s' in WC root '%s'\n"),
+ shelf->name, shelf->wc_root_abspath));
+ SVN_ERR(stats(shelf, shelf->max_version, previous_version, apr_time_now(),
+ TRUE /*with_logmsg*/, scratch_pool));
+ }
+
+ sb.target_abspath = shelf->wc_root_abspath;
+ sb.target_path = "";
+ sb.quiet = quiet;
+ sb.num_paths_shelved = 0;
+ sb.num_paths_not_shelved = 0;
+ sb.ctx = ctx;
+
+ if (! quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ keep_local ? _("--- Saving...\n")
+ : _("--- Shelving...\n")));
+ SVN_ERR(svn_client__shelf_save_new_version3(&new_version, shelf,
+ paths, depth, changelists,
+ was_shelved, &sb,
+ was_not_shelved, &sb,
+ scratch_pool));
+ if (sb.num_paths_not_shelved > 0)
+ {
+ SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, previous_version,
+ scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ Q_("%d path could not be shelved",
+ "%d paths could not be shelved",
+ sb.num_paths_not_shelved),
+ sb.num_paths_not_shelved);
+ }
+ if (sb.num_paths_shelved == 0
+ || ! new_version)
+ {
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ keep_local ? _("No local modifications could be saved")
+ : _("No local modifications could be shelved"));
+ }
+
+ /* Un-apply the changes, if required. */
+ if (!keep_local)
+ {
+ SVN_ERR(svn_client__shelf_unapply(new_version,
+ dry_run, scratch_pool));
+ }
+
+ /* Fetch the log message and any other revprops */
+ if (ctx->log_msg_func3)
+ {
+ const char *tmp_file;
+ apr_array_header_t *commit_items
+ = apr_array_make(scratch_pool, 1, sizeof(void *));
+ const char *message = "";
+
+ SVN_ERR(ctx->log_msg_func3(&message, &tmp_file, commit_items,
+ ctx->log_msg_baton3, scratch_pool));
+ /* Abort the shelving if the log message callback requested so. */
+ if (! message)
+ return SVN_NO_ERROR;
+
+ if (message && !dry_run)
+ {
+ svn_string_t *propval = svn_string_create(message, scratch_pool);
+
+ if (! revprop_table)
+ revprop_table = apr_hash_make(scratch_pool);
+ svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, propval);
+ }
+ }
+
+ SVN_ERR(svn_client__shelf_revprop_set_all(shelf, revprop_table, scratch_pool));
+
+ if (new_version_p)
+ *new_version_p = shelf->max_version;
+
+ if (dry_run)
+ {
+ SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, previous_version,
+ scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Return the single character representation of STATUS.
+ * (Similar to subversion/svn/status.c:generate_status_code()
+ * and subversion/tests/libsvn_client/client-test.c:status_to_char().) */
+static char
+status_to_char(enum svn_wc_status_kind status)
+{
+ switch (status)
+ {
+ case svn_wc_status_none: return '.';
+ case svn_wc_status_unversioned: return '?';
+ case svn_wc_status_normal: return ' ';
+ case svn_wc_status_added: return 'A';
+ case svn_wc_status_missing: 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_merged: return 'G';
+ case svn_wc_status_conflicted: return 'C';
+ case svn_wc_status_ignored: return 'I';
+ case svn_wc_status_obstructed: return '~';
+ case svn_wc_status_external: return 'X';
+ case svn_wc_status_incomplete: return ':';
+ default: return '*';
+ }
+}
+
+/* Throw an error if any path affected by SHELF_VERSION gives a conflict
+ * when applied (as a dry-run) to the WC. */
+static svn_error_t *
+test_apply(svn_client__shelf_version_t *shelf_version,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *paths;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_client__shelf_paths_changed(&paths, shelf_version,
+ scratch_pool, scratch_pool));
+ for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ svn_boolean_t conflict;
+
+ SVN_ERR(svn_client__shelf_test_apply_file(&conflict, shelf_version, path,
+ scratch_pool));
+ if (conflict)
+ {
+ char *to_wc_abspath
+ = svn_dirent_join(shelf_version->shelf->wc_root_abspath, path,
+ scratch_pool);
+ svn_wc_status3_t *status;
+
+ SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, to_wc_abspath,
+ scratch_pool, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Shelved path '%s' already has "
+ "status '%c' in the working copy"),
+ path, status_to_char(status->node_status));
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+/** Restore/unshelve a given or newest version of changes.
+ *
+ * Restore local modifications from shelf @a name version @a arg,
+ * or the newest version is @a arg is null.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * Error if any path would have a conflict, unless @a force_if_conflict.
+ */
+static svn_error_t *
+shelf_restore(const char *name,
+ const char *arg,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_boolean_t force_if_conflict,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ int version, old_version;
+ apr_time_t time_now = apr_time_now();
+ svn_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
+
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+
+ old_version = shelf->max_version;
+ if (arg)
+ {
+ SVN_ERR(svn_cstring_atoi(&version, arg));
+ SVN_ERR(svn_client__shelf_version_open(&shelf_version,
+ shelf, version,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ version = shelf->max_version;
+ SVN_ERR(get_newest_version_existing(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ }
+
+ if (! quiet)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("--- Unshelve '%s' in WC root '%s'\n"),
+ shelf->name, shelf->wc_root_abspath));
+ SVN_ERR(stats(shelf, version, shelf_version, time_now,
+ TRUE /*with_logmsg*/, scratch_pool));
+ }
+ if (! force_if_conflict)
+ {
+ SVN_ERR_W(test_apply(shelf_version, ctx, scratch_pool),
+ _("Cannot unshelve/restore, as at least one shelved "
+ "path would conflict with a local modification "
+ "or other status in the working copy"));
+ }
+
+ SVN_ERR(svn_client__shelf_apply(shelf_version,
+ dry_run, scratch_pool));
+
+ if (! dry_run)
+ {
+ SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, shelf_version,
+ scratch_pool));
+ }
+
+ if (!quiet)
+ {
+ if (version < old_version)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ Q_("restored '%s' version %d and deleted %d newer version\n",
+ "restored '%s' version %d and deleted %d newer versions\n",
+ old_version - version),
+ name, version, old_version - version));
+ else
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("restored '%s' version %d (the newest version)\n"),
+ name, version));
+ }
+
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+shelf_diff(const char *name,
+ const char *arg,
+ const char *local_abspath,
+ svn_boolean_t summarize,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
+ svn_stream_t *stream, *errstream;
+ svn_diff_tree_processor_t *diff_processor;
+
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+
+ if (arg)
+ {
+ int version;
+
+ SVN_ERR(svn_cstring_atoi(&version, arg));
+ SVN_ERR(svn_client__shelf_version_open(&shelf_version,
+ shelf, version,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(get_newest_version_existing(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ }
+
+ SVN_ERR(svn_stream_for_stdout(&stream, scratch_pool));
+ errstream = svn_stream_empty(scratch_pool);
+
+ if (summarize)
+ {
+ svn_client_diff_summarize_func_t func;
+ void *baton;
+
+ SVN_ERR(svn_cl__get_diff_summary_writer(&func, &baton,
+ FALSE /*xml*/,
+ FALSE /*ignore_properties*/,
+ "" /*anchor/prefix*/,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(&diff_processor,
+ func, baton,
+ scratch_pool,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(svn_client__get_diff_writer_svn(
+ &diff_processor,
+ NULL /*anchor*/,
+ "", "", /*orig_path_1, orig_path_2,*/
+ NULL /*options*/,
+ "" /*relative_to_dir*/,
+ FALSE /*no_diff_added*/,
+ FALSE /*no_diff_deleted*/,
+ FALSE /*show_copies_as_adds*/,
+ FALSE /*ignore_content_type*/,
+ FALSE /*ignore_properties*/,
+ FALSE /*properties_only*/,
+ TRUE /*pretty_print_mergeinfo*/,
+ svn_cmdline_output_encoding(scratch_pool),
+ stream, errstream,
+ ctx, scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf_diff(shelf_version, "",
+ depth, ignore_ancestry,
+ diff_processor, scratch_pool));
+ SVN_ERR(svn_stream_close(stream));
+
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+shelf_drop(const char *name,
+ const char *local_abspath,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_client__shelf_delete(name, local_abspath, dry_run,
+ ctx, scratch_pool));
+ if (! quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("deleted '%s'\n"),
+ name));
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+shelf_shelve(int *new_version,
+ const char *name,
+ apr_array_header_t *targets,
+ svn_depth_t depth,
+ apr_array_header_t *changelists,
+ apr_hash_t *revprop_table,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_abspath;
+
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ /* ### TODO: check all paths are in same WC; for now use first path */
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath,
+ APR_ARRAY_IDX(targets, 0, char *),
+ scratch_pool));
+
+ SVN_ERR(shelve(new_version, name,
+ targets, depth, changelists,
+ revprop_table,
+ keep_local, dry_run, quiet,
+ local_abspath, ctx, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+svn_cl__shelf_shelve(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool);
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_save(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;
+
+ opt_state->keep_local = TRUE;
+ SVN_ERR(svn_cl__shelf_shelve(os, baton, pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_shelve(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 *name;
+ apr_array_header_t *targets;
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(get_next_argument(&name, os, pool, 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));
+ {
+ int new_version;
+ svn_error_t *err;
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
+ opt_state, NULL, ctx->config,
+ pool));
+ err = shelf_shelve(&new_version, name,
+ targets, opt_state->depth, opt_state->changelists,
+ opt_state->revprop_table,
+ opt_state->keep_local, opt_state->dry_run,
+ opt_state->quiet, ctx, pool);
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
+ err, pool));
+ else
+ SVN_ERR(err);
+
+ if (! opt_state->quiet)
+ {
+ if (opt_state->keep_local)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("saved '%s' version %d\n"),
+ name, new_version));
+ else
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("shelved '%s' version %d\n"),
+ name, new_version));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_unshelve(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 *local_abspath;
+ const char *name;
+ const char *arg = NULL;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", scratch_pool));
+
+ if (os->ind < os->argc)
+ {
+ SVN_ERR(get_next_argument(&name, os, scratch_pool, scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(name_of_youngest(&name,
+ local_abspath, ctx, scratch_pool, scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("unshelving the youngest shelf, '%s'\n"),
+ name));
+ }
+
+ /* Which checkpoint number? */
+ if (os->ind < os->argc)
+ SVN_ERR(get_next_argument(&arg, os, scratch_pool, scratch_pool));
+
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(shelf_restore(name, arg,
+ opt_state->dry_run, opt_state->quiet,
+ opt_state->force /*force_already_modified*/,
+ local_abspath, ctx, scratch_pool));
+
+ if (opt_state->drop)
+ {
+ SVN_ERR(shelf_drop(name, local_abspath,
+ opt_state->dry_run, opt_state->quiet,
+ ctx, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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 = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ 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);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+
+ SVN_ERR(shelves_list(local_abspath,
+ opt_state->quiet,
+ ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* "svn shelf-list-by-paths [PATH...]"
+ *
+ * TARGET_RELPATHS are all within the same WC, relative to WC_ROOT_ABSPATH.
+ */
+static svn_error_t *
+shelf_list_by_paths(apr_array_header_t *target_relpaths,
+ const char *wc_root_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *shelves;
+ apr_hash_t *paths_to_shelf_name = apr_hash_make(scratch_pool);
+ apr_array_header_t *array;
+ int i;
+
+ SVN_ERR(list_sorted_by_date(&shelves,
+ wc_root_abspath, ctx, scratch_pool));
+
+ /* Check paths are valid */
+ for (i = 0; i < target_relpaths->nelts; i++)
+ {
+ char *target_relpath = APR_ARRAY_IDX(target_relpaths, i, char *);
+
+ if (svn_path_is_url(target_relpath))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), target_relpath);
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(target_relpath));
+ }
+
+ /* Find the most recent shelf for each affected path */
+ for (i = 0; i < shelves->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(shelves, i, svn_sort__item_t);
+ const char *name = item->key;
+ svn_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
+ apr_hash_t *shelf_paths;
+ int j;
+
+ SVN_ERR(svn_client__shelf_open_existing(&shelf,
+ name, wc_root_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf_get_newest_version(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ if (!shelf_version)
+ continue;
+ SVN_ERR(svn_client__shelf_paths_changed(&shelf_paths,
+ shelf_version,
+ scratch_pool, scratch_pool));
+ for (j = 0; j < target_relpaths->nelts; j++)
+ {
+ char *target_relpath = APR_ARRAY_IDX(target_relpaths, j, char *);
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, shelf_paths);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *shelf_path = apr_hash_this_key(hi);
+
+ if (svn_relpath_skip_ancestor(target_relpath, shelf_path))
+ {
+ if (! svn_hash_gets(paths_to_shelf_name, shelf_path))
+ {
+ svn_hash_sets(paths_to_shelf_name, shelf_path, shelf->name);
+ }
+ }
+ }
+ }
+ }
+
+ /* Print the results. */
+ array = svn_sort__hash(paths_to_shelf_name,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+ for (i = 0; i < array->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
+ const char *path = item->key;
+ const char *name = item->value;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%-20.20s %s\n",
+ name,
+ svn_dirent_local_style(path, scratch_pool)));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_list_by_paths(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 *wc_root_abspath;
+ apr_array_header_t *targets;
+
+ /* Parse the remaining arguments as paths. */
+ SVN_ERR(targets_relative_to_a_wc(&wc_root_abspath, &targets,
+ os, opt_state->targets,
+ ctx, pool, pool));
+
+ SVN_ERR(shelf_list_by_paths(targets, wc_root_abspath, ctx, pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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;
+ const char *local_abspath;
+ const char *name;
+ const char *arg = NULL;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ /* Which checkpoint number? */
+ if (os->ind < os->argc)
+ SVN_ERR(get_next_argument(&arg, os, pool, pool));
+
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ SVN_ERR(shelf_diff(name, arg, local_abspath,
+ opt_state->diff.summarize,
+ opt_state->depth, opt_state->ignore_ancestry,
+ ctx, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_drop(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 *name;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ int i;
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+ SVN_ERR(shelf_drop(name, local_abspath,
+ opt_state->dry_run, opt_state->quiet,
+ ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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;
+ const char *name;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ int i;
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+ SVN_ERR(shelf_log(name, local_abspath, ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/**************************************************************************/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__wc_copy_mods(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *src_wc_abspath, *dst_wc_abspath;
+
+ SVN_ERR(get_next_argument(&src_wc_abspath, os, pool, pool));
+ SVN_ERR(svn_dirent_get_absolute(&src_wc_abspath, src_wc_abspath, pool));
+
+ SVN_ERR(get_next_argument(&dst_wc_abspath, os, pool, pool));
+ SVN_ERR(svn_dirent_get_absolute(&dst_wc_abspath, dst_wc_abspath, pool));
+
+ SVN_ERR(svn_client__wc_copy_mods(src_wc_abspath, dst_wc_abspath,
+ ctx->notify_func2, ctx->notify_baton2,
+ ctx, pool));
+
+ return SVN_NO_ERROR;
+}
+
+const svn_opt_subcommand_desc3_t
+svn_cl__cmd_table_shelf3[] =
+{
+ { "x-shelf-diff", svn_cl__shelf_diff, {0}, {N_(
+ "Show shelved changes as a diff.\n"
+ "usage: x-shelf-diff SHELF [VERSION]\n"
+ "\n"), N_(
+ " Show the changes in SHELF:VERSION (default: latest) as a diff.\n"
+ "\n"), N_(
+ " See also: 'svn diff --cl=svn:shelf:SHELF' which supports most options of\n"
+ " 'svn diff'.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {opt_summarize},
+ },
+
+ { "x-shelf-drop", svn_cl__shelf_drop, {0}, {N_(
+ "Delete a shelf.\n"
+ "usage: x-shelf-drop SHELF [PATH ...]\n"
+ "\n"), N_(
+ " Delete the shelves named SHELF from the working copies containing PATH\n"
+ " (default PATH is '.')\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ },
+
+ { "x-shelf-list", svn_cl__shelf_list, {"x-shelves"}, {N_(
+ "List shelves.\n"
+ "usage: x-shelf-list [PATH ...]\n"
+ "\n"), N_(
+ " List shelves for each working copy containing PATH (default is '.')\n"
+ " Include the first line of any log message and some details about the\n"
+ " contents of the shelf, unless '-q' is given.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', 'v'}
+ },
+
+ { "x-shelf-list-by-paths", svn_cl__shelf_list_by_paths, {0}, {N_(
+ "List which shelf affects each path.\n"
+ "usage: x-shelf-list-by-paths [PATH...]\n"
+ "\n"), N_(
+ " List which shelf most recently affects each path below the given PATHs.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ },
+
+ { "x-shelf-log", svn_cl__shelf_log, {0}, {N_(
+ "Show the versions of a shelf.\n"
+ "usage: x-shelf-log SHELF [PATH...]\n"
+ "\n"), N_(
+ " Show all versions of SHELF for each working copy containing PATH (the\n"
+ " default PATH is '.').\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', 'v'}
+ },
+
+ { "x-shelf-save", svn_cl__shelf_save, {0}, {N_(
+ "Copy local changes onto a new version of a shelf.\n"
+ "usage: x-shelf-save SHELF [PATH...]\n"
+ "\n"), N_(
+ " Save local changes in the given PATHs as a new version of SHELF.\n"
+ " The shelf's log message can be set with -m, -F, etc.\n"
+ "\n"), N_(
+ " The same as 'svn shelve --keep-local'.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', opt_dry_run,
+ opt_depth, opt_targets, opt_changelist,
+ SVN_CL__LOG_MSG_OPTIONS,
+ }
+ },
+
+ { "x-shelve", svn_cl__shelf_shelve, {0}, {N_(
+ "Move local changes onto a shelf.\n"
+ "usage: x-shelve [--keep-local] SHELF [PATH...]\n"
+ "\n"), N_(
+ " Save the local changes in the given PATHs to a new or existing SHELF.\n"
+ " Revert those changes from the WC unless '--keep-local' is given.\n"
+ " The shelf's log message can be set with -m, -F, etc.\n"
+ "\n"), N_(
+ " 'svn shelve --keep-local' is the same as 'svn shelf-save'.\n"
+ "\n"), N_(
+ " The kinds of change you can shelve are committable changes to files and\n"
+ " properties, except the following kinds which are not yet supported:\n"
+ " * copies and moves\n"
+ " * mkdir and rmdir\n"
+ " Uncommittable states such as conflicts, unversioned and missing cannot\n"
+ " be shelved.\n"
+ "\n"), N_(
+ " To bring back shelved changes, use 'svn unshelve SHELF'.\n"
+ "\n"), N_(
+ " Shelves are currently stored under <WC>/.svn/experimental/shelves/ .\n"
+ " (In Subversion 1.10, shelves were stored under <WC>/.svn/shelves/ as\n"
+ " patch files. To recover a shelf created by 1.10, either use a 1.10\n"
+ " client to find and unshelve it, or find the patch file and use any\n"
+ " 1.10 or later 'svn patch' to apply it.)\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', opt_dry_run, opt_keep_local,
+ opt_depth, opt_targets, opt_changelist,
+ SVN_CL__LOG_MSG_OPTIONS,
+ } },
+
+ { "x-unshelve", svn_cl__shelf_unshelve, {0}, {N_(
+ "Copy shelved changes back into the WC.\n"
+ "usage: x-unshelve [--drop] [SHELF [VERSION]]\n"
+ "\n"), N_(
+ " Apply the changes stored in SHELF to the working copy.\n"
+ " SHELF defaults to the newest shelf.\n"
+ "\n"), N_(
+ " Apply the newest version of the shelf, by default. If VERSION is\n"
+ " specified, apply that version and discard all versions newer than that.\n"
+ " In any case, retain the unshelved version and versions older than that\n"
+ " (unless --drop is specified).\n"
+ "\n"), N_(
+ " With --drop, delete the entire shelf (like 'svn shelf-drop') after\n"
+ " successfully unshelving with no conflicts.\n"
+ "\n"), N_(
+ " The working files involved should be in a clean, unmodified state\n"
+ " before using this command. To roll back to an older version of the\n"
+ " shelf, first ensure any current working changes are removed, such as\n"
+ " by shelving or reverting them, and then unshelve the desired version.\n"
+ "\n"), N_(
+ " Unshelve normally refuses to apply any changes if any path involved is\n"
+ " already modified (or has any other abnormal status) in the WC. With\n"
+ " --force, it does not check and may error out and/or produce partial or\n"
+ " unexpected results.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {opt_drop, 'q', opt_dry_run, opt_force} },
+
+ { "x-wc-copy-mods", svn_cl__wc_copy_mods, {0}, {N_(
+ "Copy local modifications from one WC to another.\n"
+ "usage: x-wc-copy-mods SRC_WC_PATH DST_WC_PATH\n"
+ "\n"), N_(
+ " The source and destination WC paths may be in the same WC or in different"
+ " WCs.\n"
+ "\n"), N_(
+ " This feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ },
+
+ { NULL, NULL, {0}, {NULL}, {0} }
+};
+
diff --git a/subversion/svn/shelf-cmd.h b/subversion/svn/shelf-cmd.h
new file mode 100644
index 0000000000000..c5f3cabe60de4
--- /dev/null
+++ b/subversion/svn/shelf-cmd.h
@@ -0,0 +1,49 @@
+/*
+ * shelf-cmd.h: experimental shelving v3
+ *
+ * ====================================================================
+ * 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_SHELF_CMD_H
+#define SVN_SHELF_CMD_H
+
+/*** Includes. ***/
+#include <apr_getopt.h>
+
+#include "svn_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+extern const svn_opt_subcommand_desc3_t svn_cl__cmd_table_shelf3[];
+/*extern const apr_getopt_option_t svn_cl__options_shelving3[];*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_SHELF_CMD_H */
diff --git a/subversion/svn/shelf2-cmd.c b/subversion/svn/shelf2-cmd.c
new file mode 100644
index 0000000000000..8305ce30938eb
--- /dev/null
+++ b/subversion/svn/shelf2-cmd.c
@@ -0,0 +1,1369 @@
+/*
+ * shelf2-cmd.c -- Shelving 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.
+ * ====================================================================
+ */
+
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
+
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+#include "svn_utf.h"
+
+#include "shelf2-cmd.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_client_private.h"
+#include "private/svn_client_shelf2.h"
+
+
+/* Open the newest version of SHELF; error if no versions found. */
+static svn_error_t *
+get_newest_version_existing(svn_client__shelf2_version_t **shelf_version_p,
+ svn_client__shelf2_t *shelf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_client__shelf2_get_newest_version(shelf_version_p, shelf,
+ result_pool, scratch_pool));
+ if (!*shelf_version_p)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Shelf '%s': no versions found"),
+ shelf->name);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Fetch the next argument. */
+static svn_error_t *
+get_next_argument(const char **arg,
+ apr_getopt_t *os,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *args;
+
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(arg,
+ APR_ARRAY_IDX(args, 0, const char *),
+ result_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Parse the remaining arguments as paths relative to a WC.
+ *
+ * TARGETS are relative to current working directory.
+ *
+ * Set *targets_by_wcroot to a hash mapping (char *)wcroot_abspath to
+ * (apr_array_header_t *)array of relpaths relative to that WC root.
+ */
+static svn_error_t *
+targets_relative_to_wcs(apr_hash_t **targets_by_wcroot_p,
+ apr_array_header_t *targets,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *targets_by_wcroot = apr_hash_make(result_pool);
+ int i;
+
+ /* Make each target relative to the WC root. */
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *wcroot_abspath;
+ apr_array_header_t *paths;
+
+ SVN_ERR(svn_dirent_get_absolute(&target, target, result_pool));
+ SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, target,
+ ctx, result_pool, scratch_pool));
+ paths = svn_hash_gets(targets_by_wcroot, wcroot_abspath);
+ if (! paths)
+ {
+ paths = apr_array_make(result_pool, 0, sizeof(char *));
+ svn_hash_sets(targets_by_wcroot, wcroot_abspath, paths);
+ }
+ target = svn_dirent_skip_ancestor(wcroot_abspath, target);
+
+ if (target)
+ APR_ARRAY_PUSH(paths, const char *) = target;
+ }
+ *targets_by_wcroot_p = targets_by_wcroot;
+ return SVN_NO_ERROR;
+}
+
+/* Return targets relative to a WC. Error if they refer to more than one WC. */
+static svn_error_t *
+targets_relative_to_a_wc(const char **wc_root_abspath_p,
+ apr_array_header_t **paths_p,
+ apr_getopt_t *os,
+ const apr_array_header_t *known_targets,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *targets;
+ apr_hash_t *targets_by_wcroot;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ known_targets,
+ ctx, FALSE, result_pool));
+ svn_opt_push_implicit_dot_target(targets, result_pool);
+
+ SVN_ERR(targets_relative_to_wcs(&targets_by_wcroot, targets,
+ ctx, result_pool, scratch_pool));
+ if (apr_hash_count(targets_by_wcroot) != 1)
+ return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("All targets must be in the same WC"));
+
+ hi = apr_hash_first(scratch_pool, targets_by_wcroot);
+ *wc_root_abspath_p = apr_hash_this_key(hi);
+ *paths_p = apr_hash_this_val(hi);
+ return SVN_NO_ERROR;
+}
+
+/* Return a human-friendly description of DURATION.
+ */
+static char *
+friendly_age_str(apr_time_t mtime,
+ apr_time_t time_now,
+ apr_pool_t *result_pool)
+{
+ int minutes = (int)((time_now - mtime) / 1000000 / 60);
+ char *s;
+
+ if (minutes >= 60 * 24)
+ s = apr_psprintf(result_pool,
+ Q_("%d day ago", "%d days ago",
+ minutes / 60 / 24),
+ minutes / 60 / 24);
+ else if (minutes >= 60)
+ s = apr_psprintf(result_pool,
+ Q_("%d hour ago", "%d hours ago",
+ minutes / 60),
+ minutes / 60);
+ else
+ s = apr_psprintf(result_pool,
+ Q_("%d minute ago", "%d minutes ago",
+ minutes),
+ minutes);
+ return s;
+}
+
+/* A comparison function for svn_sort__hash(), comparing the mtime of two
+ svn_client_shelf_info_t's. */
+static int
+compare_shelf_infos_by_mtime(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ svn_client__shelf2_info_t *a_val = a->value;
+ svn_client__shelf2_info_t *b_val = b->value;
+
+ return (a_val->mtime < b_val->mtime)
+ ? -1 : (a_val->mtime > b_val->mtime) ? 1 : 0;
+}
+
+/* Return a list of shelves sorted by their mtime, oldest first.
+ */
+static svn_error_t *
+list_sorted_by_date(apr_array_header_t **list,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *shelf_infos;
+
+ SVN_ERR(svn_client__shelf2_list(&shelf_infos, local_abspath,
+ ctx, scratch_pool, scratch_pool));
+ *list = svn_sort__hash(shelf_infos,
+ compare_shelf_infos_by_mtime,
+ scratch_pool);
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+stats(svn_client__shelf2_t *shelf,
+ int version,
+ svn_client__shelf2_version_t *shelf_version,
+ apr_time_t time_now,
+ svn_boolean_t with_logmsg,
+ apr_pool_t *scratch_pool)
+{
+ char *age_str;
+ char *version_str;
+ apr_hash_t *paths;
+ const char *paths_str = "";
+
+ if (! shelf_version)
+ {
+ return SVN_NO_ERROR;
+ }
+
+ age_str = friendly_age_str(shelf_version->mtime, time_now, scratch_pool);
+ if (version == shelf->max_version)
+ version_str = apr_psprintf(scratch_pool,
+ _("version %d"), version);
+ else
+ version_str = apr_psprintf(scratch_pool,
+ Q_("version %d of %d", "version %d of %d",
+ shelf->max_version),
+ version, shelf->max_version);
+ SVN_ERR(svn_client__shelf2_paths_changed(&paths, shelf_version,
+ scratch_pool, scratch_pool));
+ paths_str = apr_psprintf(scratch_pool,
+ Q_("%d path changed", "%d paths changed",
+ apr_hash_count(paths)),
+ apr_hash_count(paths));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "%-30s %s, %s, %s\n",
+ shelf->name, version_str, age_str, paths_str));
+
+ if (with_logmsg)
+ {
+ char *log_message;
+
+ SVN_ERR(svn_client__shelf2_get_log_message(&log_message, shelf,
+ scratch_pool));
+ if (log_message)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _(" %.50s\n"),
+ log_message));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Display a list of shelves */
+static svn_error_t *
+shelves_list(const char *local_abspath,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_time_t time_now = apr_time_now();
+ apr_array_header_t *list;
+ int i;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+
+ for (i = 0; i < list->nelts; i++)
+ {
+ const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
+ const char *name = item->key;
+ svn_client__shelf2_t *shelf;
+ svn_client__shelf2_version_t *shelf_version;
+
+ SVN_ERR(svn_client__shelf2_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf2_get_newest_version(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ if (quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%s\n", shelf->name));
+ else if (!shelf_version)
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%-30s no versions\n",
+ shelf->name));
+ else
+ SVN_ERR(stats(shelf, shelf->max_version, shelf_version, time_now,
+ TRUE /*with_logmsg*/, scratch_pool));
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Print info about each checkpoint of the shelf named NAME.
+ */
+static svn_error_t *
+shelf_log(const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_time_t time_now = apr_time_now();
+ svn_client__shelf2_t *shelf;
+ apr_array_header_t *versions;
+ int i;
+
+ SVN_ERR(svn_client__shelf2_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf2_get_all_versions(&versions, shelf,
+ scratch_pool, scratch_pool));
+ for (i = 0; i < versions->nelts; i++)
+ {
+ svn_client__shelf2_version_t *shelf_version
+ = APR_ARRAY_IDX(versions, i, void *);
+
+ SVN_ERR(stats(shelf, i + 1, shelf_version, time_now,
+ FALSE /*with_logmsg*/, scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Find the name of the youngest shelf.
+ */
+static svn_error_t *
+name_of_youngest(const char **name_p,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *list;
+ const svn_sort__item_t *youngest_item;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+ if (list->nelts == 0)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("No shelves found"));
+
+ youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
+ *name_p = apr_pstrdup(result_pool, youngest_item->key);
+ return SVN_NO_ERROR;
+}
+
+struct status_baton
+{
+ /* These fields correspond to the ones in the
+ svn_cl__print_status() interface. */
+ const char *target_abspath;
+ const char *target_path;
+
+ svn_boolean_t quiet; /* don't display statuses while shelving them */
+ int num_paths_shelved;
+ int num_paths_not_shelved;
+ svn_client_ctx_t *ctx;
+};
+
+/* 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 *scratch_pool)
+{
+ struct status_baton *sb = baton;
+ unsigned int conflicts;
+
+ return svn_cl__print_status(sb->target_abspath, sb->target_path,
+ path, status,
+ TRUE /*suppress_externals_placeholders*/,
+ FALSE /*detailed*/,
+ FALSE /*show_last_committed*/,
+ TRUE /*skip_unrecognized*/,
+ FALSE /*repos_locks*/,
+ &conflicts, &conflicts, &conflicts,
+ sb->ctx,
+ scratch_pool);
+}
+
+/* A callback function for shelved paths. */
+static svn_error_t *
+was_shelved(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct status_baton *sb = baton;
+
+ if (!sb->quiet)
+ {
+ SVN_ERR(print_status(baton, path, status, scratch_pool));
+ }
+
+ ++sb->num_paths_shelved;
+ return SVN_NO_ERROR;
+}
+
+/* A callback function for not-shelved paths. */
+static svn_error_t *
+was_not_shelved(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct status_baton *sb = baton;
+
+ SVN_ERR(print_status(baton, path, status, scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, " > not shelved\n"));
+ ++sb->num_paths_not_shelved;
+ return SVN_NO_ERROR;
+}
+
+/** Shelve/save a new version of changes.
+ *
+ * Shelve in shelf @a name the local modifications found by @a paths,
+ * @a depth, @a changelists. Revert the shelved changes from the WC
+ * unless @a keep_local is true.
+ *
+ * If no local modifications are found, throw an error.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * Report in @a *new_version_p the new version number (or, with dry run,
+ * what it would be).
+ */
+static svn_error_t *
+shelve(int *new_version_p,
+ const char *name,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ apr_hash_t *revprop_table,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__shelf2_t *shelf;
+ svn_client__shelf2_version_t *previous_version;
+ svn_client__shelf2_version_t *new_version;
+ struct status_baton sb;
+
+ SVN_ERR(svn_client__shelf2_open_or_create(&shelf,
+ name, local_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf2_get_newest_version(&previous_version, shelf,
+ scratch_pool, scratch_pool));
+
+ if (! quiet)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool, keep_local
+ ? _("--- Save a new version of '%s' in WC root '%s'\n")
+ : _("--- Shelve '%s' in WC root '%s'\n"),
+ shelf->name, shelf->wc_root_abspath));
+ SVN_ERR(stats(shelf, shelf->max_version, previous_version, apr_time_now(),
+ TRUE /*with_logmsg*/, scratch_pool));
+ }
+
+ sb.target_abspath = shelf->wc_root_abspath;
+ sb.target_path = "";
+ sb.quiet = quiet;
+ sb.num_paths_shelved = 0;
+ sb.num_paths_not_shelved = 0;
+ sb.ctx = ctx;
+
+ if (! quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ keep_local ? _("--- Saving...\n")
+ : _("--- Shelving...\n")));
+ SVN_ERR(svn_client__shelf2_save_new_version3(&new_version, shelf,
+ paths, depth, changelists,
+ was_shelved, &sb,
+ was_not_shelved, &sb,
+ scratch_pool));
+ if (sb.num_paths_not_shelved > 0)
+ {
+ SVN_ERR(svn_client__shelf2_delete_newer_versions(shelf, previous_version,
+ scratch_pool));
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ Q_("%d path could not be shelved",
+ "%d paths could not be shelved",
+ sb.num_paths_not_shelved),
+ sb.num_paths_not_shelved);
+ }
+ if (sb.num_paths_shelved == 0
+ || ! new_version)
+ {
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ keep_local ? _("No local modifications could be saved")
+ : _("No local modifications could be shelved"));
+ }
+
+ /* Un-apply the changes, if required. */
+ if (!keep_local)
+ {
+ SVN_ERR(svn_client__shelf2_unapply(new_version,
+ dry_run, scratch_pool));
+ }
+
+ /* Fetch the log message and any other revprops */
+ if (ctx->log_msg_func3)
+ {
+ const char *tmp_file;
+ apr_array_header_t *commit_items
+ = apr_array_make(scratch_pool, 1, sizeof(void *));
+ const char *message = "";
+
+ SVN_ERR(ctx->log_msg_func3(&message, &tmp_file, commit_items,
+ ctx->log_msg_baton3, scratch_pool));
+ /* Abort the shelving if the log message callback requested so. */
+ if (! message)
+ return SVN_NO_ERROR;
+
+ if (message && !dry_run)
+ {
+ svn_string_t *propval = svn_string_create(message, scratch_pool);
+
+ if (! revprop_table)
+ revprop_table = apr_hash_make(scratch_pool);
+ svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, propval);
+ }
+ }
+
+ SVN_ERR(svn_client__shelf2_revprop_set_all(shelf, revprop_table, scratch_pool));
+
+ if (new_version_p)
+ *new_version_p = shelf->max_version;
+
+ if (dry_run)
+ {
+ SVN_ERR(svn_client__shelf2_delete_newer_versions(shelf, previous_version,
+ scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* Return the single character representation of STATUS.
+ * (Similar to subversion/svn/status.c:generate_status_code()
+ * and subversion/tests/libsvn_client/client-test.c:status_to_char().) */
+static char
+status_to_char(enum svn_wc_status_kind status)
+{
+ switch (status)
+ {
+ case svn_wc_status_none: return '.';
+ case svn_wc_status_unversioned: return '?';
+ case svn_wc_status_normal: return ' ';
+ case svn_wc_status_added: return 'A';
+ case svn_wc_status_missing: 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_merged: return 'G';
+ case svn_wc_status_conflicted: return 'C';
+ case svn_wc_status_ignored: return 'I';
+ case svn_wc_status_obstructed: return '~';
+ case svn_wc_status_external: return 'X';
+ case svn_wc_status_incomplete: return ':';
+ default: return '*';
+ }
+}
+
+/* Throw an error if any path affected by SHELF_VERSION gives a conflict
+ * when applied (as a dry-run) to the WC. */
+static svn_error_t *
+test_apply(svn_client__shelf2_version_t *shelf_version,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *paths;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(svn_client__shelf2_paths_changed(&paths, shelf_version,
+ scratch_pool, scratch_pool));
+ for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ svn_boolean_t conflict;
+
+ SVN_ERR(svn_client__shelf2_test_apply_file(&conflict, shelf_version, path,
+ scratch_pool));
+ if (conflict)
+ {
+ char *to_wc_abspath
+ = svn_dirent_join(shelf_version->shelf->wc_root_abspath, path,
+ scratch_pool);
+ svn_wc_status3_t *status;
+
+ SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, to_wc_abspath,
+ scratch_pool, scratch_pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Shelved path '%s' already has "
+ "status '%c' in the working copy"),
+ path, status_to_char(status->node_status));
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+/** Restore/unshelve a given or newest version of changes.
+ *
+ * Restore local modifications from shelf @a name version @a arg,
+ * or the newest version is @a arg is null.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * Error if any path would have a conflict, unless @a force_if_conflict.
+ */
+static svn_error_t *
+shelf_restore(const char *name,
+ const char *arg,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_boolean_t force_if_conflict,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ int version, old_version;
+ apr_time_t time_now = apr_time_now();
+ svn_client__shelf2_t *shelf;
+ svn_client__shelf2_version_t *shelf_version;
+
+ SVN_ERR(svn_client__shelf2_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+
+ old_version = shelf->max_version;
+ if (arg)
+ {
+ SVN_ERR(svn_cstring_atoi(&version, arg));
+ SVN_ERR(svn_client__shelf2_version_open(&shelf_version,
+ shelf, version,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ version = shelf->max_version;
+ SVN_ERR(get_newest_version_existing(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ }
+
+ if (! quiet)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("--- Unshelve '%s' in WC root '%s'\n"),
+ shelf->name, shelf->wc_root_abspath));
+ SVN_ERR(stats(shelf, version, shelf_version, time_now,
+ TRUE /*with_logmsg*/, scratch_pool));
+ }
+ if (! force_if_conflict)
+ {
+ SVN_ERR_W(test_apply(shelf_version, ctx, scratch_pool),
+ _("Cannot unshelve/restore, as at least one shelved "
+ "path would conflict with a local modification "
+ "or other status in the working copy"));
+ }
+
+ SVN_ERR(svn_client__shelf2_apply(shelf_version,
+ dry_run, scratch_pool));
+
+ if (! dry_run)
+ {
+ SVN_ERR(svn_client__shelf2_delete_newer_versions(shelf, shelf_version,
+ scratch_pool));
+ }
+
+ if (!quiet)
+ {
+ if (version < old_version)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ Q_("restored '%s' version %d and deleted %d newer version\n",
+ "restored '%s' version %d and deleted %d newer versions\n",
+ old_version - version),
+ name, version, old_version - version));
+ else
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("restored '%s' version %d (the newest version)\n"),
+ name, version));
+ }
+
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+shelf_diff(const char *name,
+ const char *arg,
+ const char *local_abspath,
+ svn_boolean_t summarize,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__shelf2_t *shelf;
+ svn_client__shelf2_version_t *shelf_version;
+ svn_stream_t *stream, *errstream;
+ svn_diff_tree_processor_t *diff_processor;
+
+ SVN_ERR(svn_client__shelf2_open_existing(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+
+ if (arg)
+ {
+ int version;
+
+ SVN_ERR(svn_cstring_atoi(&version, arg));
+ SVN_ERR(svn_client__shelf2_version_open(&shelf_version,
+ shelf, version,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(get_newest_version_existing(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ }
+
+ SVN_ERR(svn_stream_for_stdout(&stream, scratch_pool));
+ errstream = svn_stream_empty(scratch_pool);
+
+ if (summarize)
+ {
+ svn_client_diff_summarize_func_t func;
+ void *baton;
+
+ SVN_ERR(svn_cl__get_diff_summary_writer(&func, &baton,
+ FALSE /*xml*/,
+ FALSE /*ignore_properties*/,
+ "" /*anchor/prefix*/,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(&diff_processor,
+ func, baton,
+ scratch_pool,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(svn_client__get_diff_writer_svn(
+ &diff_processor,
+ NULL /*anchor*/,
+ "", "", /*orig_path_1, orig_path_2,*/
+ NULL /*options*/,
+ "" /*relative_to_dir*/,
+ FALSE /*no_diff_added*/,
+ FALSE /*no_diff_deleted*/,
+ FALSE /*show_copies_as_adds*/,
+ FALSE /*ignore_content_type*/,
+ FALSE /*ignore_properties*/,
+ FALSE /*properties_only*/,
+ TRUE /*pretty_print_mergeinfo*/,
+ svn_cmdline_output_encoding(scratch_pool),
+ stream, errstream,
+ ctx, scratch_pool));
+ }
+
+ SVN_ERR(svn_client__shelf2_diff(shelf_version, "",
+ depth, ignore_ancestry,
+ diff_processor, scratch_pool));
+ SVN_ERR(svn_stream_close(stream));
+
+ SVN_ERR(svn_client__shelf2_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+shelf_drop(const char *name,
+ const char *local_abspath,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_client__shelf2_delete(name, local_abspath, dry_run,
+ ctx, scratch_pool));
+ if (! quiet)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("deleted '%s'\n"),
+ name));
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+shelf_shelve(int *new_version,
+ const char *name,
+ apr_array_header_t *targets,
+ svn_depth_t depth,
+ apr_array_header_t *changelists,
+ apr_hash_t *revprop_table,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_abspath;
+
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
+
+ svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+ /* ### TODO: check all paths are in same WC; for now use first path */
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath,
+ APR_ARRAY_IDX(targets, 0, char *),
+ scratch_pool));
+
+ SVN_ERR(shelve(new_version, name,
+ targets, depth, changelists,
+ revprop_table,
+ keep_local, dry_run, quiet,
+ local_abspath, ctx, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+svn_cl__shelf_shelve(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool);
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_save(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;
+
+ opt_state->keep_local = TRUE;
+ SVN_ERR(svn_cl__shelf_shelve(os, baton, pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_shelve(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 *name;
+ apr_array_header_t *targets;
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(get_next_argument(&name, os, pool, 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));
+ {
+ int new_version;
+ svn_error_t *err;
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
+ opt_state, NULL, ctx->config,
+ pool));
+ err = shelf_shelve(&new_version, name,
+ targets, opt_state->depth, opt_state->changelists,
+ opt_state->revprop_table,
+ opt_state->keep_local, opt_state->dry_run,
+ opt_state->quiet, ctx, pool);
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
+ err, pool));
+ else
+ SVN_ERR(err);
+
+ if (! opt_state->quiet)
+ {
+ if (opt_state->keep_local)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("saved '%s' version %d\n"),
+ name, new_version));
+ else
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("shelved '%s' version %d\n"),
+ name, new_version));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_unshelve(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 *local_abspath;
+ const char *name;
+ const char *arg = NULL;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", scratch_pool));
+
+ if (os->ind < os->argc)
+ {
+ SVN_ERR(get_next_argument(&name, os, scratch_pool, scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(name_of_youngest(&name,
+ local_abspath, ctx, scratch_pool, scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("unshelving the youngest shelf, '%s'\n"),
+ name));
+ }
+
+ /* Which checkpoint number? */
+ if (os->ind < os->argc)
+ SVN_ERR(get_next_argument(&arg, os, scratch_pool, scratch_pool));
+
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(shelf_restore(name, arg,
+ opt_state->dry_run, opt_state->quiet,
+ opt_state->force /*force_already_modified*/,
+ local_abspath, ctx, scratch_pool));
+
+ if (opt_state->drop)
+ {
+ SVN_ERR(shelf_drop(name, local_abspath,
+ opt_state->dry_run, opt_state->quiet,
+ ctx, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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 = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ 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);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+
+ SVN_ERR(shelves_list(local_abspath,
+ opt_state->quiet,
+ ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* "svn shelf-list-by-paths [PATH...]"
+ *
+ * TARGET_RELPATHS are all within the same WC, relative to WC_ROOT_ABSPATH.
+ */
+static svn_error_t *
+shelf_list_by_paths(apr_array_header_t *target_relpaths,
+ const char *wc_root_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *shelves;
+ apr_hash_t *paths_to_shelf_name = apr_hash_make(scratch_pool);
+ apr_array_header_t *array;
+ int i;
+
+ SVN_ERR(list_sorted_by_date(&shelves,
+ wc_root_abspath, ctx, scratch_pool));
+
+ /* Check paths are valid */
+ for (i = 0; i < target_relpaths->nelts; i++)
+ {
+ char *target_relpath = APR_ARRAY_IDX(target_relpaths, i, char *);
+
+ if (svn_path_is_url(target_relpath))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), target_relpath);
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(target_relpath));
+ }
+
+ /* Find the most recent shelf for each affected path */
+ for (i = 0; i < shelves->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(shelves, i, svn_sort__item_t);
+ const char *name = item->key;
+ svn_client__shelf2_t *shelf;
+ svn_client__shelf2_version_t *shelf_version;
+ apr_hash_t *shelf_paths;
+ int j;
+
+ SVN_ERR(svn_client__shelf2_open_existing(&shelf,
+ name, wc_root_abspath,
+ ctx, scratch_pool));
+ SVN_ERR(svn_client__shelf2_get_newest_version(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
+ if (!shelf_version)
+ continue;
+ SVN_ERR(svn_client__shelf2_paths_changed(&shelf_paths,
+ shelf_version,
+ scratch_pool, scratch_pool));
+ for (j = 0; j < target_relpaths->nelts; j++)
+ {
+ char *target_relpath = APR_ARRAY_IDX(target_relpaths, j, char *);
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, shelf_paths);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *shelf_path = apr_hash_this_key(hi);
+
+ if (svn_relpath_skip_ancestor(target_relpath, shelf_path))
+ {
+ if (! svn_hash_gets(paths_to_shelf_name, shelf_path))
+ {
+ svn_hash_sets(paths_to_shelf_name, shelf_path, shelf->name);
+ }
+ }
+ }
+ }
+ }
+
+ /* Print the results. */
+ array = svn_sort__hash(paths_to_shelf_name,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+ for (i = 0; i < array->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
+ const char *path = item->key;
+ const char *name = item->value;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "%-20.20s %s\n",
+ name,
+ svn_dirent_local_style(path, scratch_pool)));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_list_by_paths(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 *wc_root_abspath;
+ apr_array_header_t *targets;
+
+ /* Parse the remaining arguments as paths. */
+ SVN_ERR(targets_relative_to_a_wc(&wc_root_abspath, &targets,
+ os, opt_state->targets,
+ ctx, pool, pool));
+
+ SVN_ERR(shelf_list_by_paths(targets, wc_root_abspath, ctx, pool));
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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;
+ const char *local_abspath;
+ const char *name;
+ const char *arg = NULL;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ /* Which checkpoint number? */
+ if (os->ind < os->argc)
+ SVN_ERR(get_next_argument(&arg, os, pool, pool));
+
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ SVN_ERR(shelf_diff(name, arg, local_abspath,
+ opt_state->diff.summarize,
+ opt_state->depth, opt_state->ignore_ancestry,
+ ctx, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_drop(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 *name;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ int i;
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+ SVN_ERR(shelf_drop(name, local_abspath,
+ opt_state->dry_run, opt_state->quiet,
+ ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+static svn_error_t *
+svn_cl__shelf_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;
+ const char *name;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ int i;
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *local_abspath;
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+ SVN_ERR(shelf_log(name, local_abspath, ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+const svn_opt_subcommand_desc3_t
+svn_cl__cmd_table_shelf2[] =
+{
+ { "x-shelf-diff", svn_cl__shelf_diff, {0}, {N_(
+ "Show shelved changes as a diff.\n"
+ "usage: x-shelf-diff SHELF [VERSION]\n"
+ "\n"), N_(
+ " Show the changes in SHELF:VERSION (default: latest) as a diff.\n"
+ "\n"), N_(
+ " See also: 'svn diff --cl=svn:shelf:SHELF' which supports most options of\n"
+ " 'svn diff'.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {opt_summarize},
+ },
+
+ { "x-shelf-drop", svn_cl__shelf_drop, {0}, {N_(
+ "Delete a shelf.\n"
+ "usage: x-shelf-drop SHELF [PATH ...]\n"
+ "\n"), N_(
+ " Delete the shelves named SHELF from the working copies containing PATH\n"
+ " (default PATH is '.')\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ },
+
+ { "x-shelf-list", svn_cl__shelf_list, {"x-shelves"}, {N_(
+ "List shelves.\n"
+ "usage: x-shelf-list [PATH ...]\n"
+ "\n"), N_(
+ " List shelves for each working copy containing PATH (default is '.')\n"
+ " Include the first line of any log message and some details about the\n"
+ " contents of the shelf, unless '-q' is given.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', 'v'}
+ },
+
+ { "x-shelf-list-by-paths", svn_cl__shelf_list_by_paths, {0}, {N_(
+ "List which shelf affects each path.\n"
+ "usage: x-shelf-list-by-paths [PATH...]\n"
+ "\n"), N_(
+ " List which shelf most recently affects each path below the given PATHs.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ },
+
+ { "x-shelf-log", svn_cl__shelf_log, {0}, {N_(
+ "Show the versions of a shelf.\n"
+ "usage: x-shelf-log SHELF [PATH...]\n"
+ "\n"), N_(
+ " Show all versions of SHELF for each working copy containing PATH (the\n"
+ " default PATH is '.').\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', 'v'}
+ },
+
+ { "x-shelf-save", svn_cl__shelf_save, {0}, {N_(
+ "Copy local changes onto a new version of a shelf.\n"
+ "usage: x-shelf-save SHELF [PATH...]\n"
+ "\n"), N_(
+ " Save local changes in the given PATHs as a new version of SHELF.\n"
+ " The shelf's log message can be set with -m, -F, etc.\n"
+ "\n"), N_(
+ " The same as 'svn shelve --keep-local'.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', opt_dry_run,
+ opt_depth, opt_targets, opt_changelist,
+ SVN_CL__LOG_MSG_OPTIONS,
+ }
+ },
+
+ { "x-shelve", svn_cl__shelf_shelve, {0}, {N_(
+ "Move local changes onto a shelf.\n"
+ "usage: x-shelve [--keep-local] SHELF [PATH...]\n"
+ "\n"), N_(
+ " Save the local changes in the given PATHs to a new or existing SHELF.\n"
+ " Revert those changes from the WC unless '--keep-local' is given.\n"
+ " The shelf's log message can be set with -m, -F, etc.\n"
+ "\n"), N_(
+ " 'svn shelve --keep-local' is the same as 'svn shelf-save'.\n"
+ "\n"), N_(
+ " The kinds of change you can shelve are committable changes to files and\n"
+ " properties, except the following kinds which are not yet supported:\n"
+ " * copies and moves\n"
+ " * mkdir and rmdir\n"
+ " Uncommittable states such as conflicts, unversioned and missing cannot\n"
+ " be shelved.\n"
+ "\n"), N_(
+ " To bring back shelved changes, use 'svn unshelve SHELF'.\n"
+ "\n"), N_(
+ " Shelves are currently stored under <WC>/.svn/experimental/shelves/ .\n"
+ " (In Subversion 1.10, shelves were stored under <WC>/.svn/shelves/ as\n"
+ " patch files. To recover a shelf created by 1.10, either use a 1.10\n"
+ " client to find and unshelve it, or find the patch file and use any\n"
+ " 1.10 or later 'svn patch' to apply it.)\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {'q', opt_dry_run, opt_keep_local,
+ opt_depth, opt_targets, opt_changelist,
+ SVN_CL__LOG_MSG_OPTIONS,
+ } },
+
+ { "x-unshelve", svn_cl__shelf_unshelve, {0}, {N_(
+ "Copy shelved changes back into the WC.\n"
+ "usage: x-unshelve [--drop] [SHELF [VERSION]]\n"
+ "\n"), N_(
+ " Apply the changes stored in SHELF to the working copy.\n"
+ " SHELF defaults to the newest shelf.\n"
+ "\n"), N_(
+ " Apply the newest version of the shelf, by default. If VERSION is\n"
+ " specified, apply that version and discard all versions newer than that.\n"
+ " In any case, retain the unshelved version and versions older than that\n"
+ " (unless --drop is specified).\n"
+ "\n"), N_(
+ " With --drop, delete the entire shelf (like 'svn shelf-drop') after\n"
+ " successfully unshelving with no conflicts.\n"
+ "\n"), N_(
+ " The working files involved should be in a clean, unmodified state\n"
+ " before using this command. To roll back to an older version of the\n"
+ " shelf, first ensure any current working changes are removed, such as\n"
+ " by shelving or reverting them, and then unshelve the desired version.\n"
+ "\n"), N_(
+ " Unshelve normally refuses to apply any changes if any path involved is\n"
+ " already modified (or has any other abnormal status) in the WC. With\n"
+ " --force, it does not check and may error out and/or produce partial or\n"
+ " unexpected results.\n"
+ "\n"), N_(
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ )},
+ {opt_drop, 'q', opt_dry_run, opt_force} },
+
+ { NULL, NULL, {0}, {NULL}, {0} }
+};
+
diff --git a/subversion/svn/shelf2-cmd.h b/subversion/svn/shelf2-cmd.h
new file mode 100644
index 0000000000000..b058073cce7b1
--- /dev/null
+++ b/subversion/svn/shelf2-cmd.h
@@ -0,0 +1,49 @@
+/*
+ * shelf2-cmd.h: experimental shelving v2
+ *
+ * ====================================================================
+ * 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_SHELF2_CMD_H
+#define SVN_SHELF2_CMD_H
+
+/*** Includes. ***/
+#include <apr_getopt.h>
+
+#include "svn_opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+extern const svn_opt_subcommand_desc3_t svn_cl__cmd_table_shelf2[];
+/*extern const apr_getopt_option_t svn_cl__options_shelving2[];*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_SHELF2_CMD_H */
diff --git a/subversion/svn/shelve-cmd.c b/subversion/svn/shelve-cmd.c
deleted file mode 100644
index df88a651d9c25..0000000000000
--- a/subversion/svn/shelve-cmd.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * shelve-cmd.c -- Shelve 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.
- * ====================================================================
- */
-
-/* We define this here to remove any further warnings about the usage of
- experimental functions in this file. */
-#define SVN_EXPERIMENTAL
-
-#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"
-#include "private/svn_sorts_private.h"
-
-
-/* First argument should be the name of a shelved change. */
-static svn_error_t *
-get_name(const char **name,
- apr_getopt_t *os,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_array_header_t *args;
-
- SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
- SVN_ERR(svn_utf_cstring_to_utf8(name,
- APR_ARRAY_IDX(args, 0, const char *),
- result_pool));
- return SVN_NO_ERROR;
-}
-
-/* A comparison function for svn_sort__hash(), comparing the mtime of two
- svn_client_shelved_patch_info_t's. */
-static int
-compare_shelved_patch_infos_by_mtime(const svn_sort__item_t *a,
- const svn_sort__item_t *b)
-{
- svn_client_shelved_patch_info_t *a_val = a->value;
- svn_client_shelved_patch_info_t *b_val = b->value;
-
- return (a_val->dirent->mtime < b_val->dirent->mtime)
- ? -1 : (a_val->dirent->mtime > b_val->dirent->mtime) ? 1 : 0;
-}
-
-/* Return a list of shelved changes sorted by patch file mtime, oldest first.
- */
-static svn_error_t *
-list_sorted_by_date(apr_array_header_t **list,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *shelved_patch_infos;
-
- SVN_ERR(svn_client_shelves_list(&shelved_patch_infos, local_abspath,
- ctx, scratch_pool, scratch_pool));
- *list = svn_sort__hash(shelved_patch_infos,
- compare_shelved_patch_infos_by_mtime,
- scratch_pool);
- return SVN_NO_ERROR;
-}
-
-#ifndef WIN32
-/* Run CMD with ARGS.
- * Send its stdout to the parent's stdout. Disconnect its stdin and stderr.
- */
-static svn_error_t *
-run_cmd(const char *cmd,
- const char *const *args,
- apr_pool_t *scratch_pool)
-{
- apr_status_t apr_err;
- apr_file_t *outfile;
- svn_error_t *err;
- int exitcode;
-
- apr_err = apr_file_open_stdout(&outfile, scratch_pool);
- if (apr_err)
- return svn_error_wrap_apr(apr_err, "Can't open stdout");
-
- err = svn_io_run_cmd(NULL /*path*/, cmd, args,
- &exitcode, NULL /*exitwhy*/,
- TRUE /*inherit*/,
- NULL /*infile*/, outfile, NULL /*errfile*/,
- scratch_pool);
- if (err || exitcode)
- return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, err,
- _("Could not run external command '%s'"), cmd);
- return SVN_NO_ERROR;
-}
-#endif
-
-/* Display a list of shelved changes */
-static svn_error_t *
-shelves_list(const char *local_abspath,
- svn_boolean_t diffstat,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- apr_array_header_t *list;
- int i;
-
- SVN_ERR(list_sorted_by_date(&list,
- local_abspath, ctx, scratch_pool));
-
- for (i = 0; i < list->nelts; i++)
- {
- const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
- const char *name = item->key;
- svn_client_shelved_patch_info_t *info = item->value;
- int age = (int)((apr_time_now() - info->mtime) / 1000000 / 60);
- apr_hash_t *paths;
-
- SVN_ERR(svn_client_shelf_get_paths(&paths,
- name, local_abspath, ctx,
- scratch_pool, scratch_pool));
-
- SVN_ERR(svn_cmdline_printf(scratch_pool,
- _("%-30s %6d mins old %10ld bytes %4d paths changed\n"),
- name, age, (long)info->dirent->filesize,
- apr_hash_count(paths)));
- SVN_ERR(svn_cmdline_printf(scratch_pool,
- _(" %.50s\n"),
- info->message));
-
- if (diffstat)
- {
-#ifndef WIN32
- const char *args[4];
- svn_error_t *err;
-
- args[0] = "diffstat";
- args[1] = "-p0";
- args[2] = info->patch_path;
- args[3] = NULL;
- err = run_cmd("diffstat", args, scratch_pool);
- if (err)
- svn_error_clear(err);
- else
- SVN_ERR(svn_cmdline_printf(scratch_pool,
- "\n"));
-#endif
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Find the name of the youngest shelved change.
- */
-static svn_error_t *
-name_of_youngest(const char **name_p,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_array_header_t *list;
- const svn_sort__item_t *youngest_item;
-
- SVN_ERR(list_sorted_by_date(&list,
- local_abspath, ctx, scratch_pool));
- if (list->nelts == 0)
- return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
- _("No shelved changes found"));
-
- youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
- *name_p = youngest_item->key;
- return SVN_NO_ERROR;
-}
-
-/* This implements the `svn_opt_subcommand_t' interface. */
-svn_error_t *
-svn_cl__shelve(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 *local_abspath;
- const char *name;
- apr_array_header_t *targets;
- svn_boolean_t has_changes;
-
- if (opt_state->quiet)
- ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
-
- if (opt_state->list)
- {
- if (os->ind < os->argc)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
-
- SVN_ERR(shelves_list(local_abspath,
- ! opt_state->quiet /*diffstat*/,
- ctx, pool));
- return SVN_NO_ERROR;
- }
-
- SVN_ERR(get_name(&name, os, pool, pool));
-
- if (opt_state->remove)
- {
- if (os->ind < os->argc)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
-
- SVN_ERR(svn_client_shelves_delete(name, local_abspath,
- opt_state->dry_run,
- ctx, pool));
- if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "deleted '%s'\n", name));
- return SVN_NO_ERROR;
- }
-
- /* Parse the remaining arguments as paths. */
- SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
- opt_state->targets,
- ctx, FALSE, pool));
- svn_opt_push_implicit_dot_target(targets, pool);
-
- {
- svn_depth_t depth = opt_state->depth;
- svn_error_t *err;
-
- /* shelve 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 (depth == svn_depth_unknown)
- depth = svn_depth_infinity;
-
- SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
-
- 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_shelve(name,
- targets, depth, opt_state->changelists,
- opt_state->keep_local, opt_state->dry_run,
- ctx, pool);
- if (ctx->log_msg_func3)
- SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
- err, pool));
- else
- SVN_ERR(err);
- }
-
- /* If no modifications were shelved, throw an error. */
- SVN_ERR(svn_client_shelf_has_changes(&has_changes,
- name, local_abspath, ctx, pool));
- if (! has_changes)
- {
- SVN_ERR(svn_client_shelves_delete(name, local_abspath,
- opt_state->dry_run, ctx, pool));
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("No changes were shelved"));
- }
-
- if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "shelved '%s'\n", name));
-
- return SVN_NO_ERROR;
-}
-
-/* This implements the `svn_opt_subcommand_t' interface. */
-svn_error_t *
-svn_cl__unshelve(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 *local_abspath;
- const char *name;
- apr_array_header_t *targets;
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
-
- if (opt_state->list)
- {
- if (os->ind < os->argc)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
-
- SVN_ERR(shelves_list(local_abspath,
- ! opt_state->quiet /*diffstat*/,
- ctx, pool));
- return SVN_NO_ERROR;
- }
-
- if (os->ind < os->argc)
- {
- SVN_ERR(get_name(&name, os, pool, pool));
- }
- else
- {
- SVN_ERR(name_of_youngest(&name, local_abspath, ctx, pool, pool));
- SVN_ERR(svn_cmdline_printf(pool,
- _("unshelving the youngest change, '%s'\n"),
- name));
- }
-
- /* There should be no remaining arguments. */
- 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_ARG_PARSING_ERROR, 0, NULL);
-
- if (opt_state->quiet)
- ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
-
- SVN_ERR(svn_client_unshelve(name, local_abspath,
- opt_state->keep_local, opt_state->dry_run,
- ctx, pool));
- if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "unshelved '%s'\n", name));
-
- return SVN_NO_ERROR;
-}
-
-/* This implements the `svn_opt_subcommand_t' interface. */
-svn_error_t *
-svn_cl__shelves(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 *local_abspath;
-
- /* There should be no remaining arguments. */
- if (os->ind < os->argc)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
- SVN_ERR(shelves_list(local_abspath, ! opt_state->quiet /*diffstat*/,
- ctx, pool));
-
- return SVN_NO_ERROR;
-}
diff --git a/subversion/svn/svn.c b/subversion/svn/svn.c
index 48b894f499831..3c3ac6d5068e1 100644
--- a/subversion/svn/svn.c
+++ b/subversion/svn/svn.c
@@ -52,6 +52,8 @@
#include "svn_hash.h"
#include "svn_version.h"
#include "cl.h"
+#include "shelf2-cmd.h"
+#include "shelf-cmd.h"
#include "private/svn_opt_private.h"
#include "private/svn_cmdline_private.h"
@@ -63,95 +65,6 @@
/*** 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_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
- opt_auth_password_from_stdin,
- opt_auth_username,
- opt_autoprops,
- opt_changelist,
- opt_config_dir,
- opt_config_options,
- /* diff options */
- opt_diff_cmd,
- opt_internal_diff,
- opt_no_diff_added,
- opt_no_diff_deleted,
- opt_show_copies_as_adds,
- opt_notice_ancestry,
- opt_summarize,
- opt_use_git_diff_format,
- opt_ignore_properties,
- opt_properties_only,
- opt_patch_compatible,
- /* end of diff options */
- 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_ignore,
- opt_no_unlock,
- opt_non_interactive,
- opt_force_interactive,
- opt_old_cmd,
- opt_record_only,
- opt_relocate,
- opt_remove,
- opt_revprop,
- opt_stop_on_copy,
- opt_strict, /* ### DEPRECATED */
- 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_trust_server_cert_failures,
- opt_strip,
- opt_ignore_keywords,
- opt_reverse_diff,
- opt_ignore_whitespace,
- opt_diff,
- opt_allow_mixed_revisions,
- opt_include_externals,
- opt_show_inherited_props,
- opt_search,
- opt_search_and,
- opt_mergeinfo_log,
- opt_remove_unversioned,
- opt_remove_ignored,
- opt_no_newline,
- opt_show_passwords,
- opt_pin_externals,
- opt_show_item,
- opt_adds_as_modification,
- opt_vacuum_pristines,
- opt_delete,
- opt_keep_shelved,
- opt_list
-} svn_cl__longopt_t;
-
-
/* Option codes and descriptions for the command line client.
*
* The entire list must be terminated with an entry of nulls.
@@ -166,7 +79,8 @@ const apr_getopt_option_t svn_cl__options[] =
{"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")},
+ {"non-recursive", 'N', 0, N_("obsolete")},
+ {"human-readable",'H', 0, N_("show human-readable output")},
{"change", 'c', 1,
N_("the change made by revision ARG (like -r ARG-1:ARG)\n"
" "
@@ -421,6 +335,8 @@ const apr_getopt_option_t svn_cl__options[] =
{"remove-unversioned", opt_remove_unversioned, 0,
N_("remove unversioned items")},
{"remove-ignored", opt_remove_ignored, 0, N_("remove ignored items")},
+ {"remove-added", opt_remove_added, 0,
+ N_("reverting an added item will remove it from disk")},
{"no-newline", opt_no_newline, 0, N_("do not output the trailing newline")},
{"show-passwords", opt_show_passwords, 0, N_("show cached passwords")},
{"pin-externals", opt_pin_externals, 0,
@@ -444,6 +360,10 @@ const apr_getopt_option_t svn_cl__options[] =
" "
" 'repos-uuid' UUID of repository\n"
" "
+ " 'repos-size' for files, the size of TARGET\n"
+ " "
+ " in the repository\n"
+ " "
" 'revision' specified or implied revision\n"
" "
" 'last-changed-revision'\n"
@@ -460,7 +380,13 @@ const apr_getopt_option_t svn_cl__options[] =
" "
" author of 'last-changed-revision'\n"
" "
- " 'wc-root' root of TARGET's working copy")},
+ " 'wc-root' root of TARGET's working copy\n"
+ " "
+ " 'schedule' 'normal','add','delete','replace'\n"
+ " "
+ " 'depth' checkout depth of TARGET in WC\n"
+ " "
+ " 'changelist' changelist of TARGET in WC")},
{"adds-as-modification", opt_adds_as_modification, 0,
N_("Local additions are merged with incoming additions\n"
@@ -474,9 +400,13 @@ const apr_getopt_option_t svn_cl__options[] =
{"vacuum-pristines", opt_vacuum_pristines, 0,
N_("remove unreferenced pristines from .svn directory")},
- {"list", opt_list, 0, N_("list shelved patches")},
- {"keep-shelved", opt_keep_shelved, 0, N_("do not delete the shelved patch")},
- {"delete", opt_delete, 0, N_("delete the shelved patch")},
+ {"drop", opt_drop, 0,
+ N_("drop shelf after successful unshelve")},
+
+ {"x-viewspec", opt_viewspec, 1,
+ N_("print the working copy layout, formatted according\n"
+ " "
+ "to ARG: 'classic' or 'svn11'")},
/* Long-opt Aliases
*
@@ -515,100 +445,120 @@ const int svn_cl__global_options[] =
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[] =
+static const svn_opt_subcommand_desc3_t
+svn_cl__cmd_table_main[] =
{
- { "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"),
+ { "add", svn_cl__add, {0}, {N_(
+ "Put new files and directories under version control.\n"
+ "usage: add PATH...\n"
+ "\n"), N_(
+ " Schedule unversioned PATHs for addition, so they will become versioned and\n"
+ " be added to the repository in the next commit. Recurse into directories by\n"
+ " default (see the --depth option).\n"
+ "\n"), N_(
+ " The 'svn add' command is only necessary for files and directories that are\n"
+ " not yet under version control. Unversioned files and directories can be\n"
+ " identified with 'svn status' (see 'svn help status').\n"
+ "\n"), N_(
+ " The effects of 'svn add' can be undone with 'svn revert' before the addition\n"
+ " has been committed. Once committed, a path can be removed from version\n"
+ " control with 'svn delete', and in some circumstances by running a reverse-\n"
+ " merge (see 'svn help merge' for details).\n"
+ "\n"), N_(
+ " With --force, add all the unversioned paths found in PATHs and ignore the\n"
+ " rest; otherwise, error out if any specified paths are already versioned.\n"
+ "\n"), N_(
+ " The selection of items to add may be influenced by the 'ignores' feature.\n"
+ " Properties may be attached to the items as configured by the 'auto-props'\n"
+ " feature.\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")}} },
-
- { "auth", svn_cl__auth, {0}, N_
- ("Manage cached authentication credentials.\n"
- "usage: 1. svn auth [PATTERN ...]\n"
- "usage: 2. svn auth --remove PATTERN [PATTERN ...]\n"
- "\n"
- " With no arguments, list all cached authentication credentials.\n"
- " Authentication credentials include usernames, passwords,\n"
- " SSL certificates, and SSL client-certificate passphrases.\n"
- " If PATTERN is specified, only list credentials with attributes matching one\n"
- " or more patterns. With the --remove option, remove cached authentication\n"
- " credentials matching one or more patterns.\n"
- "\n"
- " If more than one pattern is specified credentials are considered only if they\n"
- " match all specified patterns. Patterns are matched case-sensitively and may\n"
- " contain glob wildcards:\n"
- " ? matches any single character\n"
- " * matches a sequence of arbitrary characters\n"
- " [abc] matches any of the characters listed inside the brackets\n"
- " Note that wildcards will usually need to be quoted or escaped on the\n"
- " command line because many command shells will interfere by trying to\n"
- " expand them.\n"),
+ {{opt_parents, N_("add intermediate parents")},
+ {'N', N_("obsolete; same as --depth=empty")},
+ {opt_force, N_("ignore already versioned paths")}} },
+
+ { "auth", svn_cl__auth, {0}, {N_(
+ "Manage cached authentication credentials.\n"
+ "usage: 1. svn auth [PATTERN ...]\n"
+ " 2. svn auth --remove PATTERN [PATTERN ...]\n"
+ "\n"), N_(
+ " With no arguments, list all cached authentication credentials.\n"
+ " Authentication credentials include usernames, passwords,\n"
+ " SSL certificates, and SSL client-certificate passphrases.\n"
+ " If PATTERN is specified, only list credentials with attributes matching one\n"
+ " or more patterns. With the --remove option, remove cached authentication\n"
+ " credentials matching one or more patterns.\n"
+ "\n"), N_(
+ " If more than one pattern is specified credentials are considered only if they\n"
+ " match all specified patterns. Patterns are matched case-sensitively and may\n"
+ " contain glob wildcards:\n"
+ " ? matches any single character\n"
+ " * matches a sequence of arbitrary characters\n"
+ " [abc] matches any of the characters listed inside the brackets\n"
+ " Note that wildcards will usually need to be quoted or escaped on the\n"
+ " command line because many command shells will interfere by trying to\n"
+ " expand them.\n"
+ )},
{ opt_remove, opt_show_passwords },
{ {opt_remove, N_("remove matching authentication credentials")} }
},
- { "blame", svn_cl__blame, {"praise", "annotate", "ann"}, N_
- ("Show when each line of a file was last (or\n"
+ { "blame", svn_cl__blame, {"praise", "annotate", "ann"}, {N_(
+ "Show when each line of a file was last (or\n"
"next) changed.\n"
"usage: blame [-rM:N] TARGET[@REV]...\n"
- "\n"
+ "\n"), N_(
" Annotate each line of a file with the revision number and author of the\n"
" last change (or optionally the next change) to that line.\n"
- "\n"
+ "\n"), N_(
" With no revision range (same as -r0:REV), or with '-r M:N' where M < N,\n"
" annotate each line that is present in revision N of the file, with\n"
" the last revision at or before rN that changed or added the line,\n"
" looking back no further than rM.\n"
- "\n"
+ "\n"), N_(
" With a reverse revision range '-r M:N' where M > N,\n"
" annotate each line that is present in revision N of the file, with\n"
" the next revision after rN that changed or deleted the line,\n"
" looking forward no further than rM.\n"
- "\n"
+ "\n"), N_(
" If specified, REV determines in which revision the target is first\n"
" looked up.\n"
- "\n"
- " Write the annotated result to standard output.\n"),
+ "\n"), N_(
+ " Write the annotated result to standard output.\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"
+ { "cat", svn_cl__cat, {0}, {N_(
+ "Output the content of specified files or URLs.\n"
"usage: cat TARGET[@REV]...\n"
- "\n"
+ "\n"), N_(
" If specified, REV determines in which revision the target is first\n"
- " looked up.\n"),
+ " looked up.\n"
+ )},
{'r', opt_ignore_keywords} },
- { "changelist", svn_cl__changelist, {"cl"}, N_
- ("Associate (or dissociate) changelist CLNAME with the named files.\n"
+ { "changelist", svn_cl__changelist, {"cl"}, {N_(
+ "Associate (or dissociate) changelist CLNAME with the named\n"
+ "files.\n"
"usage: 1. changelist CLNAME PATH...\n"
- " 2. changelist --remove 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"
+ { "checkout", svn_cl__checkout, {"co"}, {N_(
+ "Check out a working copy from a repository.\n"
"usage: checkout URL[@REV]... [PATH]\n"
- "\n"
+ "\n"), N_(
" If specified, REV determines in which revision the URL is first\n"
" looked up.\n"
- "\n"
+ "\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"
+ "\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"
@@ -619,62 +569,67 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\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} },
+ " reporting the action taken.\n"
+ )},
+ {'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals},
+ {{'N', N_("obsolete; same as --depth=files")}} },
- { "cleanup", svn_cl__cleanup, {0}, N_
- ("Either recover from an interrupted operation that left the working copy locked,\n"
- "or remove unwanted files.\n"
+ { "cleanup", svn_cl__cleanup, {0}, {N_(
+ "Either recover from an interrupted operation that left the working\n"
+ "copy locked, or remove unwanted files.\n"
"usage: 1. cleanup [WCPATH...]\n"
" 2. cleanup --remove-unversioned [WCPATH...]\n"
" cleanup --remove-ignored [WCPATH...]\n"
" 3. cleanup --vacuum-pristines [WCPATH...]\n"
- "\n"
+ "\n"), N_(
" 1. When none of the options --remove-unversioned, --remove-ignored, and\n"
" --vacuum-pristines is specified, remove all write locks (shown as 'L' by\n"
" the 'svn status' command) from the working copy. Usually, this is only\n"
" necessary if a Subversion client has crashed while using the working copy,\n"
" leaving it in an unusable state.\n"
- "\n"
+ "\n"), N_(
" WARNING: There is no mechanism that will protect write locks still\n"
" being used by other Subversion clients. Running this command\n"
" without any options while another client is using the working\n"
" copy can corrupt the working copy beyond repair!\n"
- "\n"
+ "\n"), N_(
" 2. If the --remove-unversioned option or the --remove-ignored option\n"
" is given, remove any unversioned or ignored items within WCPATH.\n"
" Note that the 'svn status' command shows unversioned items as '?',\n"
" and ignored items as 'I' if the --no-ignore option is given to it.\n"
- "\n"
+ "\n"), N_(
" 3. If the --vacuum-pristines option is given, remove pristine copies of\n"
" files which are stored inside the .svn directory and which are no longer\n"
- " referenced by any file in the working copy.\n"),
+ " referenced by any file in the working copy.\n"
+ )},
{ opt_remove_unversioned, opt_remove_ignored, opt_vacuum_pristines,
- opt_include_externals, 'q', opt_merge_cmd },
+ opt_include_externals, 'q', opt_merge_cmd },
{ { opt_merge_cmd, N_("deprecated and ignored") } } },
-
- { "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"
- "\n"
- " If any targets are (or contain) locked items, those will be\n"
- " unlocked after a successful commit, unless --no-unlock is given.\n"
- "\n"
- " If --include-externals is given, also commit file and directory\n"
- " externals reached by recursion. Do not commit externals with a\n"
- " fixed revision.\n"),
+
+ { "commit", svn_cl__commit, {"ci"}, {N_(
+ "Send changes from your working copy to the repository.\n"
+ "usage: commit [PATH...]\n"
+ "\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"
+ "\n"), N_(
+ " If any targets are (or contain) locked items, those will be\n"
+ " unlocked after a successful commit, unless --no-unlock is given.\n"
+ "\n"), N_(
+ " If --include-externals is given, also commit file and directory\n"
+ " externals reached by recursion. Do not commit externals with a\n"
+ " fixed revision.\n"
+ )},
{'q', 'N', opt_depth, opt_targets, opt_no_unlock, SVN_CL__LOG_MSG_OPTIONS,
- opt_changelist, opt_keep_changelists, opt_include_externals} },
+ opt_changelist, opt_keep_changelists, opt_include_externals},
+ {{'N', N_("obsolete; same as --depth=empty")}} },
- { "copy", svn_cl__copy, {"cp"}, N_
- ("Copy files and directories in a working copy or repository.\n"
+ { "copy", svn_cl__copy, {"cp"}, {N_(
+ "Copy files and directories in a working copy or repository.\n"
"usage: copy SRC[@REV]... DST\n"
- "\n"
+ "\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"
@@ -683,33 +638,35 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" All the SRCs must be of the same type. If DST is an existing directory,\n"
" the sources will be added as children of DST. When copying multiple\n"
" sources, DST must be an existing directory.\n"
- "\n"
+ "\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"),
+ " to the destination.\n"
+ )},
{'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS,
opt_pin_externals} },
- { "delete", svn_cl__delete, {"del", "remove", "rm"}, N_
- ("Remove files and directories from version control.\n"
+ { "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"
+ "\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"
+ "\n"), N_(
" 2. Each item specified by a URL is deleted from the repository\n"
- " via an immediate commit.\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 local changes or differences between two revisions or paths.\n"
+ { "diff", svn_cl__diff, {"di"}, {N_(
+ "Display local changes or differences between two revisions or paths.\n"
"usage: 1. diff\n"
" 2. diff [-c M | -r N[:M]] [TARGET[@REV]...]\n"
" 3. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \\\n"
@@ -717,16 +674,16 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 4. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]\n"
" 5. diff OLD-URL[@OLDREV] NEW-PATH[@NEWREV]\n"
" 6. diff OLD-PATH[@OLDREV] NEW-URL[@NEWREV]\n"
- "\n"
+ "\n"), N_(
" 1. Use just 'svn diff' to display local modifications in a working copy.\n"
- "\n"
+ "\n"), N_(
" 2. 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"
+ "\n"), N_(
" 3. 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"
@@ -735,164 +692,178 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" to N, -r N:M makes OLDREV default to N and NEWREV default to M.\n"
" If OLDREV or NEWREV are not specified, they default to WORKING for\n"
" working copy targets and to HEAD for URL targets.\n"
- "\n"
+ "\n"), N_(
" Either or both OLD-TGT and NEW-TGT may also be paths to unversioned\n"
" targets. Revisions cannot be specified for unversioned targets.\n"
" Both targets must be of the same node kind (file or directory).\n"
" Diffing unversioned targets against URL targets is not supported.\n"
- "\n"
+ "\n"), N_(
" 4. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
" 5. Shorthand for 'svn diff --old=OLD-URL[@OLDREV] --new=NEW-PATH[@NEWREV]'\n"
- " 6. Shorthand for 'svn diff --old=OLD-PATH[@OLDREV] --new=NEW-URL[@NEWREV]'\n"),
+ " 6. Shorthand for 'svn diff --old=OLD-PATH[@OLDREV] --new=NEW-URL[@NEWREV]'\n"
+ )},
{'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd,
opt_internal_diff, 'x', opt_no_diff_added, opt_no_diff_deleted,
opt_ignore_properties, opt_properties_only,
opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize, opt_changelist,
- opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible} },
- { "export", svn_cl__export, {0}, N_
- ("Create an unversioned copy of a tree.\n"
+ opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible},
+ {{'N', N_("obsolete; same as --depth=files")}} },
+
+ { "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"
+ "\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"
+ "\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"
+ "\n"), N_(
" If specified, PEGREV determines in which revision the target is first\n"
- " looked up.\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} },
+ opt_ignore_keywords},
+ {{'N', N_("obsolete; same as --depth=files")}} },
+
+ { "help", svn_cl__help, {"?", "h"}, {N_(
+ "Describe the usage of this program or its subcommands.\n"
+ "usage: help [SUBCOMMAND...]\n"
+ )},
+ {'v'},
+ {{'v', N_("also show experimental subcommands and options")}} },
/* 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"
+ { "import", svn_cl__import, {0}, {N_(
+ "Commit an unversioned file or tree into the repository.\n"
"usage: import [PATH] URL\n"
- "\n"
+ "\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"),
+ " if --force is specified.\n"
+ )},
{'q', 'N', opt_depth, opt_autoprops, opt_force, opt_no_autoprops,
- SVN_CL__LOG_MSG_OPTIONS, opt_no_ignore} },
+ SVN_CL__LOG_MSG_OPTIONS, opt_no_ignore},
+ {{'N', N_("obsolete; same as --depth=files")}} },
- { "info", svn_cl__info, {0}, N_
- ("Display information about a local or remote item.\n"
+ { "info", svn_cl__info, {0}, {N_(
+ "Display information about a local or remote item.\n"
"usage: info [TARGET[@REV]...]\n"
- "\n"
+ "\n"), N_(
" Print information about each TARGET (default: '.').\n"
" TARGET may be either a working-copy path or a URL. If specified, REV\n"
" determines in which revision the target is first looked up; the default\n"
" is HEAD for a URL or BASE for a WC path.\n"
- "\n"
+ "\n"), N_(
" With --show-item, print only the value of one item of information\n"
- " about TARGET.\n"),
- {'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml,
- opt_changelist, opt_include_externals, opt_show_item, opt_no_newline}
+ " about TARGET.\n"
+ "\n"), N_(
+ " EXPERIMENTAL:\n"
+ " With --x-viewspec, print the working copy layout.\n"
+ )},
+ {'r', 'R', 'H', opt_depth, opt_targets, opt_incremental, opt_xml,
+ opt_changelist, opt_include_externals, opt_show_item, opt_no_newline,
+ opt_viewspec},
+ {{'H', N_("show file sizes with base-2 unit suffixes\n"
+ " "
+ "(Byte, Kilobyte, Megabyte, Gigabyte, Terabyte\n"
+ " "
+ "and Petabyte), limiting the number of digits\n"
+ " "
+ "to three or less")}}
},
{ "list", svn_cl__list, {"ls"},
-#if defined(WIN32)
- N_
- ("List directory entries in the repository.\n"
+ {N_(
+ "List directory entries in the repository.\n"
"usage: list [TARGET[@REV]...]\n"
- "\n"
+ "\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"
+ "\n"), N_(
" The default TARGET is '.', meaning the repository URL of the current\n"
" working directory.\n"
- "\n"
+ "\n"),
+#if defined(WIN32)
+ N_(
" Multiple --search patterns may be specified and the output will be\n"
" reduced to those paths whose last segment - i.e. the file or directory\n"
" name - contains a sub-string matching at least one of these patterns\n"
" (Windows only).\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"),
+ "\n"),
#else
- 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"
+ N_(
" Multiple --search patterns may be specified and the output will be\n"
" reduced to those paths whose last segment - i.e. the file or directory\n"
" name - matches at least one of these patterns.\n"
- "\n"
+ "\n"),
+#endif
+ N_(
" With --verbose, the following fields will be shown for each item:\n"
- "\n"
+ "\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"),
-#endif
- {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml,
- opt_include_externals, opt_search}, },
-
- { "lock", svn_cl__lock, {0}, N_
- ("Lock working copy paths or URLs in the repository, so that\n"
+ " Date and time of the last commit\n"
+ )},
+ {'r', 'v', 'R', 'H', opt_depth, opt_incremental, opt_xml,
+ opt_include_externals, opt_search},
+ {{'H', N_("with --verbose, show file sizes with base-2\n"
+ " "
+ "unit suffixes (Byte, Kilobyte, Megabyte,\n"
+ " "
+ "Gigabyte, Terabyte and Petabyte), limiting\n"
+ " "
+ "the number of digits to three or less")}} },
+
+ { "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 a lock from another user or working copy.\n"),
+ "\n"), N_(
+ " Use --force to steal a lock from another user or working copy.\n"
+ )},
{ opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force, 'q' },
{{'F', N_("read lock comment from file ARG")},
{'m', N_("specify lock comment ARG")},
{opt_force_log, N_("force validity of lock comment source")},
{opt_force, N_("steal locks")}} },
- { "log", svn_cl__log, {0}, N_
- ("Show the log messages for a set of revision(s) and/or path(s).\n"
+ { "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"
+ "\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"
+ "\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"
+ "\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"
+ "\n"), N_(
" With -v, also print all affected paths with each log message.\n"
" Each changed path is preceded with a symbol describing the change:\n"
" A: The path was added or copied.\n"
@@ -904,18 +875,18 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" If a file or directory was moved from one path to another with 'svn move'\n"
" the old path will be listed as deleted and the new path will be listed\n"
" as copied from the old path at a prior revision.\n"
- "\n"
+ "\n"), N_(
" With -q, don't print the log message body itself (note that this is\n"
" compatible with -v).\n"
- "\n"
+ "\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"
+ "\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"
+ "\n"), N_(
" If the --search option is used, log messages are displayed only if the\n"
" provided search pattern matches any of the author, date, log message\n"
" text (unless --quiet is used), or, if the --verbose option is also\n"
@@ -932,37 +903,42 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" If --limit is used in combination with --search, --limit restricts the\n"
" number of log messages searched, rather than restricting the output\n"
" to a particular number of matching log messages.\n"
- "\n"
+ "\n"), N_(
" Examples:\n"
- "\n"
+ "\n"), N_(
" Show the latest 5 log messages for the current working copy\n"
" directory and display paths changed in each commit:\n"
" svn log -l 5 -v\n"
- "\n"
+ "\n"), N_(
" Show the log for bar.c as of revision 42:\n"
" svn log bar.c@42\n"
- "\n"
+ "\n"), N_(
" Show log messages and diffs for each commit to foo.c:\n"
" svn log --diff http://www.example.com/repo/project/foo.c\n"
" (Because the above command uses a full URL it does not require\n"
" a working copy.)\n"
- "\n"
+ "\n"), N_(
" Show log messages for the children foo.c and bar.c of the directory\n"
" '/trunk' as it appeared in revision 50, using the ^/ URL shortcut:\n"
" svn log ^/trunk@50 foo.c bar.c\n"
- "\n"
+ "\n"), N_(
" Show the log messages for any incoming changes to foo.c during the\n"
" next 'svn update':\n"
" svn log -r BASE:HEAD foo.c\n"
- "\n"
+ "\n"), N_(
" Show the log message for the revision in which /branches/foo\n"
" was created:\n"
" svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"
- "\n"
+ "\n"), N_(
+ " Show all log messages for commits between the tags ^/tags/2.0 and\n"
+ " ^/tags/3.0; assuming that tag 2.0 was created in revision 100:\n"
+ " svn log -rHEAD:100 ^/tags/3.0\n"
+ "\n"), N_(
" If ^/trunk/foo.c was moved to ^/trunk/bar.c' in revision 22, 'svn log -v'\n"
" shows a deletion and a copy in its changed paths list, such as:\n"
" D /trunk/foo.c\n"
- " A /trunk/bar.c (from /trunk/foo.c:21)\n"),
+ " A /trunk/bar.c (from /trunk/foo.c:21)\n"
+ )},
{'r', 'c', 'q', 'v', 'g', 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,
@@ -972,8 +948,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
{'v', N_("also print all affected paths")},
{'q', N_("do not print the log message")}} },
- { "merge", svn_cl__merge, {0}, N_
- ( /* For this large section, let's keep it unindented for easier
+ { "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"
@@ -983,54 +959,54 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" (the 'cherry-pick' merge)\n"
" 3. merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n"
" (the '2-URL' merge)\n"
-"\n"
+"\n"), N_(
" 1. This form, with one source path and no revision range, is called\n"
" a 'complete' merge:\n"
-"\n"
+"\n"), N_(
" svn merge SOURCE[@REV] [TARGET_WCPATH]\n"
-"\n"
+"\n"), N_(
" The complete merge is used for the 'sync' and 'reintegrate' merges\n"
" in the 'feature branch' pattern described below. It finds all the\n"
" changes on the source branch that have not already been merged to the\n"
" target branch, and merges them into the working copy. Merge tracking\n"
" is used to know which changes have already been merged.\n"
-"\n"
+"\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"
+"\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"
+"\n"), N_(
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. There are some special cases:\n"
-"\n"
+"\n"), N_(
" - If SOURCE is a URL:\n"
-"\n"
+"\n"), N_(
" - If the basename of the URL and the basename of '.' are the\n"
" same, then the differences are applied to '.'. Otherwise,\n"
" if a file with the same basename as that of the URL is found\n"
" within '.', then the differences are applied to that file.\n"
" In all other cases, the target defaults to '.'.\n"
-"\n"
+"\n"), N_(
" - If SOURCE is a working copy path:\n"
-"\n"
+"\n"), N_(
" - If the source is a file, then differences are applied to that\n"
" file (useful for reverse-merging earlier changes). Otherwise,\n"
" if the source is a directory, then the target defaults to '.'.\n"
-"\n"
+"\n"), N_(
" In normal usage the working copy should be up to date, at a single\n"
" revision, with no local modifications and no switched subtrees.\n"
-"\n"
+"\n"), N_(
" - The 'Feature Branch' Merging Pattern -\n"
-"\n"
+"\n"), N_(
" In this commonly used work flow, known also as the 'development\n"
" branch' pattern, a developer creates a branch and commits a series of\n"
" changes that implement a new feature. The developer periodically\n"
@@ -1038,17 +1014,17 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" development branch up to date with those changes. When the feature is\n"
" complete, the developer performs a merge from the feature branch to\n"
" the parent branch to re-integrate the changes.\n"
-"\n"
+"\n"), N_(
" parent --+----------o------o-o-------------o--\n"
" \\ \\ \\ /\n"
" \\ merge merge merge\n"
" \\ \\ \\ /\n"
" feature +--o-o-------o----o-o----o-------\n"
-"\n"
+"\n"), N_(
" A merge from the parent branch to the feature branch is called a\n"
" 'sync' or 'catch-up' merge, and a merge from the feature branch to the\n"
" parent branch is called a 'reintegrate' merge.\n"
-"\n"
+"\n"), N_(
" - Sync Merge Example -\n"
" ............\n"
" . .\n"
@@ -1058,7 +1034,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" \\ v\n"
" feature +------------------------o-----\n"
" r100 r200\n"
-"\n"
+"\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 (trunk@100)\n"
@@ -1066,162 +1042,162 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+"\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"
+"\n"), N_(
" svn merge ^/trunk\n"
-"\n"
+"\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"), N_(
" - Reintegrate Merge Example -\n"
-"\n"
+"\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"
+"\n"), N_(
" rW rX\n"
" trunk ------+--------------------L------------------o\n"
" \\ . ^\n"
" \\ ............. /\n"
" \\ . /\n"
" feature +--------------------------------R\n"
-"\n"
+"\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"
+"\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"
+"\n"), N_(
" svn merge ^/feature\n"
-"\n"
+"\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"
+"\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"
-"\n"
+"\n"), N_(
+"\n"), N_(
" 2. This form is called a 'cherry-pick' merge:\n"
-"\n"
+"\n"), N_(
" svn merge [-c M[,N...] | -r N:M ...] SOURCE[@REV] [TARGET_WCPATH]\n"
-"\n"
+"\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"
+"\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"
+"\n"), N_(
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. The special cases noted above in the 'complete' merge form\n"
" also apply here.\n"
-"\n"
+"\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"
+"\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"
+"\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>'. Undoing changes like this is also known\n"
" as performing a 'reverse merge'.\n"
-"\n"
+"\n"), N_(
" Multiple '-c' and/or '-r' options may be specified and mixing of\n"
" forward and reverse ranges is allowed.\n"
-"\n"
+"\n"), N_(
" - Cherry-pick Merge Example -\n"
-"\n"
+"\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"
+"\n"), N_(
" 1.x-release +-----------------------o-----\n"
" / ^\n"
" / |\n"
" / |\n"
" trunk ------+--------------------------LR-----\n"
" r50\n"
-"\n"
+"\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"
+"\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"
+"\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"
+"\n"), N_(
" svn merge -c50 ^/trunk\n"
-"\n"
+"\n"), N_(
" You can also cherry-pick several revisions and/or revision ranges:\n"
-"\n"
+"\n"), N_(
" svn merge -c50,54,60 -r65:68 ^/trunk\n"
-"\n"
-"\n"
+"\n"), N_(
+"\n"), N_(
" 3. This form is called a '2-URL merge':\n"
-"\n"
+"\n"), N_(
" svn merge SOURCE1[@REV1] SOURCE2[@REV2] [TARGET_WCPATH]\n"
-"\n"
+"\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"
+"\n"), N_(
" Two source URLs are specified, identifying two trees on the same\n"
" branch or on different branches. The trees are compared and the\n"
" difference from SOURCE1@REV1 to SOURCE2@REV2 is applied to the\n"
" working copy of the target branch at TARGET_WCPATH. The target\n"
" branch may be the same as one or both sources, or different again.\n"
" The three branches involved can be completely unrelated.\n"
-"\n"
+"\n"), N_(
" TARGET_WCPATH is a working copy path; if omitted, '.' is generally\n"
" assumed. The special cases noted above in the 'complete' merge form\n"
" also apply here.\n"
-"\n"
+"\n"), N_(
" SOURCE1 and/or SOURCE2 can also be specified as a working copy path,\n"
" in which case the merge source URL is derived from the working copy.\n"
-"\n"
+"\n"), N_(
" - 2-URL Merge Example -\n"
-"\n"
+"\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"
+"\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"
+"\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"
+"\n"), N_(
" foo +-----------------------------------o\n"
" / ^\n"
" / /\n"
@@ -1231,41 +1207,41 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" \\ ............ /\n"
" \\ . /\n"
" bar +-----------------------------------R\n"
-"\n"
+"\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"
+"\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"
+"\n"), N_(
" svn merge ^/trunk@500 ^/bar\n"
-"\n"
+"\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"
+"\n"), N_(
" svn diff ^/trunk@500 ^/bar@HEAD\n"
-"\n"
-"\n"
+"\n"), N_(
+"\n"), N_(
" The following applies to all types of merges:\n"
-"\n"
+"\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"
+"\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"
+"\n"), N_(
" Switched sub-paths should also be avoided during merging, as they may\n"
" cause incomplete merges and create subtree mergeinfo.\n"
-"\n"
+"\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"
+"\n"), N_(
" A Added\n"
" D Deleted\n"
" U Updated\n"
@@ -1273,15 +1249,15 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" G Merged\n"
" E Existed\n"
" R Replaced\n"
-"\n"
+"\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"
+"\n"), N_(
" - Merge Tracking -\n"
-"\n"
+"\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"
@@ -1290,78 +1266,82 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+"\n"), N_(
" The --ignore-ancestry option prevents merge tracking and thus ignores\n"
" mergeinfo, neither considering it nor recording it.\n"
-"\n"
+"\n"), N_(
" - Merging from foreign repositories -\n"
-"\n"
+"\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"),
+" 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, 'v'},
- { { opt_force, N_("force deletions even if deleted contents don't match") } }
+ { { opt_force, N_("force deletions even if deleted contents don't match") },
+ {'N', N_("obsolete; same as --depth=files")} }
},
- { "mergeinfo", svn_cl__mergeinfo, {0}, N_
- ("Display merge-related information.\n"
+ { "mergeinfo", svn_cl__mergeinfo, {0}, {N_(
+ "Display merge-related information.\n"
"usage: 1. mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
" 2. mergeinfo --show-revs=WHICH SOURCE[@REV] [TARGET[@REV]]\n"
- "\n"
+ "\n"), N_(
" 1. Summarize the history of merging between SOURCE and TARGET. The graph\n"
" shows, from left to right:\n"
" the youngest common ancestor of the branches;\n"
" the latest full merge in either direction, and thus the common base\n"
" that will be used for the next complete merge;\n"
" the repository path and revision number of the tip of each branch.\n"
- "\n"
+ "\n"), N_(
" 2. Print the revision numbers on SOURCE that have been merged to TARGET\n"
" (with --show-revs=merged), or that have not been merged to TARGET\n"
" (with --show-revs=eligible). Print only revisions in which there was\n"
" at least one change in SOURCE.\n"
- "\n"
+ "\n"), N_(
" If --revision (-r) is provided, filter the displayed information to\n"
" show only that which is associated with the revisions within the\n"
" specified range. Revision numbers, dates, and the 'HEAD' keyword are\n"
" valid range values.\n"
- "\n"
+ "\n"), N_(
" SOURCE and TARGET are the source and target branch URLs, respectively.\n"
" (If a WC path is given, the corresponding base URL is used.) The default\n"
" TARGET is the current working directory ('.'). REV specifies the revision\n"
" to be considered the tip of the branch; the default for SOURCE is HEAD,\n"
" and the default for TARGET is HEAD for a URL or BASE for a WC path.\n"
- "\n"
- " The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"),
+ "\n"), N_(
+ " The depth can be 'empty' or 'infinity'; the default is 'empty'.\n"
+ )},
{'r', 'R', 'q', 'v', opt_depth, opt_show_revs, opt_mergeinfo_log,
opt_incremental } },
- { "mkdir", svn_cl__mkdir, {0}, N_
- ("Create a new directory under version control.\n"
+ { "mkdir", svn_cl__mkdir, {0}, {N_(
+ "Create a new directory under version control.\n"
"usage: 1. mkdir PATH...\n"
" 2. mkdir URL...\n"
- "\n"
+ "\n"), N_(
" Create version controlled directories.\n"
- "\n"
+ "\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"
+ "\n"), N_(
" 2. Each directory specified by a URL is created in the repository via\n"
" an immediate commit.\n"
- "\n"
+ "\n"), N_(
" In both cases, all the intermediate directories must already exist,\n"
- " unless the --parents option is given.\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 (rename) an item in a working copy or repository.\n"
+ { "move", svn_cl__move, {"mv", "rename", "ren"}, {N_(
+ "Move (rename) an item in a working copy or repository.\n"
"usage: move SRC... DST\n"
- "\n"
+ "\n"), N_(
" SRC and DST can both be working copy (WC) paths or URLs:\n"
" WC -> WC: move an item in a working copy, as a local change to\n"
" be committed later (with or without further changes)\n"
@@ -1370,28 +1350,29 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" All the SRCs must be of the same type. If DST is an existing directory,\n"
" the sources will be added as children of DST. When moving multiple\n"
" sources, DST must be an existing directory.\n"
- "\n"
+ "\n"), N_(
" SRC and DST of WC -> WC moves must be committed in the same revision.\n"
" Furthermore, WC -> WC moves will refuse to move a mixed-revision subtree.\n"
" To avoid unnecessary conflicts, it is recommended to run 'svn update'\n"
" to update the subtree to a single revision before moving it.\n"
- " The --allow-mixed-revisions option is provided for backward compatibility.\n"),
+ " The --allow-mixed-revisions option is provided for backward compatibility.\n"
+ )},
{'q', opt_force, opt_parents, opt_allow_mixed_revisions,
SVN_CL__LOG_MSG_OPTIONS, 'r'},
{{'r', "deprecated and ignored"}} },
- { "patch", svn_cl__patch, {0}, N_
- ("Apply a patch to a working copy.\n"
+ { "patch", svn_cl__patch, {0}, {N_(
+ "Apply a patch to a working copy.\n"
"usage: patch PATCHFILE [WCPATH]\n"
- "\n"
+ "\n"), N_(
" Apply a unidiff patch in PATCHFILE to the working copy WCPATH.\n"
" If WCPATH is omitted, '.' is assumed.\n"
- "\n"
+ "\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, except for Subversion\n"
" property diffs as produced by 'svn diff'.\n"
- "\n"
+ "\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"
@@ -1400,114 +1381,118 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\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"
+ "\n"), N_(
" A Added\n"
" D Deleted\n"
" U Updated\n"
" C Conflict\n"
" G Merged (with local uncommitted changes)\n"
- "\n"
+ "\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"
+ "\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"
+ "\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"
+ { "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"
+ "\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"
- "\n"
- " See 'svn help propset' for descriptions of the svn:* special properties.\n"),
+ "\n"), N_(
+ " See 'svn help propset' for descriptions of the svn:* special properties.\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"
+ { "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"
+ "\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 descriptions of the svn:* special properties.\n"),
+ "\n"), N_(
+ " See 'svn help propset' for descriptions of the svn:* special 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"
+ { "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"
+ "\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"
+ "\n"), N_(
" With --verbose, the target path and the property name are printed on\n"
" separate lines before each value, like 'svn proplist --verbose'.\n"
" Otherwise, if there is more than one TARGET or a depth other than\n"
" 'empty', the target path is printed on the same line before each value.\n"
- "\n"
+ "\n"), N_(
" By default, an extra newline is printed after the property value so that\n"
" the output looks pretty. With a single TARGET, depth 'empty' and without\n"
" --show-inherited-props, you can use the --no-newline option to disable this\n"
" (useful when redirecting a binary property value to a file, for example).\n"
- "\n"
- " See 'svn help propset' for descriptions of the svn:* special properties.\n"),
+ "\n"), N_(
+ " See 'svn help propset' for descriptions of the svn:* special properties.\n"
+ )},
{'v', 'R', opt_depth, 'r', opt_revprop, opt_strict, opt_no_newline, opt_xml,
opt_changelist, opt_show_inherited_props },
{{'v', N_("print path, name and value on separate lines")},
{opt_strict, N_("(deprecated; use --no-newline)")}} },
- { "proplist", svn_cl__proplist, {"plist", "pl"}, N_
- ("List all properties on files, dirs, or revisions.\n"
+ { "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"
+ "\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"
- "\n"
+ "\n"), N_(
" With --verbose, the property values are printed as well, like 'svn propget\n"
" --verbose'. With --quiet, the paths are not printed.\n"
- "\n"
- " See 'svn help propset' for descriptions of the svn:* special properties.\n"),
+ "\n"), N_(
+ " See 'svn help propset' for descriptions of the svn:* special properties.\n"
+ )},
{'v', 'R', opt_depth, 'r', 'q', opt_revprop, opt_xml, opt_changelist,
opt_show_inherited_props },
{{'v', N_("print path, name and value on separate lines")},
{'q', N_("don't print the path")}} },
- { "propset", svn_cl__propset, {"pset", "ps"}, N_
- ("Set the value of a property on files, dirs, or revisions.\n"
+ { "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"
+ "\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"
+ "\n"), N_(
" The value may be provided with the --file option instead of PROPVAL.\n"
- "\n"
+ "\n"), N_(
" Property names starting with 'svn:' are reserved. Subversion recognizes\n"
" the following special versioned properties on a file:\n"
" svn:keywords - Keywords to be expanded. Valid keywords are:\n"
@@ -1518,7 +1503,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" LastChangedRevision\n"
" Id - A compressed summary of the previous four.\n"
" Header - Similar to Id but includes the full URL.\n"
- "\n"
+ "\n"), N_(
" Custom keywords can be defined with a format string separated from\n"
" the keyword name with '='. Valid format substitutions are:\n"
" %a - The author of the revision given by %r.\n"
@@ -1536,7 +1521,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" Example custom keyword definition: MyKeyword=%r%_%a%_%P\n"
" Once a custom keyword has been defined for a file, it can be used\n"
" within the file like any other keyword: $MyKeyword$\n"
- "\n"
+ "\n"), 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"
@@ -1548,7 +1533,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\n"), N_(
" Subversion recognizes the following special versioned properties on a\n"
" directory:\n"
" svn:ignore - A list of file glob patterns to ignore, one per line.\n"
@@ -1582,51 +1567,53 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" LOCALPATH [-r PEG] URL\n"
" The ambiguous format 'relative_path relative_path' is taken as\n"
" 'relative_url relative_path' with peg revision support.\n"
- " Lines starting with a '#' character are ignored.\n"),
+ " Lines starting with a '#' character are ignored.\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"
+ { "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"
+ "\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"
+ "\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"
+ "\n"), N_(
" 2. TO-URL is the (complete) new repository URL to use for PATH.\n"
- "\n"
+ "\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"),
+ " 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"
+ { "resolve", svn_cl__resolve, {0}, {N_(
+ "Resolve conflicts on working copy files or directories.\n"
"usage: resolve [PATH...]\n"
- "\n"
+ "\n"), N_(
" By default, perform interactive conflict resolution on PATH.\n"
" In this mode, the command is recursive by default (depth 'infinity').\n"
- "\n"
+ "\n"), N_(
" The --accept=ARG option prevents interactive prompting and forces\n"
" conflicts on PATH to be resolved in the manner specified by ARG.\n"
" In this mode, the command is not recursive by default (depth 'empty').\n"
- "\n"
+ "\n"), N_(
" A conflicted path cannot be committed with 'svn commit' until it\n"
" has been marked as resolved with 'svn resolve'.\n"
- "\n"
+ "\n"), N_(
" Subversion knows three types of conflicts:\n"
" Text conflicts, Property conflicts, and Tree conflicts.\n"
- "\n"
+ "\n"), N_(
" Text conflicts occur when overlapping changes to file contents were\n"
" made. Text conflicts are usually resolved by editing the conflicted\n"
" file or by using a merge tool (which may be an external program).\n"
@@ -1634,12 +1621,12 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" edit files (such as 'mine-full' or 'theirs-conflict'), but these are\n"
" only useful in situations where it is acceptable to discard local or\n"
" incoming changes altogether.\n"
- "\n"
+ "\n"), N_(
" Property conflicts are usually resolved by editing the value of the\n"
" conflicted property (either from the interactive prompt, or with\n"
" 'svn propedit'). As with text conflicts, options exist to edit a\n"
" property automatically, discarding some changes in favour of others.\n"
- "\n"
+ "\n"), N_(
" Tree conflicts occur when a change to the directory structure was\n"
" made, and when this change cannot be applied to the working copy\n"
" without affecting other changes (text changes, property changes,\n"
@@ -1649,7 +1636,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" in detail, and may offer options to resolve the conflict automatically.\n"
" It is recommended to use these automatic options whenever possible,\n"
" rather than attempting manual tree conflict resolution.\n"
- "\n"
+ "\n"), N_(
" If a tree conflict cannot be resolved automatically, it is recommended\n"
" to figure out why the conflict occurred before attempting to resolve it.\n"
" The 'svn log -v' command can be used to inspect structural changes\n"
@@ -1660,7 +1647,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" in a way such that the conflict is resolved. This may involve editing\n"
" files manually or with 'svn merge'. It may be necessary to discard some\n"
" local changes with 'svn revert'. Files or directories might have to be\n"
- " copied, deleted, or moved.\n"),
+ " copied, deleted, or moved.\n"
+ )},
{opt_targets, 'R', opt_depth, 'q', opt_accept},
{{opt_accept, N_("specify automatic conflict resolution source\n"
" "
@@ -1668,37 +1656,40 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" "
"'theirs-conflict', 'mine-full', 'theirs-full')")}} },
- { "resolved", svn_cl__resolved, {0}, N_
- ("Remove 'conflicted' state on working copy files or directories.\n"
+ { "resolved", svn_cl__resolved, {0}, {N_(
+ "Remove 'conflicted' state on working copy files or directories.\n"
"usage: resolved PATH...\n"
- "\n"
+ "\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"),
+ " 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 state (undo local changes).\n"
+ { "revert", svn_cl__revert, {0}, {N_(
+ "Restore pristine working copy state (undo local changes).\n"
"usage: revert PATH...\n"
- "\n"
+ "\n"), N_(
" Revert changes in the working copy at or within PATH, and remove\n"
" conflict markers as well, if any.\n"
- "\n"
+ "\n"), N_(
" This subcommand does not revert already committed changes.\n"
" For information about undoing already committed changes, search\n"
- " the output of 'svn help merge' for 'undo'.\n"),
- {opt_targets, 'R', opt_depth, 'q', opt_changelist} },
+ " the output of 'svn help merge' for 'undo'.\n"
+ )},
+ {opt_targets, 'R', opt_depth, 'q', opt_changelist,
+ opt_remove_added} },
- { "status", svn_cl__status, {"stat", "st"}, N_
- ("Print the status of working copy files and directories.\n"
+ { "status", svn_cl__status, {"stat", "st"}, {N_(
+ "Print the status of working copy files and directories.\n"
"usage: status [PATH...]\n"
- "\n"
+ "\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"
+ "\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"
@@ -1742,61 +1733,64 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" '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"
+ "\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"
+ "\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"
+ "\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"
+ "\n"), N_(
" Example output:\n"
" svn status wc\n"
" M wc/bar.c\n"
" A + wc/qax.c\n"
- "\n"
+ "\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"
+ "\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"
+ "\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"),
+ " D wc/qax.c\n"
+ )},
{ 'u', 'v', 'N', opt_depth, 'r', 'q', opt_no_ignore, opt_incremental,
opt_xml, opt_ignore_externals, opt_changelist},
- {{'q', N_("don't print unversioned items")}} },
+ {{'q', N_("don't print unversioned items")},
+ {'N', N_("obsolete; same as --depth=immediates")}} },
- { "switch", svn_cl__switch, {"sw"}, N_
- ("Update the working copy to a different URL within the same repository.\n"
+ { "switch", svn_cl__switch, {"sw"}, {N_(
+ "Update the working copy to a different URL within the same\n"
+ "repository.\n"
"usage: 1. switch URL[@PEGREV] [PATH]\n"
" 2. switch --relocate FROM-PREFIX TO-PREFIX [PATH...]\n"
- "\n"
+ "\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"
+ "\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"
@@ -1807,25 +1801,23 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\n"), N_(
" Use the --set-depth option to set a new working copy depth on the\n"
" targets of this operation.\n"
- "\n"
+ "\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"
+ "\n"), N_(
" 2. The '--relocate' option is deprecated. This syntax is equivalent to\n"
" 'svn relocate FROM-PREFIX TO-PREFIX [PATH]'.\n"
- "\n"
+ "\n"), N_(
" See also 'svn help update' for a list of possible characters\n"
" reporting the action taken.\n"
- "\n"
+ "\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_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept,
opt_relocate },
@@ -1833,27 +1825,29 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
N_("allow switching to a node with no common ancestor")},
{opt_force,
N_("handle unversioned obstructions as changes")},
- {opt_relocate,N_("deprecated; use 'svn relocate'")}}
+ {opt_relocate, N_("deprecated; use 'svn relocate'")},
+ {'N', N_("obsolete; same as --depth=files")}}
},
- { "unlock", svn_cl__unlock, {0}, N_
- ("Unlock working copy paths or URLs.\n"
+ { "unlock", svn_cl__unlock, {0}, {N_(
+ "Unlock working copy paths or URLs.\n"
"usage: unlock TARGET...\n"
- "\n"
- " Use --force to break a lock held by another user or working copy.\n"),
+ "\n"), N_(
+ " Use --force to break a lock held by another user or working copy.\n"
+ )},
{ opt_targets, opt_force, 'q' },
{{opt_force, N_("break locks")}} },
- { "update", svn_cl__update, {"up"}, N_
- ("Bring changes from the repository into the working copy.\n"
+ { "update", svn_cl__update, {"up"}, {N_(
+ "Bring changes from the repository into the working copy.\n"
"usage: update [PATH...]\n"
- "\n"
+ "\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"
+ "\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"
+ "\n"), N_(
" A Added\n"
" D Deleted\n"
" U Updated\n"
@@ -1861,7 +1855,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" G Merged\n"
" E Existed\n"
" R Replaced\n"
- "\n"
+ "\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"
@@ -1869,7 +1863,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\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"
@@ -1881,99 +1875,36 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" 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"
+ "\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"
+ "\n"), N_(
" Use the --set-depth option to set a new working copy depth on the\n"
- " targets of this operation.\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, opt_adds_as_modification},
{ {opt_force,
- N_("handle unversioned obstructions as changes")} } },
+ N_("handle unversioned obstructions as changes")},
+ {'N', N_("obsolete; same as --depth=files")} } },
- { "upgrade", svn_cl__upgrade, {0}, N_
- ("Upgrade the metadata storage format for a working copy.\n"
+ { "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"),
+ "\n"), N_(
+ " Local modifications are preserved.\n"
+ )},
{ 'q' } },
- { "x-shelve", svn_cl__shelve, {"shelve"}, N_
- ("Put a local change aside, as if putting it on a shelf.\n"
- "usage: 1. x-shelve [--keep-local] NAME [PATH...]\n"
- " 2. x-shelve --delete NAME\n"
- " 3. x-shelve --list\n"
- "\n"
- " 1. Save the local change in the given PATHs to a patch file, and\n"
- " revert that change from the WC unless '--keep-local' is given.\n"
- " If a log message is given with '-m' or '-F', include it at the\n"
- " beginning of the patch file.\n"
- "\n"
- " 2. Delete the shelved change NAME.\n"
- " (A backup is kept, named with a '.bak' extension.)\n"
- "\n"
- " 3. List shelved changes. Include the first line of any log message\n"
- " and some details about the contents of the change, unless '-q' is\n"
- " given.\n"
- "\n"
- " The kinds of change you can shelve are those supported by 'svn diff'\n"
- " and 'svn patch'. The following are currently NOT supported:\n"
- " mergeinfo changes, copies, moves, mkdir, rmdir,\n"
- " 'binary' content, uncommittable states\n"
- "\n"
- " To bring back a shelved change, use 'svn x-unshelve NAME'.\n"
- "\n"
- " Shelved changes are stored in <WC>/.svn/shelves/\n"
- "\n"
- " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
- " in the next release, and there is no promise of backward compatibility.\n"
- ),
- {opt_delete, opt_list, 'q', opt_dry_run, opt_keep_local,
- opt_depth, opt_targets, opt_changelist,
- /* almost SVN_CL__LOG_MSG_OPTIONS but not currently opt_with_revprop: */
- 'm', 'F', opt_force_log, opt_editor_cmd, opt_encoding,
- } },
-
- { "x-unshelve", svn_cl__unshelve, {"unshelve"}, N_
- ("Bring a shelved change back to a local change in the WC.\n"
- "usage: 1. x-unshelve [--keep-shelved] [NAME]\n"
- " 2. x-unshelve --list\n"
- "\n"
- " 1. Apply the shelved change NAME to the working copy.\n"
- " Delete the patch unless the '--keep-shelved' option is given.\n"
- " (A backup is kept, named with a '.bak' extension.)\n"
- " NAME defaults to the most recent shelved change.\n"
- "\n"
- " 2. List shelved changes. Include the first line of any log message\n"
- " and some details about the contents of the change, unless '-q' is\n"
- " given.\n"
- "\n"
- " Any conflict between the change being unshelved and a change\n"
- " already in the WC is handled the same way as by 'svn patch',\n"
- " creating a 'reject' file.\n"
- "\n"
- " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
- " in the next release, and there is no promise of backward compatibility.\n"
- ),
- {opt_keep_shelved, opt_list, 'q', opt_dry_run} },
-
- { "x-shelves", svn_cl__shelves, {"shelves"}, N_
- ("List shelved changes.\n"
- "usage: x-shelves\n"
- "\n"
- " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
- " in the next release, and there is no promise of backward compatibility.\n"
- ),
- {'q'} },
-
- { NULL, NULL, {0}, NULL, {0} }
+ { NULL, NULL, {0}, {NULL}, {0} }
};
+const svn_opt_subcommand_desc3_t *svn_cl__cmd_table = svn_cl__cmd_table_main;
+
/* Version compatibility check */
static svn_error_t *
@@ -2036,6 +1967,49 @@ add_search_pattern_to_latest_group(svn_cl__opt_state_t *opt_state,
APR_ARRAY_PUSH(group, const char *) = pattern;
}
+/* Parse the argument to the --x-viewspec option. */
+static svn_error_t *
+viewspec_from_word(enum svn_cl__viewspec_t *viewspec,
+ const char *utf8_opt_arg)
+{
+ if (!strcmp(utf8_opt_arg, "classic"))
+ *viewspec = svn_cl__viewspec_classic;
+ else if (!strcmp(utf8_opt_arg, "svn11"))
+ *viewspec = svn_cl__viewspec_svn11;
+ else
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid --x-viewspec value"),
+ utf8_opt_arg);
+ return SVN_NO_ERROR;
+}
+
+/* Re-initialize the command table SVN_CL__CMD_TABLE,
+ * adding additional commands from CMDS_ADD.
+ * (TODO: and the options table) */
+static void
+add_commands(const svn_opt_subcommand_desc3_t *cmds_add,
+ apr_pool_t *pool)
+{
+ int elt_size = sizeof(svn_opt_subcommand_desc3_t);
+ const svn_opt_subcommand_desc3_t *cmds_old = svn_cl__cmd_table;
+ const svn_opt_subcommand_desc3_t *cmd;
+ int n_cmds_old, n_cmds_add, n_cmds_new;
+ svn_opt_subcommand_desc3_t *cmds_new;
+
+ for (cmd = cmds_old; cmd->name; cmd++) ;
+ n_cmds_old = (int)(cmd - cmds_old);
+ for (cmd = cmds_add; cmd->name; cmd++) ;
+ n_cmds_add = (int)(cmd - cmds_add);
+ n_cmds_new = n_cmds_old + n_cmds_add;
+
+ /* copy CMDS_OLD and CMDS_ADD, plus an all-zeros terminator entry */
+ cmds_new = apr_pcalloc(pool, (n_cmds_new + 1) * elt_size);
+ memcpy(cmds_new, cmds_old, n_cmds_old * elt_size);
+ memcpy(&cmds_new[n_cmds_old], cmds_add, n_cmds_add * elt_size);
+
+ svn_cl__cmd_table = cmds_new;
+}
+
/*** Main. ***/
@@ -2053,8 +2027,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
svn_cl__opt_state_t opt_state = { 0, { 0 } };
svn_client_ctx_t *ctx;
apr_array_header_t *received_opts;
+ const char *exp_cmds;
int i;
- const svn_opt_subcommand_desc2_t *subcommand = NULL;
+ const svn_opt_subcommand_desc3_t *subcommand = NULL;
const char *dash_F_arg = NULL;
svn_cl__cmd_baton_t command_baton;
svn_auth_baton_t *ab;
@@ -2093,6 +2068,18 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
/* Init the temporary buffer. */
svn_membuf__create(&buf, 0, pool);
+ /* Add experimental commands, if requested. Use the most recent version
+ * that we know about and that is mentioned in the env. var. */
+ exp_cmds = getenv("SVN_EXPERIMENTAL_COMMANDS");
+ if (exp_cmds && strstr(exp_cmds, "shelf3"))
+ {
+ add_commands(svn_cl__cmd_table_shelf3, pool);
+ }
+ else if (exp_cmds && strstr(exp_cmds, "shelf2"))
+ {
+ add_commands(svn_cl__cmd_table_shelf2, pool);
+ }
+
/* Begin processing arguments. */
opt_state.start_revision.kind = svn_opt_revision_unspecified;
opt_state.end_revision.kind = svn_opt_revision_unspecified;
@@ -2102,6 +2089,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.set_depth = svn_depth_unknown;
opt_state.accept_which = svn_cl__accept_unspecified;
opt_state.show_revs = svn_cl__show_revs_invalid;
+ opt_state.file_size_unit = SVN_CL__SIZE_UNIT_NONE;
/* No args? Show usage. */
if (argc <= 1)
@@ -2308,9 +2296,6 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
case opt_dry_run:
opt_state.dry_run = TRUE;
break;
- case opt_list:
- opt_state.list = TRUE;
- break;
case opt_revprop:
opt_state.revprop = TRUE;
break;
@@ -2320,6 +2305,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
case 'N':
descend = FALSE;
break;
+ case 'H':
+ opt_state.file_size_unit = SVN_CL__SIZE_UNIT_BASE_2;
+ break;
case opt_depth:
err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool);
if (err)
@@ -2457,7 +2445,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
break;
case opt_config_dir:
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
- opt_state.config_dir = svn_dirent_internal_style(utf8_opt_arg, pool);
+ SVN_ERR(svn_dirent_internal_style_safe(&opt_state.config_dir, NULL,
+ utf8_opt_arg, pool, pool));
break;
case opt_config_options:
if (!opt_state.config_options)
@@ -2494,9 +2483,11 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.diff.summarize = TRUE;
break;
case opt_remove:
- case opt_delete:
opt_state.remove = TRUE;
break;
+ case opt_drop:
+ opt_state.drop = TRUE;
+ break;
case opt_changelist:
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
if (utf8_opt_arg[0] == '\0')
@@ -2510,7 +2501,6 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.keep_changelists = TRUE;
break;
case opt_keep_local:
- case opt_keep_shelved:
opt_state.keep_local = TRUE;
break;
case opt_with_all_revprops:
@@ -2625,6 +2615,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
case opt_remove_ignored:
opt_state.remove_ignored = TRUE;
break;
+ case opt_remove_added:
+ opt_state.remove_added = TRUE;
+ break;
case opt_no_newline:
case opt_strict: /* ### DEPRECATED */
opt_state.no_newline = TRUE;
@@ -2645,6 +2638,11 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
case opt_vacuum_pristines:
opt_state.vacuum_pristines = TRUE;
break;
+ case opt_viewspec:
+ opt_state.viewspec = TRUE;
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
+ SVN_ERR(viewspec_from_word(&opt_state.viewspec, utf8_opt_arg));
+ break;
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
@@ -2685,7 +2683,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
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");
+ subcommand = svn_opt_get_canonical_subcommand3(svn_cl__cmd_table, "help");
/* If we're not running the `help' subcommand, then look for a
subcommand in the first argument. */
@@ -2696,8 +2694,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
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}, "",
+ static const svn_opt_subcommand_desc3_t pseudo_cmd =
+ { "--version", svn_cl__help, {0}, {""},
{opt_version, /* must accept its own option */
'q', /* brief output */
'v', /* verbose output */
@@ -2722,7 +2720,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
pool));
- subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
+ subcommand = svn_opt_get_canonical_subcommand3(svn_cl__cmd_table,
first_arg);
if (subcommand == NULL)
{
@@ -2760,12 +2758,12 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
if (opt_id == 'h' || opt_id == '?')
continue;
- if (! svn_opt_subcommand_takes_option3(subcommand, opt_id,
+ if (! svn_opt_subcommand_takes_option4(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,
+ svn_opt_get_option_from_code3(opt_id, svn_cl__options,
subcommand, pool);
svn_opt_format_option(&optstr, badopt, FALSE, pool);
if (subcommand->name[0] == '-')
@@ -3016,15 +3014,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
sense (unless we've also been instructed not to care). This may
access the working copy so do it after setting the locking mode. */
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
- || subcommand->cmd_func == svn_cl__shelve))
+ && subcommand->cmd_func != svn_cl__propset)
{
/* If the -F argument is a file that's under revision control,
that's probably not what the user intended. */
@@ -3032,7 +3022,10 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
svn_node_kind_t kind;
const char *local_abspath;
- const char *fname = svn_dirent_internal_style(dash_F_arg, pool);
+ const char *fname;
+
+ SVN_ERR(svn_dirent_internal_style_safe(&fname, NULL, dash_F_arg,
+ pool, pool));
err = svn_dirent_get_absolute(&local_abspath, fname, pool);
diff --git a/subversion/svn/util.c b/subversion/svn/util.c
index a18e5f37c9f7a..757983a9963f1 100644
--- a/subversion/svn/util.c
+++ b/subversion/svn/util.c
@@ -163,6 +163,8 @@ svn_cl__merge_file_externally(const char *base_path,
arguments[5] = wc_path;
arguments[6] = NULL;
+ /* Presumably apr_filepath_get() returns a valid path, so we don't have
+ to use the safe version of svn_dirent_internal_style() here. */
SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), merge_tool,
arguments, &exitcode, NULL, TRUE, NULL, NULL, NULL,
pool));