You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2017/07/11 11:55:45 UTC

svn commit: r1801595 - in /subversion/branches/shelve-checkpoint: subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_wc/ subversion/svn/ tools/client-side/

Author: julianfoad
Date: Tue Jul 11 11:55:45 2017
New Revision: 1801595

URL: http://svn.apache.org/viewvc?rev=1801595&view=rev
Log:
Implement svn shelving, in prototype form.

Commands are:
    svn shelve NAME PATH...
    svn shelve --delete NAME
    svn shelves OR svn shelve --list
    svn unshelve [--keep] NAME

* subversion/include/private/svn_wc_private.h,
  subversion/libsvn_wc/wcroot_anchor.c
  (svn_wc__get_shelves_dir): New function.

* subversion/include/svn_client.h
  (svn_client_shelve,
   svn_client_unshelve,
   svn_client_shelves_delete,
   svn_client_shelves_list): New functions.

* subversion/libsvn_client/shelve.c
  New file.

* subversion/svn/cl.h
  (svn_cl__opt_state_t): Add the '--list' option.
  (svn_cl__shelve,
   svn_cl__unshelve,
   svn_cl__shelves): New.

* subversion/svn/shelve-cmd.c
  New file.

* subversion/svn/svn.c
  Add the new commands and options.

* tools/client-side/bash_completion
  (_svn): Add the 'shelve' commands and options.

Added:
    subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c   (with props)
    subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c   (with props)
Modified:
    subversion/branches/shelve-checkpoint/subversion/include/private/svn_wc_private.h
    subversion/branches/shelve-checkpoint/subversion/include/svn_client.h
    subversion/branches/shelve-checkpoint/subversion/libsvn_wc/wcroot_anchor.c
    subversion/branches/shelve-checkpoint/subversion/svn/cl.h
    subversion/branches/shelve-checkpoint/subversion/svn/svn.c
    subversion/branches/shelve-checkpoint/tools/client-side/bash_completion

Modified: subversion/branches/shelve-checkpoint/subversion/include/private/svn_wc_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/include/private/svn_wc_private.h?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/include/private/svn_wc_private.h (original)
+++ subversion/branches/shelve-checkpoint/subversion/include/private/svn_wc_private.h Tue Jul 11 11:55:45 2017
@@ -348,6 +348,14 @@ svn_wc__get_wcroot(const char **wcroot_a
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool);
 
