Modified: subversion/branches/shelve-checkpoint/subversion/svn/auth-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/auth-cmd.c?rev=1829257&r1=1829256&r2=1829257&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/auth-cmd.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/auth-cmd.c Mon Apr 16 
12:21:02 2018
@@ -455,12 +455,15 @@ svn_cl__auth(apr_getopt_t *os, void *bat
         {
           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 *bat
                                    "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;

Modified: subversion/branches/shelve-checkpoint/subversion/svn/cl.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/cl.h?rev=1829257&r1=1829256&r2=1829257&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/cl.h (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/cl.h Mon Apr 16 
12:21:02 2018
@@ -256,7 +256,8 @@ typedef struct svn_cl__opt_state_t
   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_boolean_t viewspec;
 } svn_cl__opt_state_t;
 
 /* Conflict stats for operations such as update and merge. */
@@ -307,13 +308,11 @@ svn_opt_subcommand_t
   svn_cl__shelf_diff,
   svn_cl__shelf_drop,
   svn_cl__shelf_list,
+  svn_cl__shelf_list_by_paths,
   svn_cl__shelf_log,
   svn_cl__shelf_save,
   svn_cl__shelf_shelve,
   svn_cl__shelf_unshelve,
-  svn_cl__shelve,
-  svn_cl__unshelve,
-  svn_cl__shelves,
   svn_cl__status,
   svn_cl__switch,
   svn_cl__unlock,
@@ -322,7 +321,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[];

Modified: subversion/branches/shelve-checkpoint/subversion/svn/help-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/help-cmd.c?rev=1829257&r1=1829256&r2=1829257&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/help-cmd.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/help-cmd.c Mon Apr 16 
12:21:02 2018
@@ -166,7 +166,7 @@ svn_cl__help(apr_getopt_t *os,
                              pool);
 #endif
     }
-#ifdef SVN_HAVE_GNOME_KEYRING
+#if (defined(SVN_HAVE_GNOME_KEYRING) || defined(SVN_HAVE_LIBSECRET))
   svn_stringbuf_appendcstr(version_footer, "* Gnome Keyring\n");
 #endif
 #ifdef SVN_HAVE_GPG_AGENT
@@ -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,

Modified: subversion/branches/shelve-checkpoint/subversion/svn/info-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/info-cmd.c?rev=1829257&r1=1829256&r2=1829257&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/info-cmd.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/info-cmd.c Mon Apr 16 
12:21:02 2018
@@ -45,6 +45,245 @@
 
 /*** 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;
+}
+
+static svn_error_t *
+cl_layout_list(apr_array_header_t *targets,
+               void *baton,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *scratch_pool)
+{
+  const char *list_path, *list_abspath;
+  struct layout_list_baton_t llb;
+
+  /* Add "." if user passed 0 arguments */
+  svn_opt_push_implicit_dot_target(targets, scratch_pool);
+
+  if (targets->nelts > 1)
+    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+  list_path = APR_ARRAY_IDX(targets, 0, const char *);
+
+  SVN_ERR(svn_cl__check_target_is_local_path(list_path));
+
+  SVN_ERR(svn_dirent_get_absolute(&list_abspath, list_path,
+                                  scratch_pool));
+
+  llb.checkout = TRUE;
+  llb.target = list_path;
+  llb.target_abspath = list_abspath;
+  llb.with_revs = TRUE;
+
+  if (TRUE)
+    {
+      /* 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));
+    }
+  else
+    {
+      /* svn command-line format */
+      SVN_ERR(svn_client_layout_list(list_abspath,
+                                     output_svn_command_line, &llb,
+                                     ctx, scratch_pool));
+    }
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 svn_cl__info_print_time(apr_time_t atime,
                         const char *desc,
@@ -110,7 +349,9 @@ 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_t;
 
 /* Mapping between option keywords and info_item_t. */
@@ -133,7 +374,9 @@ static const info_item_map_t info_item_m
                                           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 },
   };
 #undef MAKE_STRING
 
@@ -888,6 +1131,20 @@ 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;
+
     default:
       SVN_ERR_MALFUNCTION();
     }
@@ -918,6 +1175,12 @@ svn_cl__info(apr_getopt_t *os,
                                                       opt_state->targets,
                                                       ctx, FALSE, pool));
 
+  if (opt_state->viewspec)
+    {
+      SVN_ERR(cl_layout_list(targets, baton, ctx, pool));
+      return SVN_NO_ERROR;
+    }
+
   /* Add "." if user passed 0 arguments. */
   svn_opt_push_implicit_dot_target(targets, pool);
 

Modified: subversion/branches/shelve-checkpoint/subversion/svn/shelf-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/shelf-cmd.c?rev=1829257&r1=1829256&r2=1829257&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/shelf-cmd.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/shelf-cmd.c Mon Apr 16 
12:21:02 2018
@@ -28,7 +28,9 @@
 #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_utf.h"
 
 #include "cl.h"
