You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2018/11/28 21:25:35 UTC
svn commit: r1847678 [16/25] - in /subversion/branches/swig-py3: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/ build/win32/
contrib/client-side/ contrib/client-side/svn_load_dirs/
contrib/client-side/svnmerge/ contrib/hook-scri...
Modified: subversion/branches/swig-py3/subversion/svn/shelf-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/svn/shelf-cmd.c?rev=1847678&r1=1847677&r2=1847678&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/svn/shelf-cmd.c (original)
+++ subversion/branches/swig-py3/subversion/svn/shelf-cmd.c Wed Nov 28 21:25:32 2018
@@ -1,5 +1,5 @@
/*
- * shelve-cmd.c -- Shelve commands.
+ * shelf-cmd.c -- Shelving commands.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
@@ -28,15 +28,38 @@
#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 "cl.h"
#include "svn_private_config.h"
#include "private/svn_sorts_private.h"
+#include "private/svn_client_private.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,
@@ -53,59 +76,121 @@ get_next_argument(const char **arg,
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_duration_str(apr_time_t duration,
- apr_pool_t *result_pool)
+friendly_age_str(apr_time_t mtime,
+ apr_time_t time_now,
+ apr_pool_t *result_pool)
{
- int minutes = (int)(duration / 1000000 / 60);
+ int minutes = (int)((time_now - mtime) / 1000000 / 60);
char *s;
if (minutes >= 60 * 24)
- s = apr_psprintf(result_pool, _("%d days"), 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, _("%d hours"), minutes / 60);
+ s = apr_psprintf(result_pool,
+ Q_("%d hour ago", "%d hours ago",
+ minutes / 60),
+ minutes / 60);
else
- s = apr_psprintf(result_pool, _("%d minutes"), minutes);
+ s = apr_psprintf(result_pool,
+ Q_("%d minute ago", "%d minutes ago",
+ minutes),
+ minutes);
return s;
}
-/* Print some details of the changes in the patch described by INFO.
- */
-static svn_error_t *
-show_diffstat(svn_client_shelf_version_t *shelf_version,
- apr_pool_t *scratch_pool)
-{
-#ifndef WIN32
- const char *patch_abspath;
- int result;
-
- SVN_ERR(svn_client_shelf_get_patch_abspath(&patch_abspath, shelf_version,
- scratch_pool));
- result = system(apr_psprintf(scratch_pool,
- "diffstat -p0 '%s' 2> /dev/null",
- patch_abspath));
- if (result == 0)
- SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
-#endif
- return SVN_NO_ERROR;
-}
-
/* 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;
+ 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;
+ ? -1 : (a_val->mtime > b_val->mtime) ? 1 : 0;
}
-/* Return a list of shelves sorted by patch file mtime, oldest first.
+/* Return a list of shelves sorted by their mtime, oldest first.
*/
static svn_error_t *
list_sorted_by_date(apr_array_header_t **list,
@@ -115,7 +200,7 @@ list_sorted_by_date(apr_array_header_t *
{
apr_hash_t *shelf_infos;
- SVN_ERR(svn_client_shelf_list(&shelf_infos, local_abspath,
+ 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,
@@ -125,53 +210,47 @@ list_sorted_by_date(apr_array_header_t *
/* */
static svn_error_t *
-stats(svn_client_shelf_t *shelf,
+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)
{
- svn_client_shelf_version_t *shelf_version;
char *age_str;
char *version_str;
apr_hash_t *paths;
const char *paths_str = "";
- char *info_str;
- if (version == 0)
+ if (! shelf_version)
{
return SVN_NO_ERROR;
}
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, version,
- scratch_pool, scratch_pool));
-
- age_str = friendly_duration_str(time_now - shelf_version->mtime, scratch_pool);
+ 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,
- _("version %d of %d"),
+ 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,
+ SVN_ERR(svn_client__shelf_paths_changed(&paths, shelf_version,
scratch_pool, scratch_pool));
- if (paths)
- paths_str = apr_psprintf(scratch_pool,
- _(", %d paths changed"), apr_hash_count(paths));
- info_str = apr_psprintf(scratch_pool,
- _("%s, %s ago%s\n"),
- version_str, age_str, paths_str);
+ 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",
- shelf->name, info_str));
+ "%-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,
+ SVN_ERR(svn_client__shelf_get_log_message(&log_message, shelf,
scratch_pool));
if (log_message)
{
@@ -188,7 +267,6 @@ stats(svn_client_shelf_t *shelf,
static svn_error_t *
shelves_list(const char *local_abspath,
svn_boolean_t quiet,
- svn_boolean_t with_diffstat,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
@@ -203,24 +281,22 @@ shelves_list(const char *local_abspath,
{
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_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
- SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath,
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
ctx, scratch_pool));
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, shelf->max_version,
- scratch_pool, 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, time_now,
+ SVN_ERR(stats(shelf, shelf->max_version, shelf_version, time_now,
TRUE /*with_logmsg*/, scratch_pool));
- if (with_diffstat)
- {
- SVN_ERR(show_diffstat(shelf_version, scratch_pool));
- }
- SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
}
return SVN_NO_ERROR;
@@ -231,33 +307,28 @@ shelves_list(const char *local_abspath,
static svn_error_t *
shelf_log(const char *name,
const char *local_abspath,
- svn_boolean_t with_diffstat,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_time_t time_now = apr_time_now();
- svn_client_shelf_t *shelf;
+ svn_client__shelf_t *shelf;
+ apr_array_header_t *versions;
int i;
- SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath,
+ SVN_ERR(svn_client__shelf_open_existing(&shelf, name, local_abspath,
ctx, scratch_pool));
-
- for (i = 1; i <= shelf->max_version; i++)
+ 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;
+ svn_client__shelf_version_t *shelf_version
+ = APR_ARRAY_IDX(versions, i, void *);
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, i,
- scratch_pool, scratch_pool));
- SVN_ERR(stats(shelf, i, time_now,
+ SVN_ERR(stats(shelf, i + 1, shelf_version, time_now,
FALSE /*with_logmsg*/, scratch_pool));
- if (with_diffstat)
- {
- SVN_ERR(show_diffstat(shelf_version, scratch_pool));
- }
}
- SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
@@ -284,45 +355,6 @@ name_of_youngest(const char **name_p,
return SVN_NO_ERROR;
}
-/*
- * PATHS are relative to WC_ROOT_ABSPATH.
- */
-static svn_error_t *
-run_status_on_wc_paths(const char *paths_base_abspath,
- const apr_array_header_t *paths,
- svn_depth_t depth,
- const apr_array_header_t *changelists,
- svn_client_status_func_t status_func,
- void *status_baton,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- int i;
-
- for (i = 0; i < paths->nelts; i++)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
- const char *abspath = svn_dirent_join(paths_base_abspath, path,
- scratch_pool);
-
- SVN_ERR(svn_client_status6(NULL /*result_rev*/,
- ctx, abspath,
- NULL /*revision*/,
- depth,
- FALSE /*get_all*/,
- FALSE /*check_out_of_date*/,
- TRUE /*check_working_copy*/,
- TRUE /*no_ignore*/,
- TRUE /*ignore_externals*/,
- FALSE /*depth_as_sticky*/,
- changelists,
- status_func, status_baton,
- scratch_pool));
- }
-
- return SVN_NO_ERROR;
-}
-
struct status_baton
{
/* These fields correspond to the ones in the
@@ -330,9 +362,9 @@ struct status_baton
const char *target_abspath;
const char *target_path;
- const char *header;
- svn_boolean_t quiet; /* don't display statuses while checking them */
- svn_boolean_t modified; /* set to TRUE when any modification is found */
+ 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;
};
@@ -341,7 +373,7 @@ static svn_error_t *
print_status(void *baton,
const char *path,
const svn_client_status_t *status,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
struct status_baton *sb = baton;
unsigned int conflicts;
@@ -355,37 +387,39 @@ print_status(void *baton,
FALSE /*repos_locks*/,
&conflicts, &conflicts, &conflicts,
sb->ctx,
- pool);
+ scratch_pool);
}
-/* Set BATON->modified to true if TARGET has any local modification or
- * any status that means we should not attempt to patch it.
- *
- * A callback of type svn_client_status_func_t. */
+/* A callback function for shelved paths. */
static svn_error_t *
-modification_checker(void *baton,
- const char *target,
- const svn_client_status_t *status,
- apr_pool_t *scratch_pool)
+was_shelved(void *baton,
+ const char *path,
+ const svn_client_status_t *status,
+ apr_pool_t *scratch_pool)
{
struct status_baton *sb = baton;
- if (status->conflicted
- || ! (status->node_status == svn_wc_status_none
- || status->node_status == svn_wc_status_unversioned
- || status->node_status == svn_wc_status_normal))
+ if (!sb->quiet)
{
- if (!sb->quiet)
- {
- if (!sb->modified) /* print the header only once */
- {
- SVN_ERR(svn_cmdline_printf(scratch_pool, "%s", sb->header));
- }
- SVN_ERR(print_status(baton, target, status, scratch_pool));
- }
-
- sb->modified = TRUE;
+ 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;
}
@@ -416,133 +450,168 @@ shelve(int *new_version_p,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- svn_client_shelf_t *shelf;
- int previous_version;
- const char *cwd_abspath;
+ 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(&shelf,
- name, local_abspath, ctx, scratch_pool));
- previous_version = shelf->max_version;
+ 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, previous_version, apr_time_now(),
+ ? _("--- 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.header = (keep_local
- ? _("--- Modifications to save:\n")
- : _("--- Modifications to shelve:\n"));
+ sb.target_abspath = shelf->wc_root_abspath;
+ sb.target_path = "";
sb.quiet = quiet;
- sb.modified = FALSE;
+ sb.num_paths_shelved = 0;
+ sb.num_paths_not_shelved = 0;
sb.ctx = ctx;
- SVN_ERR(svn_dirent_get_absolute(&cwd_abspath, "", scratch_pool));
- SVN_ERR(run_status_on_wc_paths(cwd_abspath, paths, depth, changelists,
- modification_checker, &sb,
- ctx, scratch_pool));
-
- if (!sb.modified)
- {
- SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("No local modifications found"));
- }
if (! quiet)
SVN_ERR(svn_cmdline_printf(scratch_pool,
keep_local ? _("--- Saving...\n")
- : _("--- Shelving...\n")));
- SVN_ERR(svn_client_shelf_save_new_version(shelf,
- paths, depth, changelists,
- scratch_pool));
- if (shelf->max_version == previous_version)
+ : _("--- 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));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- keep_local ? _("None of the local modifications could be saved")
- : _("None of the local modifications could be shelved"));
+ keep_local ? _("No local modifications could be saved")
+ : _("No local modifications could be shelved"));
}
- /* Un-apply the patch, if required. */
+ /* Un-apply the changes, if required. */
if (!keep_local)
{
- svn_client_shelf_version_t *shelf_version;
-
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, shelf->max_version,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_client_shelf_unapply(shelf_version,
+ SVN_ERR(svn_client__shelf_unapply(new_version,
dry_run, scratch_pool));
}
- SVN_ERR(svn_client_shelf_set_log_message(shelf, revprop_table,
- 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_set_current_version(shelf, previous_version,
- scratch_pool));
+ SVN_ERR(svn_client__shelf_delete_newer_versions(shelf, previous_version,
+ scratch_pool));
}
- SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
-/* Throw an error if any paths affected by SHELF:VERSION are currently
- * modified in the WC. */
+/* 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 *
-check_no_modified_paths(const char *paths_base_abspath,
- svn_client_shelf_version_t *shelf_version,
- svn_boolean_t quiet,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
+test_apply(svn_client__shelf_version_t *shelf_version,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
apr_hash_t *paths;
- struct status_baton sb;
apr_hash_index_t *hi;
- sb.target_abspath = shelf_version->shelf->wc_root_abspath;
- sb.target_path = "";
- sb.header = _("--- Paths modified in shelf and in WC:\n");
- sb.quiet = quiet;
- sb.modified = FALSE;
- sb.ctx = ctx;
-
- SVN_ERR(svn_client_shelf_paths_changed(&paths, shelf_version,
+ 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);
- const char *abspath = svn_dirent_join(paths_base_abspath, path,
- scratch_pool);
+ svn_boolean_t conflict;
- SVN_ERR(svn_client_status6(NULL /*result_rev*/,
- ctx, abspath,
- NULL /*revision*/,
- svn_depth_empty,
- FALSE /*get_all*/,
- FALSE /*check_out_of_date*/,
- TRUE /*check_working_copy*/,
- TRUE /*no_ignore*/,
- TRUE /*ignore_externals*/,
- FALSE /*depth_as_sticky*/,
- NULL /*changelists*/,
- modification_checker, &sb,
- scratch_pool));
- }
- if (sb.modified)
- {
- return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("Cannot unshelve/restore, as at least one "
- "path is modified in shelf and in WC"));
+ 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;
}
@@ -553,32 +622,40 @@ check_no_modified_paths(const char *path
* 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_client__shelf_t *shelf;
+ svn_client__shelf_version_t *shelf_version;
- SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath,
+ 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)
@@ -586,29 +663,33 @@ shelf_restore(const char *name,
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, time_now,
+ SVN_ERR(stats(shelf, version, shelf_version, time_now,
TRUE /*with_logmsg*/, scratch_pool));
}
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, version,
- scratch_pool, scratch_pool));
- SVN_ERR(check_no_modified_paths(shelf->wc_root_abspath,
- shelf_version, quiet, ctx, 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,
+ SVN_ERR(svn_client__shelf_apply(shelf_version,
dry_run, scratch_pool));
if (! dry_run)
{
- SVN_ERR(svn_client_shelf_set_current_version(shelf, version,
- scratch_pool));
+ 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,
- _("restored '%s' version %d and deleted %d newer versions\n"),
+ 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,
@@ -616,7 +697,7 @@ shelf_restore(const char *name,
name, version));
}
- SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
@@ -624,35 +705,79 @@ 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)
{
- int version;
- svn_client_shelf_t *shelf;
- svn_client_shelf_version_t *shelf_version;
- svn_stream_t *stream;
+ 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,
+ 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
{
- version = shelf->max_version;
+ SVN_ERR(get_newest_version_existing(&shelf_version, shelf,
+ scratch_pool, scratch_pool));
}
- SVN_ERR(svn_client_shelf_version_open(&shelf_version,
- shelf, version,
- scratch_pool, scratch_pool));
SVN_ERR(svn_stream_for_stdout(&stream, scratch_pool));
- SVN_ERR(svn_client_shelf_export_patch(shelf_version, 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));
+ SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
@@ -665,7 +790,7 @@ shelf_drop(const char *name,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- SVN_ERR(svn_client_shelf_delete(name, local_abspath, dry_run,
+ SVN_ERR(svn_client__shelf_delete(name, local_abspath, dry_run,
ctx, scratch_pool));
if (! quiet)
SVN_ERR(svn_cmdline_printf(scratch_pool,
@@ -747,35 +872,35 @@ svn_cl__shelf_shelve(apr_getopt_t *os,
opt_state->targets,
ctx, FALSE, pool));
{
- int new_version;
- svn_error_t *err;
+ 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 (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));
- }
+ {
+ 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;
@@ -821,8 +946,15 @@ svn_cl__shelf_unshelve(apr_getopt_t *os,
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;
}
@@ -834,18 +966,139 @@ svn_cl__shelf_list(apr_getopt_t *os,
{
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;
+ apr_array_header_t *targets = NULL;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ int i;
- /* 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_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ /* Add "." if user passed 0 arguments */
+ svn_opt_push_implicit_dot_target(targets, pool);
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
- SVN_ERR(shelves_list(local_abspath,
- opt_state->quiet,
- opt_state->verbose /*with_diffstat*/,
- ctx, 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. */
+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;
}
@@ -855,6 +1108,7 @@ 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;
@@ -872,7 +1126,10 @@ svn_cl__shelf_diff(apr_getopt_t *os,
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Too many arguments"));
- SVN_ERR(shelf_diff(name, arg, local_abspath, ctx, pool));
+ 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;
}
@@ -886,18 +1143,31 @@ svn_cl__shelf_drop(apr_getopt_t *os,
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;
- const char *local_abspath;
+ 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));
- /* 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_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
- SVN_ERR(shelf_drop(name, local_abspath,
- opt_state->dry_run, opt_state->quiet,
- ctx, 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;
}
@@ -911,19 +1181,29 @@ svn_cl__shelf_log(apr_getopt_t *os,
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;
- const char *local_abspath;
+ 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));
- /* There should be no remaining arguments. */
- if (os->ind < os->argc)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0,
- _("Too many arguments"));
+ 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_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
- SVN_ERR(shelf_log(name, local_abspath,
- opt_state->verbose /*with_diffstat*/,
- ctx, 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;
}