+/*  */
+svn_error_t *
+svn_wc__get_shelves_dir(char **dir,
+                        svn_wc_context_t *wc_ctx,
+                        const char *local_abspath,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
+
 /**
  * The following are temporary APIs to aid in the transition from wc-1 to
  * wc-ng.  Use them for new development now, but they may be disappearing

Modified: subversion/branches/shelve-checkpoint/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/include/svn_client.h?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/include/svn_client.h (original)
+++ subversion/branches/shelve-checkpoint/subversion/include/svn_client.h Tue Jul 11 11:55:45 2017
@@ -6716,6 +6716,61 @@ svn_client_cat(svn_stream_t *out,
 
 
 
+/** Shelving commands
+ *
+ * @defgroup svn_client_shelve_funcs Client Shelving Functions
+ * @{
+ */
+
+/**
+ *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_shelve(const char *shelf_name,
+                  const apr_array_header_t *paths,
+                  svn_depth_t depth,
+                  const apr_array_header_t *changelists,
+                  svn_boolean_t dry_run,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *pool);
+
+/**
+ *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_unshelve(const char *shelf_name,
+                    const char *local_abspath,
+                    svn_boolean_t keep,
+                    svn_boolean_t dry_run,
+                    svn_client_ctx_t *ctx,
+                    apr_pool_t *pool);
+
+/**
+ *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_shelves_delete(const char *shelf_name,
+                          const char *local_abspath,
+                          svn_boolean_t dry_run,
+                          svn_client_ctx_t *ctx,
+                          apr_pool_t *pool);
+
+/**
+ *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_shelves_list(apr_hash_t **dirents,
+                        const char *local_abspath,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
+
+/** @} */
+
 /** Changelist commands
  *
  * @defgroup svn_client_changelist_funcs Client Changelist Functions

Added: subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c?rev=1801595&view=auto
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c (added)
+++ subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c Tue Jul 11 11:55:45 2017
@@ -0,0 +1,317 @@
+/*
+ * shelve.c:  implementation of the '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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_client.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_hash.h"
+#include "svn_utf.h"
+
+#include "client.h"
+#include "private/svn_wc_private.h"
+#include "svn_private_config.h"
+
+
+/*  */
+static svn_error_t *
+validate_shelf_name(const char *shelf_name,
+                    apr_pool_t *scratch_pool)
+{
+  if (shelf_name[0] == '\0' || strchr(shelf_name, '/'))
+    return svn_error_createf(SVN_ERR_BAD_CHANGELIST_NAME, NULL,
+                             _("Shelve: Bad name '%s'"), shelf_name);
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+get_patch_abspath(char **patch_abspath,
+                  const char *local_path,
+                  const char *shelf_name,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  char *dir;
+  const char *local_abspath;
+  const char *filename;
+
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, local_path, scratch_pool));
+  SVN_ERR(svn_wc__get_shelves_dir(&dir, ctx->wc_ctx, local_abspath,
+                                  scratch_pool, scratch_pool));
+  filename = apr_pstrcat(scratch_pool, shelf_name, ".patch", SVN_VA_NULL);
+  *patch_abspath = svn_dirent_join(dir, filename, result_pool);
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+write_patch(const char *patch_abspath,
+            const apr_array_header_t *paths,
+            svn_depth_t depth,
+            const apr_array_header_t *changelists,
+            svn_client_ctx_t *ctx,
+            apr_pool_t *scratch_pool)
+{
+  svn_stream_t *outstream;
+  svn_stream_t *errstream;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int i;
+  svn_opt_revision_t peg_revision = {svn_opt_revision_unspecified, {0}};
+  svn_opt_revision_t start_revision = {svn_opt_revision_base, {0}};
+  svn_opt_revision_t end_revision = {svn_opt_revision_working, {0}};
+
+  /* Get streams for the output and any error output of the diff. */
+  SVN_ERR(svn_stream_open_writable(&outstream, patch_abspath,
+                                   scratch_pool, scratch_pool));
+  SVN_ERR(svn_stream_for_stderr(&errstream, scratch_pool));
+
+  for (i = 0; i < paths->nelts; i++)
+    {
+      const char *path = APR_ARRAY_IDX(paths, i, const char *);
+
+      if (svn_path_is_url(path))
+        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                                 _("'%s' is not a local path"), path);
+
+      SVN_ERR(svn_client_diff_peg6(
+                     NULL /*options*/,
+                     path,
+                     &peg_revision,
+                     &start_revision,
+                     &end_revision,
+                     NULL,
+                     depth,
+                     TRUE /*notice_ancestry*/,
+                     FALSE /*no_diff_added*/,
+                     FALSE /*no_diff_deleted*/,
+                     TRUE /*show_copies_as_adds*/,
+                     FALSE /*ignore_content_type: FALSE -> omit binary files*/,
+                     FALSE /*ignore_properties*/,
+                     FALSE /*properties_only*/,
+                     FALSE /*use_git_diff_format*/,
+                     SVN_APR_LOCALE_CHARSET,
+                     outstream,
+                     errstream,
+                     changelists,
+                     ctx, iterpool));
+    }
+  SVN_ERR(svn_stream_close(outstream));
+  SVN_ERR(svn_stream_close(errstream));
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+apply_patch(const char *patch_abspath,
+            const char *wc_dir_abspath,
+            svn_boolean_t reverse,
+            svn_boolean_t dry_run,
+            svn_client_ctx_t *ctx,
+            apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_client_patch(patch_abspath, wc_dir_abspath,
+                           dry_run, 0 /*strip*/,
+                           reverse,
+                           FALSE /*ignore_whitespace*/,
+                           TRUE /*remove_tempfiles*/, NULL, NULL,
+                           ctx, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+delete_patch(const char *patch_abspath,
+             apr_pool_t *pool)
+{
+  SVN_ERR(svn_io_remove_file2(patch_abspath, FALSE /*ignore_enoent*/, pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_shelve(const char *shelf_name,
+                  const apr_array_header_t *paths,
+                  svn_depth_t depth,
+                  const apr_array_header_t *changelists,
+                  svn_boolean_t dry_run,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *pool)
+{
+  const char *local_abspath;
+  const char *wc_root_abspath;
+  char *patch_abspath;
+  svn_error_t *err;
+
+  SVN_ERR(validate_shelf_name(shelf_name, 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(paths, 0, char *), pool));
+  SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
+                                 local_abspath, ctx, pool, pool));
+  SVN_ERR(get_patch_abspath(&patch_abspath,
+                            wc_root_abspath, shelf_name, ctx, pool, pool));
+
+  err = write_patch(patch_abspath, paths, depth, changelists, ctx, pool);
+  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
+    {
+      return svn_error_quick_wrapf(err,
+                                   "Shelved change '%s' already exists",
+                                   shelf_name);
+    }
+  else
+    SVN_ERR(err);
+
+  /* Reverse-apply the patch. This should be a safer way to remove those
+     changes from the WC than running a 'revert' operation. */
+  SVN_ERR(apply_patch(patch_abspath, wc_root_abspath,
+                      TRUE /*reverse*/,
+                      dry_run,
+                      ctx, pool));
+
+  if (dry_run)
+    {
+      SVN_ERR(delete_patch(patch_abspath, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_unshelve(const char *shelf_name,
+                    const char *local_abspath,
+                    svn_boolean_t keep,
+                    svn_boolean_t dry_run,
+                    svn_client_ctx_t *ctx,
+                    apr_pool_t *pool)
+{
+  const char *wc_root_abspath;
+  char *patch_abspath;
+  svn_error_t *err;
+
+  SVN_ERR(validate_shelf_name(shelf_name, pool));
+
+  SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
+                                 local_abspath, ctx, pool, pool));
+  SVN_ERR(get_patch_abspath(&patch_abspath,
+                            local_abspath, shelf_name, ctx, pool, pool));
+
+  /* Apply the patch. */
+  err = apply_patch(patch_abspath, wc_root_abspath,
+                    FALSE /*reverse*/,
+                    dry_run /*dry_run*/,
+                    ctx, pool);
+  if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
+    {
+      return svn_error_quick_wrapf(err,
+                                   "Shelved change '%s' not found",
+                                   shelf_name);
+    }
+  else
+    SVN_ERR(err);
+
+  /* Remove the patch. */
+  if (! keep && ! dry_run)
+    {
+      SVN_ERR(delete_patch(patch_abspath, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_shelves_delete(const char *shelf_name,
+                          const char *local_abspath,
+                          svn_boolean_t dry_run,
+                          svn_client_ctx_t *ctx,
+                          apr_pool_t *pool)
+{
+  const char *wc_root_abspath;
+  char *patch_abspath;
+
+  SVN_ERR(validate_shelf_name(shelf_name, pool));
+
+  SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
+                                 local_abspath, ctx, pool, pool));
+  SVN_ERR(get_patch_abspath(&patch_abspath,
+                            local_abspath, shelf_name, ctx, pool, pool));
+
+  /* Remove the patch. */
+  if (! dry_run)
+    {
+      svn_error_t *err;
+
+      err = delete_patch(patch_abspath, pool);
+      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+        {
+          return svn_error_quick_wrapf(err,
+                                       "Shelved change '%s' not found",
+                                       shelf_name);
+        }
+      else
+        SVN_ERR(err);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_shelves_list(apr_hash_t **dirents,
+                        const char *local_abspath,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
+{
+  char *shelves_dir;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_wc__get_shelves_dir(&shelves_dir, ctx->wc_ctx, local_abspath,
+                                  scratch_pool, scratch_pool));
+  SVN_ERR(svn_io_get_dirents3(dirents, shelves_dir, TRUE /*only_check_type*/,
+                              result_pool, scratch_pool));
+
+  /* Remove non-shelves */
+  for (hi = apr_hash_first(scratch_pool, *dirents); hi; hi = apr_hash_next(hi))
+    {
+      const char *name = apr_hash_this_key(hi);
+
+      if (! strstr(name, ".patch"))
+        {
+          svn_hash_sets(*dirents, name, NULL);
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+

Propchange: subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/shelve-checkpoint/subversion/libsvn_wc/wcroot_anchor.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/libsvn_wc/wcroot_anchor.c?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/libsvn_wc/wcroot_anchor.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/libsvn_wc/wcroot_anchor.c Tue Jul 11 11:55:45 2017
@@ -183,6 +183,26 @@ svn_wc__get_wcroot(const char **wcroot_a
 
 
 svn_error_t *
+svn_wc__get_shelves_dir(char **dir,
+                        svn_wc_context_t *wc_ctx,
+                        const char *local_abspath,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
+{
+  const char *wc_adm_dir;
+
+  SVN_ERR(svn_wc__get_wcroot(&wc_adm_dir, wc_ctx, local_abspath,
+                             scratch_pool, scratch_pool));
+  *dir = svn_dirent_join(wc_adm_dir, ".svn/shelves", result_pool);
+  
+  /* Ensure the directory exists. (Other versions of svn don't create it.) */
+  SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
 svn_wc_get_actual_target2(const char **anchor,
                           const char **target,
                           svn_wc_context_t *wc_ctx,

Modified: subversion/branches/shelve-checkpoint/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/cl.h?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/cl.h (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/cl.h Tue Jul 11 11:55:45 2017
@@ -250,6 +250,7 @@ typedef struct svn_cl__opt_state_t
   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 list;
 } svn_cl__opt_state_t;
 
 /* Conflict stats for operations such as update and merge. */
@@ -297,6 +298,9 @@ 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,

Added: subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c?rev=1801595&view=auto
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c (added)
+++ subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c Tue Jul 11 11:55:45 2017
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ * ====================================================================
+ */
+
+#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"
+
+
+/* First argument should be the name of a shelve. */
+static svn_error_t *
+get_shelf_name(const char **shelf_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(shelf_name,
+                                  APR_ARRAY_IDX(args, 0, const char *),
+                                  result_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Display a list of shelves */
+static svn_error_t *
+shelves_list(const char *local_abspath,
+             svn_client_ctx_t *ctx,
+             apr_pool_t *pool)
+{
+  apr_hash_t *dirents;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_client_shelves_list(&dirents, local_abspath, ctx, pool, pool));
+
+  for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
+    {
+      const char *name = apr_hash_this_key(hi);
+
+      if (strstr(name, ".patch"))
+        {
+          printf("%s\n", name);
+        }
+    }
+
+  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 *shelf_name;
+  apr_array_header_t *targets;
+
+  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, ctx, pool));
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(get_shelf_name(&shelf_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(shelf_name, local_abspath,
+                                        opt_state->dry_run,
+                                        ctx, pool));
+      if (! opt_state->quiet)
+        SVN_ERR(svn_cmdline_printf(pool, "deleted '%s'\n", shelf_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_depth_t depth = opt_state->depth;
+
+      /* 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));
+
+      SVN_ERR(svn_client_shelve(shelf_name,
+                                targets, depth, opt_state->changelists,
+                                opt_state->dry_run,
+                                ctx, pool));
+      if (! opt_state->quiet)
+        SVN_ERR(svn_cmdline_printf(pool, "shelved '%s'\n", shelf_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 *shelf_name;
+  apr_array_header_t *targets;
+  const char *local_abspath;
+
+  SVN_ERR(get_shelf_name(&shelf_name, os, pool, pool));
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+  /* 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(shelf_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", shelf_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_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, ctx, pool));
+
+  return SVN_NO_ERROR;
+}

Propchange: subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/shelve-checkpoint/subversion/svn/svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/svn.c?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/svn.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/svn.c Tue Jul 11 11:55:45 2017
@@ -143,7 +143,8 @@ typedef enum svn_cl__longopt_t {
   opt_show_passwords,
   opt_pin_externals,
   opt_show_item,
-  opt_adds_as_modification
+  opt_adds_as_modification,
+  opt_list
 } svn_cl__longopt_t;
 
 
@@ -459,6 +460,12 @@ const apr_getopt_option_t svn_cl__option
                        "                             "
                        "resolve tree conflicts instead.")},
 
+  {"list", opt_list, 0, N_("list shelved patches")},
+
+  /* ### should have new option codes, and not be aliases */
+  {"keep", opt_keep_local, 0, N_("do not delete the shelved patch")},
+  {"delete", opt_remove, 0, N_("just delete the shelved patch")},
+
   /* Long-opt Aliases
    *
    * These have NULL desriptions, but an option code that matches some
@@ -1631,6 +1638,28 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  the output of 'svn help merge' for 'undo'.\n"),
     {opt_targets, 'R', opt_depth, 'q', opt_changelist} },
 
+  { "shelve", svn_cl__shelve, {0}, N_
+    ("Shelve changes.\n"
+     "usage: 1. shelve NAME PATH...\n"
+     "       2. shelve --delete NAME\n"
+     "       3. shelve --list\n"
+     "\n"
+     "  1. Shelve as NAME the local changes in the given PATHs.\n"
+     "  2. Delete the shelved patch NAME.\n"
+     "  3. List shelved patches.\n"),
+    {'q', opt_remove, opt_list, opt_dry_run,
+     'N', opt_depth, opt_targets, opt_changelist} },
+
+  { "unshelve", svn_cl__unshelve, {0}, N_
+    ("Unshelve changes.\n"
+     "usage: unshelve [--keep] NAME\n"),
+    {'q', opt_keep_local, opt_dry_run} },
+
+  { "shelves", svn_cl__shelves, {0}, N_
+    ("List shelved patches.\n"
+     "usage: shelves\n"),
+    {} },
+
   { "status", svn_cl__status, {"stat", "st"}, N_
     ("Print the status of working copy files and directories.\n"
      "usage: status [PATH...]\n"
@@ -2177,6 +2206,9 @@ sub_main(int *exit_code, int argc, const
       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;

Modified: subversion/branches/shelve-checkpoint/tools/client-side/bash_completion
URL: http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/tools/client-side/bash_completion?rev=1801595&r1=1801594&r2=1801595&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/tools/client-side/bash_completion (original)
+++ subversion/branches/shelve-checkpoint/tools/client-side/bash_completion Tue Jul 11 11:55:45 2017
@@ -248,6 +248,7 @@ _svn()
 	cmds="$cmds patch propdel pdel propedit pedit propget pget proplist"
 	cmds="$cmds plist propset pset relocate resolve resolved revert status"
 	cmds="$cmds switch unlock update upgrade"
+	cmds="$cmds shelve shelves unshelve"
 
 	# help options have a strange command status...
 	local helpOpts='--help -h'
@@ -264,6 +265,7 @@ _svn()
 	optsParam="$optsParam|--depth|--set-depth|--with-revprop"
 	optsParam="$optsParam|--cl|--changelist|--accept|--show-revs"
 	optsParam="$optsParam|--show-item"
+	optsParam="$optsParam|--name"
 
 	# svn:* and other (env SVN_BASH_*_PROPS) properties
 	local svnProps revProps allProps psCmds propCmds
@@ -1018,6 +1020,15 @@ _svn()
 	upgrade)
 		cmdOpts="$qOpts $pOpts"
 		;;
+	shelve)
+		cmdOpts="$qOpts --delete --list --dry-run $nOpts --targets $cOpts"
+		;;
+	unshelve)
+		cmdOpts="$qOpts --keep --dry-run"
+		;;
+	shelves)
+		cmdOpts="$qOpts"
+		;;
 	*)
 		;;
 	esac