@@ -53,45 +55,107 @@ 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
@@ -127,45 +191,39 @@ list_sorted_by_date(apr_array_header_t *
 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)
 {
-  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,
                                          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)
     {
@@ -188,7 +246,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)
 {
@@ -208,18 +265,13 @@ shelves_list(const char *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));
-      if (quiet)
+      SVN_ERR(svn_client_shelf_get_newest_version(&shelf_version, shelf,
+                                                  scratch_pool, scratch_pool));
+      if (quiet || !shelf_version)
         SVN_ERR(svn_cmdline_printf(scratch_pool, "%s\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));
     }
 
@@ -231,30 +283,25 @@ 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;
+  apr_array_header_t *versions;
   int i;
 
   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));
@@ -417,13 +464,16 @@ shelve(int *new_version_p,
        apr_pool_t *scratch_pool)
 {
   svn_client_shelf_t *shelf;
-  int previous_version;
+  svn_client_shelf_version_t *previous_version;
+  svn_client_shelf_version_t *new_version;
   const char *cwd_abspath;
   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)
     {
@@ -431,7 +481,7 @@ shelve(int *new_version_p,
         ? _("--- 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(),
+      SVN_ERR(stats(shelf, shelf->max_version, previous_version, 
apr_time_now(),
                     TRUE /*with_logmsg*/, scratch_pool));
     }
 
@@ -457,10 +507,10 @@ shelve(int *new_version_p,
     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)
+  SVN_ERR(svn_client_shelf_save_new_version2(&new_version, shelf,
+                                             paths, depth, changelists,
+                                             scratch_pool));
+  if (! new_version)
     {
       SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
       return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -471,94 +521,112 @@ shelve(int *new_version_p,
   /* Un-apply the patch, 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));
   return SVN_NO_ERROR;
 }
 
-/* Throw an error if any paths affected by SHELF:VERSION are currently
- * modified in the WC. */
+/* 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,
                                          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)
+        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                 _("Conflict in applying shelf '%s' path 
'%s'"),
+                                 shelf_version->shelf->name, path);
     }
   return SVN_NO_ERROR;
 }
 
+/* Intercept patch notifications to detect when there is a conflict */
+struct patch_notify_baton_t
+{
+  svn_wc_notify_func2_t notify_func;
+  void *notify_baton;
+  svn_boolean_t rejects;
+};
+
+/* Intercept patch notifications to detect when there is a conflict */
+static void
+patch_notify(void *baton,
+             const svn_wc_notify_t *notify,
+             apr_pool_t *pool)
+{
+  struct patch_notify_baton_t *b = baton;
+
+  if (notify->action == svn_wc_notify_patch_rejected_hunk)
+    b->rejects = TRUE;
+  b->notify_func(b->notify_baton, notify, pool);
+}
+
 /** 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)
@@ -567,6 +635,7 @@ shelf_restore(const char *name,
   apr_time_t time_now = apr_time_now();
   svn_client_shelf_t *shelf;
   svn_client_shelf_version_t *shelf_version;
+  struct patch_notify_baton_t b;
 
   SVN_ERR(svn_client_shelf_open_existing(&shelf, name, local_abspath,
                                          ctx, scratch_pool));
@@ -575,10 +644,15 @@ shelf_restore(const char *name,
   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(svn_client_shelf_get_newest_version(&shelf_version, shelf,
+                                                  scratch_pool, scratch_pool));
     }
 
   if (! quiet)
@@ -586,30 +660,47 @@ 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 "
+                  "path would conflict"));
+    }
+
+  b.rejects = FALSE;
+  b.notify_func = ctx->notify_func2;
+  b.notify_baton = ctx->notify_baton2;
+  ctx->notify_func2 = patch_notify;
+  ctx->notify_baton2 = &b;
 
   SVN_ERR(svn_client_shelf_apply(shelf_version,
                                  dry_run, scratch_pool));
+  ctx->notify_func2 = b.notify_func;
+  ctx->notify_baton2 = b.notify_baton;
+
+  if (b.rejects)
+    {
+      return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+                              _("Unshelve/restore failed due to conflicts"));
+    }
 
   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"),
-                                   name, version, old_version - version));
+                  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"),
@@ -627,7 +718,6 @@ shelf_diff(const char *name,
            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;
@@ -637,15 +727,18 @@ shelf_diff(const char *name,
 
   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(svn_client_shelf_get_newest_version(&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,
@@ -821,8 +914,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;
 }
 
@@ -843,12 +943,116 @@ svn_cl__shelf_list(apr_getopt_t *os,
   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));
 
   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));
+      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;
+}
+
 /* This implements the `svn_opt_subcommand_t' interface. */
 svn_error_t *
 svn_cl__shelf_diff(apr_getopt_t *os,
@@ -908,7 +1112,6 @@ 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;
   const char *local_abspath;
@@ -922,7 +1125,6 @@ svn_cl__shelf_log(apr_getopt_t *os,
 
   SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
   SVN_ERR(shelf_log(name, local_abspath,
-                    opt_state->verbose /*with_diffstat*/,
                     ctx, pool));
 
   return SVN_NO_ERROR;


Reply via email to