Author: julianfoad
Date: Fri Nov 17 22:02:35 2017
New Revision: 1815634
URL: http://svn.apache.org/viewvc?rev=1815634&view=rev
Log:
On the 'shelve-checkpoint' branch: Implement a step towards a new v1 design.
It includes: basic shelve/save, unshelve/restore, deleting and listing.
It does not: detect whether any paths that are about to be restored are
already modified in the WC; avoid storing a version containing no changes or
changes identical to the previous version; integrate with changelists.
Removed:
subversion/branches/shelve-checkpoint/subversion/libsvn_client/checkpoint.c
subversion/branches/shelve-checkpoint/subversion/svn/checkpoint-cmd.c
Modified:
subversion/branches/shelve-checkpoint/subversion/include/svn_client.h
subversion/branches/shelve-checkpoint/subversion/include/svn_opt.h
subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c
subversion/branches/shelve-checkpoint/subversion/svn/cl.h
subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c
subversion/branches/shelve-checkpoint/subversion/svn/svn.c
subversion/branches/shelve-checkpoint/tools/client-side/bash_completion
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=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/include/svn_client.h
(original)
+++ subversion/branches/shelve-checkpoint/subversion/include/svn_client.h Fri
Nov 17 22:02:35 2017
@@ -6715,148 +6715,170 @@ svn_client_cat(svn_stream_t *out,
-/** Checkpointing commands
+/** Shelves and checkpoints
*
- * @defgroup svn_client_checkpoint_funcs Client Checkpointing Functions
+ * @defgroup svn_client_shelve_checkpoint Shelves and checkpoints
* @{
*/
-/**
+/** A shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
-svn_error_t *
-svn_client_checkpoint_get_current(int *checkpoint_number_p,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool);
+typedef struct svn_client_shelf_t
+{
+ /* Public fields (read-only for public use) */
+ const char *name;
+ const char *log_message;
+ int max_version;
-/**
+ /* Private fields */
+ const char *wc_root_abspath;
+ const char *shelves_dir;
+ svn_client_ctx_t *ctx;
+ apr_pool_t *pool;
+} svn_client_shelf_t;
+
+/** Open an existing shelf or create a new shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_checkpoint_save(int *checkpoint_number,
- const char *local_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_client_shelf_open(svn_client_shelf_t **shelf_p,
+ const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool);
-/**
+/** Close @a shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_checkpoint_restore(int checkpoint_number,
- const char *local_abspath,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool);
+svn_client_shelf_close(svn_client_shelf_t *shelf,
+ apr_pool_t *scratch_pool);
-/**
+/** Delete a shelf, by name.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_checkpoint_delete(int checkpoint_number,
- const char *local_abspath,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool);
+svn_client_shelf_delete(const char *name,
+ const char *local_abspath,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
-/**
+/** Save the local modifications found by @a paths, @a depth,
+ * @a changelists as a new version of @a shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_checkpoint_list(apr_array_header_t **checkpoints,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
-/** @} */
-
+svn_client_shelf_save_new_version(svn_client_shelf_t *shelf,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ apr_pool_t *scratch_pool);
-/** Shelving commands
+/** Apply version @a version of @a shelf to the WC.
*
- * @defgroup svn_client_shelve_funcs Client Shelving Functions
- * @{
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_apply(svn_client_shelf_t *shelf,
+ int version,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool);
-/** Shelve a change.
- *
- * Shelve as @a name the local modifications found by @a paths, @a depth,
- * @a changelists. Revert the shelved change from the WC unless @a keep_local
- * is true.
+/** Reverse-apply the current version of @a shelf to the WC.
*
- * If @a dry_run is true, don't actually do it.
- *
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_shelve(const char *name,
- const apr_array_header_t *paths,
- svn_depth_t depth,
- const apr_array_header_t *changelists,
- svn_boolean_t keep_local,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool);
+svn_client_shelf_unapply(svn_client_shelf_t *shelf,
+ int version,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool);
-/** Unshelve the shelved change @a name.
- *
- * @a local_abspath is any path in the WC and is used to find the WC root.
- * Rename the shelved patch to add a '.bak' extension unless @a keep is true.
+/** Set the current version of @a shelf. Delete all newer versions.
*
- * If @a dry_run is true, don't actually do it.
+ * @since New in 1.X.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_set_current_version(svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *scratch_pool);
+
+/** Set @a *wc_abspaths_p to the files affected by version @a version
+ * of @a shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_unshelve(const char *name,
- const char *local_abspath,
- svn_boolean_t keep,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool);
+svn_client_shelf_get_files(apr_array_header_t **wc_abspaths_p,
+ svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *scratch_pool);
-/** Delete the shelved patch @a name.
+/** Information about one version.
*
- * @a local_abspath is any path in the WC and is used to find the WC root.
+ * @since New in 1.X.
+ */
+typedef struct svn_client_shelf_version_info_t
+{
+ const char *patch_abspath; /* abspath of the patch file */
+ apr_time_t mtime; /* mtime of the patch file */
+} svn_client_shelf_version_info_t;
+
+/** Set @a *info to the files affected by the current version of SHELF.
*
- * If @a dry_run is true, don't actually do it.
+ * @since New in 1.X.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_version_get_info(svn_client_shelf_version_info_t **info,
+ svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Set the log message in SHELF, using the log message callbacks in
+ * the client context.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
-svn_client_shelves_delete(const char *name,
- const char *local_abspath,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool);
+svn_client_shelf_set_log_message(svn_client_shelf_t *shelf,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool);
-/** Information about a shelved patch.
+/** Information about a shelf.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
-typedef struct svn_client_shelved_patch_info_t
+typedef struct svn_client_shelf_info_t
{
- const char *message; /* first line of log message */
- const char *patch_path; /* abspath of the patch file */
- svn_io_dirent2_t *dirent; /* info about the patch file */
- apr_time_t mtime; /* a copy of dirent->mtime */
-} svn_client_shelved_patch_info_t;
+ apr_time_t mtime; /* mtime of the latest change */
+} svn_client_shelf_info_t;
-/** Set *shelved_patches to a hash, keyed by patch name, of pointers to
- * @c svn_client_shelved_patch_info_t structures.
+/** Set *shelved_patches to a hash, keyed by shelf name, of pointers to
+ * @c svn_client_shelf_info_t structures.
*
* @a local_abspath is any path in the WC and is used to find the WC root.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
svn_client_shelves_list(apr_hash_t **shelved_patch_infos,
const char *local_abspath,
@@ -6874,56 +6896,15 @@ svn_client_shelves_list(apr_hash_t **she
*
* @a local_abspath is any path in the WC and is used to find the WC root.
*
- * @since New in 1.11.
+ * @since New in 1.X.
*/
+SVN_EXPERIMENTAL
svn_error_t *
svn_client_shelves_any(svn_boolean_t *any_shelved,
const char *local_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
-/** Write local changes to a patch file for shelved change @a name.
- *
- * @a message: An optional log message.
- * @a wc_root_abspath: The WC root dir.
- * @a overwrite_existing: If a file at @a patch_abspath exists, overwrite it.
- * @a paths, @a depth, @a changelists: The selection of local paths to diff.
- */
-svn_error_t *
-svn_client_shelf_write_patch(const char *name,
- const char *message,
- const char *wc_root_abspath,
- svn_boolean_t overwrite_existing,
- 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);
-
-/** Apply the patch file for shelved change @a name to the WC.
- *
- * @a wc_root_abspath: The WC root dir.
- * @a reverse: Apply the patch in reverse.
- * @a dry_run: Don't really apply the changes, just notify what would be done.
- */
-svn_error_t *
-svn_client_shelf_apply_patch(const char *name,
- const char *wc_root_abspath,
- svn_boolean_t reverse,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool);
-
-/** Delete the patch file for shelved change @a name.
- *
- * @a wc_root_abspath: The WC root dir.
- */
-svn_error_t *
-svn_client_shelf_delete_patch(const char *name,
- const char *wc_root_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool);
-
/** @} */
/** Changelist commands
Modified: subversion/branches/shelve-checkpoint/subversion/include/svn_opt.h
URL:
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/include/svn_opt.h?rev=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/include/svn_opt.h
(original)
+++ subversion/branches/shelve-checkpoint/subversion/include/svn_opt.h Fri Nov
17 22:02:35 2017
@@ -64,7 +64,7 @@ typedef svn_error_t *(svn_opt_subcommand
/** The maximum number of aliases a subcommand can have. */
-#define SVN_OPT_MAX_ALIASES 3
+#define SVN_OPT_MAX_ALIASES 4
/** The maximum number of options that can be accepted by a subcommand. */
#define SVN_OPT_MAX_OPTIONS 50
Modified:
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=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c
(original)
+++ subversion/branches/shelve-checkpoint/subversion/libsvn_client/shelve.c Fri
Nov 17 22:02:35 2017
@@ -23,9 +23,9 @@
/* ==================================================================== */
-
-
-/*** Includes. ***/
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
#include "svn_client.h"
#include "svn_wc.h"
@@ -37,6 +37,7 @@
#include "client.h"
#include "private/svn_wc_private.h"
+#include "private/svn_sorts_private.h"
#include "svn_private_config.h"
@@ -52,39 +53,188 @@ validate_name(const char *name,
return SVN_NO_ERROR;
}
-/* Set *PATCH_ABSPATH to the abspath of the patch file for shelved change
- * NAME, no matter whether it exists.
+/* Set *PATCH_ABSPATH to the abspath of the patch file for SHELF
+ * version VERSION, no matter whether it exists.
*/
static svn_error_t *
-get_patch_abspath(char **patch_abspath,
- const char *name,
- const char *wc_root_abspath,
- svn_client_ctx_t *ctx,
+get_patch_abspath(const char **abspath,
+ svn_client_shelf_t *shelf,
+ int version,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- char *dir;
const char *filename;
- SVN_ERR(svn_wc__get_shelves_dir(&dir, ctx->wc_ctx, wc_root_abspath,
- scratch_pool, scratch_pool));
- filename = apr_pstrcat(scratch_pool, name, ".patch", SVN_VA_NULL);
- *patch_abspath = svn_dirent_join(dir, filename, result_pool);
+ filename = apr_psprintf(scratch_pool, "%s-%03d.patch", shelf->name, version);
+ *abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool);
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_client_shelf_write_patch(const char *name,
- const char *message,
- const char *wc_root_abspath,
- svn_boolean_t overwrite_existing,
- 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)
+/* Set *PATCH_ABSPATH to the abspath of the patch file for SHELF
+ * version VERSION. Error if VERSION is invalid or nonexistent.
+ */
+static svn_error_t *
+get_existing_patch_abspath(const char **abspath,
+ svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (shelf->max_version <= 0)
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("shelf '%s': no versions available"),
+ shelf->name);
+ if (version <= 0 || version > shelf->max_version)
+ return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("shelf '%s' has no version %d: max version is
%d"),
+ shelf->name, version, shelf->max_version);
+
+ SVN_ERR(get_patch_abspath(abspath, shelf, version,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+shelf_delete_patch_file(svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *scratch_pool)
+{
+ const char *patch_abspath;
+
+ SVN_ERR(get_existing_patch_abspath(&patch_abspath, shelf, version,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_io_remove_file2(patch_abspath, TRUE /*ignore_enoent*/,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+get_log_abspath(char **log_abspath,
+ svn_client_shelf_t *shelf,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *filename;
+
+ filename = apr_pstrcat(scratch_pool, shelf->name, ".log", SVN_VA_NULL);
+ *log_abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool);
+ return SVN_NO_ERROR;
+}
+
+/* Set SHELF->log_message by reading from its file storage.
+ *
+ * ### Currently just reads the first line.
+ */
+static svn_error_t *
+shelf_read_log_message(svn_client_shelf_t *shelf,
+ apr_pool_t *result_pool)
+{
+ char *log_abspath;
+ apr_file_t *file;
+ svn_error_t *err;
+ svn_stream_t *stream;
+ svn_boolean_t eof;
+ svn_stringbuf_t *line;
+
+ SVN_ERR(get_log_abspath(&log_abspath, shelf, result_pool, result_pool));
+
+ err = svn_io_file_open(&file, log_abspath,
+ APR_FOPEN_READ,
+ APR_FPROT_OS_DEFAULT, result_pool);
+ if (err && err->apr_err == APR_ENOENT)
+ {
+ shelf->log_message = "";
+ return SVN_NO_ERROR;
+ }
+ else
+ SVN_ERR(err);
+ stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, result_pool);
+
+ SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
+ SVN_ERR(svn_stream_close(stream));
+ shelf->log_message = line->data;
+ return SVN_NO_ERROR;
+}
+
+/* Write SHELF->log_message to its file storage.
+ */
+static svn_error_t *
+shelf_write_log_message(svn_client_shelf_t *shelf,
+ apr_pool_t *scratch_pool)
+{
+ char *log_abspath;
+ apr_file_t *file;
+ svn_stream_t *stream;
+
+ SVN_ERR(get_log_abspath(&log_abspath, shelf, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_io_file_open(&file, log_abspath,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE,
+ APR_FPROT_OS_DEFAULT, scratch_pool));
+ stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, scratch_pool);
+
+ SVN_ERR(svn_stream_puts(stream, shelf->log_message));
+ SVN_ERR(svn_stream_close(stream));
+ return SVN_NO_ERROR;
+}
+
+/* */
+static char *
+get_current_abspath(svn_client_shelf_t *shelf,
+ apr_pool_t *result_pool)
+{
+ const char *current_filename
+ = apr_psprintf(result_pool, "%s.current", shelf->name);
+ return svn_dirent_join(shelf->shelves_dir, current_filename, result_pool);
+}
+
+/* */
+static svn_error_t *
+shelf_read_current(svn_client_shelf_t *shelf,
+ apr_pool_t *scratch_pool)
+{
+ const char *current_abspath = get_current_abspath(shelf, scratch_pool);
+ FILE *fp = fopen(current_abspath, "r");
+
+ if (! fp)
+ {
+ shelf->max_version = 0;
+ return SVN_NO_ERROR;
+ }
+ fscanf(fp, "%d", &shelf->max_version);
+ fclose(fp);
+ return SVN_NO_ERROR;
+}
+
+/* */
+static svn_error_t *
+shelf_write_current(svn_client_shelf_t *shelf,
+ apr_pool_t *scratch_pool)
+{
+ const char *current_abspath = get_current_abspath(shelf, scratch_pool);
+ FILE *fp = fopen(current_abspath, "w");
+
+ fprintf(fp, "%d", shelf->max_version);
+ fclose(fp);
+ return SVN_NO_ERROR;
+}
+
+/** Write local changes to a patch file.
+ *
+ * @a paths, @a depth, @a changelists: The selection of local paths to diff.
+ *
+ * ### TODO: Ignore any external diff cmd as configured in config file.
+ * This might also solve the buffering problem.
+ */
+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)
{
- char *patch_abspath;
apr_int32_t flag;
apr_file_t *outfile;
svn_stream_t *outstream;
@@ -95,34 +245,16 @@ svn_client_shelf_write_patch(const char
svn_opt_revision_t start_revision = {svn_opt_revision_base, {0}};
svn_opt_revision_t end_revision = {svn_opt_revision_working, {0}};
- printf("writing '%s.patch'\n", name);
-
- SVN_ERR(get_patch_abspath(&patch_abspath, name, wc_root_abspath,
- ctx, scratch_pool, scratch_pool));
-
/* Get streams for the output and any error output of the diff. */
/* ### svn_stream_open_writable() doesn't work here: the buffering
goes wrong so that diff headers appear after their hunks.
For now, fix by opening the file without APR_BUFFERED. */
flag = APR_FOPEN_WRITE | APR_FOPEN_CREATE;
- if (! overwrite_existing)
- flag |= APR_FOPEN_EXCL;
SVN_ERR(svn_io_file_open(&outfile, patch_abspath,
flag, APR_FPROT_OS_DEFAULT, scratch_pool));
outstream = svn_stream_from_aprfile2(outfile, FALSE /*disown*/,
scratch_pool);
SVN_ERR(svn_stream_for_stderr(&errstream, scratch_pool));
- /* Write the patch file header (log message, etc.) */
- if (message)
- {
- SVN_ERR(svn_stream_printf(outstream, scratch_pool, "%s\n",
- message));
- }
- SVN_ERR(svn_stream_printf(outstream, scratch_pool,
- "--This line, and those below, will be
ignored--\n\n"));
- SVN_ERR(svn_stream_printf(outstream, scratch_pool,
- "--This patch was generated by 'svn
shelve'--\n\n"));
-
for (i = 0; i < paths->nelts; i++)
{
const char *path = APR_ARRAY_IDX(paths, i, const char *);
@@ -160,210 +292,188 @@ svn_client_shelf_write_patch(const char
}
svn_error_t *
-svn_client_shelf_apply_patch(const char *name,
- const char *wc_root_abspath,
- svn_boolean_t reverse,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- char *patch_abspath;
-
- SVN_ERR(get_patch_abspath(&patch_abspath, name, wc_root_abspath,
- ctx, scratch_pool, scratch_pool));
- SVN_ERR(svn_client_patch(patch_abspath, wc_root_abspath,
- dry_run, 0 /*strip*/,
- reverse,
- FALSE /*ignore_whitespace*/,
- TRUE /*remove_tempfiles*/, NULL, NULL,
- ctx, scratch_pool));
+svn_client_shelf_open(svn_client_shelf_t **shelf_p,
+ const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool)
+{
+ svn_client_shelf_t *shelf = apr_palloc(result_pool, sizeof(*shelf));
+ char *shelves_dir;
+
+ SVN_ERR(validate_name(name, result_pool));
+
+ SVN_ERR(svn_client_get_wc_root(&shelf->wc_root_abspath,
+ local_abspath, ctx,
+ result_pool, result_pool));
+ SVN_ERR(svn_wc__get_shelves_dir(&shelves_dir,
+ ctx->wc_ctx, local_abspath,
+ result_pool, result_pool));
+ shelf->shelves_dir = shelves_dir;
+ shelf->ctx = ctx;
+ shelf->pool = result_pool;
+
+ shelf->name = apr_pstrdup(result_pool, name);
+ SVN_ERR(shelf_read_log_message(shelf, result_pool));
+ SVN_ERR(shelf_read_current(shelf, result_pool));
+ *shelf_p = shelf;
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client_shelf_delete_patch(const char *name,
- const char *wc_root_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
+svn_client_shelf_close(svn_client_shelf_t *shelf,
+ apr_pool_t *scratch_pool)
{
- char *patch_abspath, *to_abspath;
-
- SVN_ERR(get_patch_abspath(&patch_abspath, name, wc_root_abspath,
- ctx, scratch_pool, scratch_pool));
- to_abspath = apr_pstrcat(scratch_pool, patch_abspath, ".bak", SVN_VA_NULL);
-
- /* remove any previous backup */
- SVN_ERR(svn_io_remove_file2(to_abspath, TRUE /*ignore_enoent*/,
- scratch_pool));
-
- /* move the patch to a backup file */
- printf("moving '%s.patch' to '%s.patch.bak'\n", name, name);
- SVN_ERR(svn_io_file_rename2(patch_abspath, to_abspath, FALSE
/*flush_to_disk*/,
- scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client_shelve(const char *name,
- const apr_array_header_t *paths,
- svn_depth_t depth,
- const apr_array_header_t *changelists,
- svn_boolean_t keep_local,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_shelf_delete(const char *name,
+ const char *local_abspath,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- const char *local_abspath;
- const char *wc_root_abspath;
- const char *message = "";
- svn_error_t *err;
+ svn_client_shelf_t *shelf;
+ int i;
+ char *abspath;
- SVN_ERR(validate_name(name, pool));
+ SVN_ERR(validate_name(name, scratch_pool));
- /* ### TODO: check all paths are in same WC; for now use first path */
- SVN_ERR(svn_dirent_get_absolute(&local_abspath,
- APR_ARRAY_IDX(paths, 0, char *), pool));
- SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
- local_abspath, ctx, pool, pool));
+ SVN_ERR(svn_client_shelf_open(&shelf,
+ name, local_abspath, ctx, scratch_pool));
- /* Fetch the log message and any other revprops */
- if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
+ /* Remove the patches. */
+ for (i = shelf->max_version; i > 0; i--)
{
- const char *tmp_file;
- apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(void
*));
-
- SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
- ctx, pool));
- if (! message)
- return SVN_NO_ERROR;
+ SVN_ERR(shelf_delete_patch_file(shelf, i, scratch_pool));
}
- err = svn_client_shelf_write_patch(name, message, wc_root_abspath,
- FALSE /*overwrite_existing*/,
- 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",
- name);
- }
- else
- SVN_ERR(err);
-
- if (!keep_local)
- {
- /* Reverse-apply the patch. This should be a safer way to remove those
- changes from the WC than running a 'revert' operation. */
- SVN_ERR(svn_client_shelf_apply_patch(name, wc_root_abspath,
- TRUE /*reverse*/, dry_run,
- ctx, pool));
- }
-
- if (dry_run)
- {
- SVN_ERR(svn_client_shelf_delete_patch(name, wc_root_abspath,
- ctx, pool));
- }
+ /* Remove the other files */
+ SVN_ERR(get_log_abspath(&abspath, shelf, scratch_pool, scratch_pool));
+ SVN_ERR(svn_io_remove_file2(abspath, TRUE /*ignore_enoent*/, scratch_pool));
+ abspath = get_current_abspath(shelf, scratch_pool);
+ SVN_ERR(svn_io_remove_file2(abspath, TRUE /*ignore_enoent*/, scratch_pool));
+ SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client_unshelve(const char *name,
- const char *local_abspath,
- svn_boolean_t keep,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_shelf_apply(svn_client_shelf_t *shelf,
+ int version,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool)
{
- const char *wc_root_abspath;
- svn_error_t *err;
-
- SVN_ERR(validate_name(name, pool));
+ const char *patch_abspath;
- SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
- local_abspath, ctx, pool, pool));
+ SVN_ERR(get_existing_patch_abspath(&patch_abspath, shelf, version,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_patch(patch_abspath, shelf->wc_root_abspath,
+ dry_run, 0 /*strip*/,
+ FALSE /*reverse*/,
+ FALSE /*ignore_whitespace*/,
+ TRUE /*remove_tempfiles*/, NULL, NULL,
+ shelf->ctx, scratch_pool));
- /* Apply the patch. */
- err = svn_client_shelf_apply_patch(name, wc_root_abspath,
- FALSE /*reverse*/, dry_run,
- ctx, pool);
- if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
- {
- return svn_error_quick_wrapf(err,
- "Shelved change '%s' not found",
- name);
- }
- else
- SVN_ERR(err);
+ return SVN_NO_ERROR;
+}
- /* Remove the patch. */
- if (! keep && ! dry_run)
- {
- SVN_ERR(svn_client_shelf_delete_patch(name, wc_root_abspath,
- ctx, pool));
- }
+svn_error_t *
+svn_client_shelf_unapply(svn_client_shelf_t *shelf,
+ int version,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool)
+{
+ const char *patch_abspath;
+
+ SVN_ERR(get_existing_patch_abspath(&patch_abspath, shelf, version,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_patch(patch_abspath, shelf->wc_root_abspath,
+ dry_run, 0 /*strip*/,
+ TRUE /*reverse*/,
+ FALSE /*ignore_whitespace*/,
+ TRUE /*remove_tempfiles*/, NULL, NULL,
+ shelf->ctx, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client_shelves_delete(const char *name,
- const char *local_abspath,
- svn_boolean_t dry_run,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_shelf_set_current_version(svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *scratch_pool)
{
- const char *wc_root_abspath;
+ int i;
- SVN_ERR(validate_name(name, pool));
+ /* Delete any newer checkpoints */
+ for (i = shelf->max_version; i > version; i--)
+ {
+ SVN_ERR(shelf_delete_patch_file(shelf, i, scratch_pool));
+ }
- SVN_ERR(svn_client_get_wc_root(&wc_root_abspath,
- local_abspath, ctx, pool, pool));
+ shelf->max_version = version;
+ SVN_ERR(shelf_write_current(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
- /* Remove the patch. */
- if (! dry_run)
- {
- svn_error_t *err;
+svn_error_t *
+svn_client_shelf_get_files(apr_array_header_t **wc_abspaths_p,
+ svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *scratch_pool)
+{
- err = svn_client_shelf_delete_patch(name, wc_root_abspath,
- ctx, pool);
- if (err && APR_STATUS_IS_ENOENT(err->apr_err))
- {
- return svn_error_quick_wrapf(err,
- "Shelved change '%s' not found",
- name);
- }
- else
- SVN_ERR(err);
- }
+ return SVN_NO_ERROR;
+}
+svn_error_t *
+svn_client_shelf_save_new_version(svn_client_shelf_t *shelf,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ apr_pool_t *scratch_pool)
+{
+ int next_version = shelf->max_version + 1;
+ const char *patch_abspath;
+
+ SVN_ERR(get_patch_abspath(&patch_abspath, shelf, next_version,
+ scratch_pool, scratch_pool));
+ SVN_ERR(write_patch(patch_abspath,
+ paths, depth, changelists,
+ shelf->ctx, scratch_pool));
+ SVN_ERR(svn_client_shelf_set_current_version(shelf, next_version,
+ scratch_pool));
return SVN_NO_ERROR;
}
-/* Set *LOGMSG to the log message stored in the file PATCH_ABSPATH.
- *
- * ### Currently just reads the first line.
- */
-static svn_error_t *
-read_logmsg_from_patch(const char **logmsg,
- const char *patch_abspath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_error_t *
+svn_client_shelf_set_log_message(svn_client_shelf_t *shelf,
+ svn_boolean_t dry_run,
+ apr_pool_t *scratch_pool)
{
- apr_file_t *file;
- svn_stream_t *stream;
- svn_boolean_t eof;
- svn_stringbuf_t *line;
+ svn_client_ctx_t *ctx = shelf->ctx;
+ const char *message = "";
+
+ /* Fetch the log message and any other revprops */
+ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
+ {
+ const char *tmp_file;
+ apr_array_header_t *commit_items
+ = apr_array_make(scratch_pool, 1, sizeof(void *));
+
+ SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
+ ctx, scratch_pool));
+ if (! message)
+ return SVN_NO_ERROR;
+ }
+ if (message && !dry_run)
+ {
+ shelf->log_message = apr_pstrdup(shelf->pool, message);
+ SVN_ERR(shelf_write_log_message(shelf, scratch_pool));
+ }
- SVN_ERR(svn_io_file_open(&file, patch_abspath,
- APR_FOPEN_READ, APR_FPROT_OS_DEFAULT,
scratch_pool));
- stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, scratch_pool);
- SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
- SVN_ERR(svn_stream_close(stream));
- *logmsg = line->data;
return SVN_NO_ERROR;
}
@@ -374,10 +484,13 @@ svn_client_shelves_list(apr_hash_t **she
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
+ const char *wc_root_abspath;
char *shelves_dir;
apr_hash_t *dirents;
apr_hash_index_t *hi;
+ SVN_ERR(svn_wc__get_wcroot(&wc_root_abspath, ctx->wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
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, FALSE /*only_check_type*/,
@@ -389,21 +502,16 @@ svn_client_shelves_list(apr_hash_t **she
for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
{
const char *filename = apr_hash_this_key(hi);
+ svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
int len = strlen(filename);
- if (len > 6 && strcmp(filename + len - 6, ".patch") == 0)
+ if (len > 6 && strcmp(filename + len - 8, ".current") == 0)
{
- const char *name = apr_pstrndup(result_pool, filename, len - 6);
- svn_client_shelved_patch_info_t *info
+ const char *name = apr_pstrndup(result_pool, filename, len - 8);
+ svn_client_shelf_info_t *info
= apr_palloc(result_pool, sizeof(*info));
- info->dirent = apr_hash_this_val(hi);
- info->mtime = info->dirent->mtime;
- info->patch_path
- = svn_dirent_join(shelves_dir, filename, result_pool);
- SVN_ERR(read_logmsg_from_patch(&info->message, info->patch_path,
- result_pool, scratch_pool));
-
+ info->mtime = dirent->mtime;
svn_hash_sets(*shelved_patch_infos, name, info);
}
}
@@ -424,3 +532,26 @@ svn_client_shelves_any(svn_boolean_t *an
*any_shelved = apr_hash_count(shelved_patch_infos) != 0;
return SVN_NO_ERROR;
}
+
+svn_error_t *
+svn_client_shelf_version_get_info(svn_client_shelf_version_info_t **info_p,
+ svn_client_shelf_t *shelf,
+ int version,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_shelf_version_info_t *info
+ = apr_palloc(result_pool, sizeof(*info));
+ const svn_io_dirent2_t *dirent;
+
+ SVN_ERR(get_patch_abspath(&info->patch_abspath, shelf, version,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_io_stat_dirent2(&dirent,
+ info->patch_abspath,
+ FALSE /*verify_truename*/,
+ TRUE /*ignore_enoent*/,
+ result_pool, scratch_pool));
+ info->mtime = dirent->mtime;
+ *info_p = info;
+ return SVN_NO_ERROR;
+}
\ No newline at end of file
Modified: subversion/branches/shelve-checkpoint/subversion/svn/cl.h
URL:
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/cl.h?rev=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/cl.h (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/cl.h Fri Nov 17
22:02:35 2017
@@ -278,7 +278,6 @@ svn_opt_subcommand_t
svn_cl__changelist,
svn_cl__checkout,
svn_cl__checkpoint,
- svn_cl__checkpoints,
svn_cl__cleanup,
svn_cl__commit,
svn_cl__copy,
Modified: 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=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/shelve-cmd.c Fri Nov
17 22:02:35 2017
@@ -21,6 +21,10 @@
* ====================================================================
*/
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
+
#include "svn_client.h"
#include "svn_error_codes.h"
#include "svn_error.h"
@@ -33,36 +37,53 @@
#include "private/svn_sorts_private.h"
-/* First argument should be the name of a shelved change. */
+/* Fetch the next argument. */
static svn_error_t *
-get_name(const char **name,
- apr_getopt_t *os,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+get_next_argument(const char **arg,
+ apr_getopt_t *os,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_array_header_t *args;
SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
- SVN_ERR(svn_utf_cstring_to_utf8(name,
+ SVN_ERR(svn_utf_cstring_to_utf8(arg,
APR_ARRAY_IDX(args, 0, const char *),
result_pool));
return SVN_NO_ERROR;
}
+/* Return a human-friendly description of the time duration MINUTES.
+ */
+static char *
+friendly_duration_str(int minutes,
+ apr_pool_t *result_pool)
+{
+ char *s;
+
+ if (minutes >= 60 * 24)
+ s = apr_psprintf(result_pool, _("%d days"), minutes / 60 / 24);
+ else if (minutes >= 60)
+ s = apr_psprintf(result_pool, _("%d hours"), minutes / 60);
+ else
+ s = apr_psprintf(result_pool, _("%d minutes"), minutes);
+ return s;
+}
+
/* A comparison function for svn_sort__hash(), comparing the mtime of two
svn_client_shelved_patch_info_t's. */
static int
compare_shelved_patch_infos_by_mtime(const svn_sort__item_t *a,
const svn_sort__item_t *b)
{
- svn_client_shelved_patch_info_t *a_val = a->value;
- svn_client_shelved_patch_info_t *b_val = b->value;
+ svn_client_shelf_info_t *a_val = a->value;
+ svn_client_shelf_info_t *b_val = b->value;
- return (a_val->dirent->mtime < b_val->dirent->mtime)
- ? -1 : (a_val->dirent->mtime > b_val->dirent->mtime) ? 1 : 0;
+ return (a_val->mtime < b_val->mtime)
+ ? -1 : (a_val->mtime > b_val->mtime) ? 1 : 0;
}
-/* Return a list of shelved changes sorted by patch file mtime, oldest first.
+/* Return a list of shelves sorted by patch file mtime, oldest first.
*/
static svn_error_t *
list_sorted_by_date(apr_array_header_t **list,
@@ -80,10 +101,11 @@ list_sorted_by_date(apr_array_header_t *
return SVN_NO_ERROR;
}
-/* Display a list of shelved changes */
+/* Display a list of shelves */
static svn_error_t *
shelves_list(const char *local_abspath,
- svn_boolean_t diffstat,
+ svn_boolean_t with_logmsg,
+ svn_boolean_t with_diffstat,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
@@ -97,26 +119,87 @@ 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_shelved_patch_info_t *info = item->value;
- int age = (apr_time_now() - info->mtime) / 1000000 / 60;
+ svn_client_shelf_t *shelf;
+ svn_client_shelf_version_info_t *info;
+ int age_mins;
+ char *age_str;
+
+ SVN_ERR(svn_client_shelf_open(&shelf,
+ name, local_abspath, ctx, scratch_pool));
+ SVN_ERR(svn_client_shelf_version_get_info(&info,
+ shelf, shelf->max_version,
+ scratch_pool, scratch_pool));
+ age_mins = (apr_time_now() - info->mtime) / 1000000 / 60;
+ age_str = friendly_duration_str(age_mins, scratch_pool);
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("%-30s %s ago, %d versions\n"),
+ name, age_str, shelf->max_version));
+ if (with_logmsg)
+ {
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _(" %.50s\n"),
+ shelf->log_message));
+ }
+
+ if (with_diffstat)
+ {
+#ifndef WIN32
+ system(apr_psprintf(scratch_pool, "diffstat %s 2> /dev/null",
+ info->patch_abspath));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
+#endif
+ }
+ SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Print info about each checkpoint of the shelf named NAME.
+ */
+static svn_error_t *
+checkpoint_list(const char *name,
+ const char *local_abspath,
+ svn_boolean_t diffstat,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_shelf_t *shelf;
+ int i;
- printf("%-30s %6d mins old %10ld bytes\n",
- name, age, (long)info->dirent->filesize);
- printf(" %.50s\n",
- info->message);
+ SVN_ERR(svn_client_shelf_open(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+
+ for (i = 1; i <= shelf->max_version; i++)
+ {
+ svn_client_shelf_version_info_t *info;
+ int age_mins;
+ char *age_str;
+
+ SVN_ERR(svn_client_shelf_version_get_info(&info,
+ shelf, i,
+ scratch_pool, scratch_pool));
+ age_mins = (apr_time_now() - info->mtime) / 1000000 / 60;
+ age_str = friendly_duration_str(age_mins, scratch_pool);
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("version %d: %s ago\n"),
+ i, age_str));
if (diffstat)
{
system(apr_psprintf(scratch_pool, "diffstat %s 2> /dev/null",
- info->patch_path));
- printf("\n");
+ info->patch_abspath));
+ SVN_ERR(svn_cmdline_printf(scratch_pool, "\n"));
}
}
+ SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
-/* Find the name of the youngest shelved change.
+/* Find the name of the youngest shelf.
*/
static svn_error_t *
name_of_youngest(const char **name_p,
@@ -132,10 +215,125 @@ name_of_youngest(const char **name_p,
local_abspath, ctx, scratch_pool));
if (list->nelts == 0)
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
- _("No shelved changes found"));
+ _("No shelves found"));
youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
- *name_p = youngest_item->key;
+ *name_p = apr_pstrdup(result_pool, youngest_item->key);
+ return SVN_NO_ERROR;
+}
+
+/** Shelve/save a new version of changes.
+ *
+ * Shelve in shelf @a name the local modifications found by @a paths,
+ * @a depth, @a changelists. Revert the shelved changes from the WC
+ * unless @a keep_local is true.
+ *
+ * If @a dry_run is true, don't actually do it.
+ */
+static svn_error_t *
+shelve(int *new_version_p,
+ const char *name,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_shelf_t *shelf;
+
+ SVN_ERR(svn_client_shelf_open(&shelf,
+ name, local_abspath, ctx, scratch_pool));
+
+ SVN_ERR(svn_client_shelf_save_new_version(shelf,
+ paths, depth, changelists,
+ scratch_pool));
+ if (!keep_local)
+ {
+ /* Reverse-apply the patch. This should be a safer way to remove those
+ changes from the WC than running a 'revert' operation. */
+ SVN_ERR(svn_client_shelf_unapply(shelf, shelf->max_version,
+ dry_run, scratch_pool));
+ }
+
+ SVN_ERR(svn_client_shelf_set_log_message(shelf, dry_run, scratch_pool));
+
+ if (new_version_p)
+ *new_version_p = shelf->max_version;
+
+ if (dry_run)
+ {
+ SVN_ERR(svn_client_shelf_set_current_version(shelf,
+ shelf->max_version - 1,
+ scratch_pool));
+ }
+
+ SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+/** Restore/unshelve a given or newest version of changes.
+ *
+ * Restore local modifications from shelf @a name version @a arg,
+ * or the newest version is @a arg is null.
+ *
+ * If @a dry_run is true, don't actually do it.
+ */
+static svn_error_t *
+restore(const char *name,
+ const char *arg,
+ svn_boolean_t dry_run,
+ svn_boolean_t quiet,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ int version, old_version;
+ svn_client_shelf_t *shelf;
+
+ SVN_ERR(svn_client_shelf_open(&shelf, name, local_abspath,
+ ctx, scratch_pool));
+ if (shelf->max_version <= 0)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Shelf '%s' not found"),
+ name);
+ }
+
+ old_version = shelf->max_version;
+ if (arg)
+ {
+ SVN_ERR(svn_cstring_atoi(&version, arg));
+ }
+ else
+ {
+ version = shelf->max_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));
+ }
+
+ 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));
+ else
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("restored '%s' version %d (the newest
version)\n"),
+ name, version));
+ }
+
+ SVN_ERR(svn_client_shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
@@ -162,23 +360,25 @@ svn_cl__shelve(apr_getopt_t *os,
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
SVN_ERR(shelves_list(local_abspath,
- ! opt_state->quiet /*diffstat*/,
+ ! opt_state->quiet /*with_logmsg*/,
+ ! opt_state->quiet /*with_diffstat*/,
ctx, pool));
return SVN_NO_ERROR;
}
- SVN_ERR(get_name(&name, os, pool, pool));
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
if (opt_state->remove)
{
if (os->ind < os->argc)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
- SVN_ERR(svn_client_shelves_delete(name, local_abspath,
- opt_state->dry_run,
- ctx, pool));
+ SVN_ERR(svn_client_shelf_delete(name, local_abspath,
+ opt_state->dry_run, ctx, pool));
if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "deleted '%s'\n", name));
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("deleted '%s'\n"),
+ name));
return SVN_NO_ERROR;
}
@@ -190,28 +390,28 @@ svn_cl__shelve(apr_getopt_t *os,
{
svn_depth_t depth = opt_state->depth;
+ int new_version;
svn_error_t *err;
- /* shelve has no implicit dot-target `.', so don't you put that
- code here! */
- if (!targets->nelts)
- return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
-
SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+ /* ### TODO: check all paths are in same WC; for now use first path */
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath,
+ APR_ARRAY_IDX(targets, 0, char *),
+ pool));
if (ctx->log_msg_func3)
SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
opt_state, NULL, ctx->config,
pool));
- err = svn_client_shelve(name,
- targets, depth, opt_state->changelists,
- opt_state->keep_local, opt_state->dry_run,
- ctx, pool);
+ err = shelve(&new_version, name,
+ targets, depth, opt_state->changelists,
+ opt_state->keep_local, opt_state->dry_run,
+ local_abspath, ctx, pool);
if (ctx->log_msg_func3)
SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
err, pool));
@@ -219,7 +419,9 @@ svn_cl__shelve(apr_getopt_t *os,
SVN_ERR(err);
if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "shelved '%s'\n", name));
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("shelved '%s' version %d\n"),
+ name, new_version));
}
return SVN_NO_ERROR;
@@ -229,7 +431,7 @@ svn_cl__shelve(apr_getopt_t *os,
svn_error_t *
svn_cl__unshelve(apr_getopt_t *os,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
@@ -237,7 +439,7 @@ svn_cl__unshelve(apr_getopt_t *os,
const char *name;
apr_array_header_t *targets;
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", scratch_pool));
if (opt_state->list)
{
@@ -245,36 +447,38 @@ svn_cl__unshelve(apr_getopt_t *os,
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
SVN_ERR(shelves_list(local_abspath,
- ! opt_state->quiet /*diffstat*/,
- ctx, pool));
+ ! opt_state->quiet /*with_logmsg*/,
+ ! opt_state->quiet /*with_diffstat*/,
+ ctx, scratch_pool));
return SVN_NO_ERROR;
}
if (os->ind < os->argc)
{
- SVN_ERR(get_name(&name, os, pool, pool));
+ SVN_ERR(get_next_argument(&name, os, scratch_pool, scratch_pool));
}
else
{
- SVN_ERR(name_of_youngest(&name, local_abspath, ctx, pool, pool));
- printf("unshelving the youngest change, '%s'\n", name);
+ SVN_ERR(name_of_youngest(&name,
+ local_abspath, ctx, scratch_pool,
scratch_pool));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("unshelving the youngest change, '%s'\n"),
+ name));
}
/* There should be no remaining arguments. */
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
- ctx, FALSE, pool));
+ ctx, FALSE,
scratch_pool));
if (targets->nelts)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
if (opt_state->quiet)
ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
- SVN_ERR(svn_client_unshelve(name, local_abspath,
- opt_state->keep_local, opt_state->dry_run,
- ctx, pool));
- if (! opt_state->quiet)
- SVN_ERR(svn_cmdline_printf(pool, "unshelved '%s'\n", name));
+ SVN_ERR(restore(name, NULL,
+ opt_state->dry_run, opt_state->quiet,
+ local_abspath, ctx, scratch_pool));
return SVN_NO_ERROR;
}
@@ -285,6 +489,7 @@ svn_cl__shelves(apr_getopt_t *os,
void *baton,
apr_pool_t *pool)
{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
const char *local_abspath;
@@ -293,7 +498,113 @@ svn_cl__shelves(apr_getopt_t *os,
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, TRUE /*diffstat*/, ctx, pool));
+ SVN_ERR(shelves_list(local_abspath,
+ ! opt_state->quiet /*with_logmsg*/,
+ ! opt_state->quiet /*with_diffstat*/,
+ ctx, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__checkpoint(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 *subsubcommand;
+ apr_array_header_t *targets;
+ const char *local_abspath;
+ const char *name;
+
+ if (opt_state->list)
+ subsubcommand = "list";
+ else
+ SVN_ERR(get_next_argument(&subsubcommand, os, pool, pool));
+
+ SVN_ERR(get_next_argument(&name, os, pool, pool));
+
+ /* Parse the remaining arguments as paths. */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL;
+
+ if (strcmp(subsubcommand, "list") == 0)
+ {
+ if (targets->nelts)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ SVN_ERR(checkpoint_list(name, local_abspath,
+ ! opt_state->quiet /*diffstat*/,
+ ctx, pool));
+ }
+ else if (strcmp(subsubcommand, "save") == 0)
+ {
+ svn_depth_t depth
+ = (opt_state->depth == svn_depth_unknown) ? svn_depth_infinity
+ : opt_state->depth;
+ int new_version;
+ svn_error_t *err;
+
+ svn_opt_push_implicit_dot_target(targets, pool);
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+ /* ### TODO: check all paths are in same WC; for now use first path */
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath,
+ APR_ARRAY_IDX(targets, 0, char *),
+ pool));
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
+ opt_state, NULL, ctx->config,
+ pool));
+ err = shelve(&new_version,
+ name, targets, depth, opt_state->changelists,
+ TRUE /*keep_local*/, opt_state->dry_run,
+ local_abspath, 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)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("saved '%s' version %d\n"),
+ name, new_version));
+ }
+ else if (strcmp(subsubcommand, "restore") == 0)
+ {
+ const char *arg;
+
+ if (targets->nelts > 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Too many arguments"));
+
+ /* Which checkpoint number? */
+ if (targets->nelts != 1)
+ arg = NULL;
+ else
+ arg = APR_ARRAY_IDX(targets, 0, char *);
+
+ SVN_ERR(restore(name, arg,
+ opt_state->dry_run, opt_state->quiet,
+ local_abspath, ctx, pool));
+ }
+ else
+ {
+ return svn_error_createf(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("checkpoint: Unknown checkpoint command '%s';
"
+ "try 'svn help checkpoint'"),
+ subsubcommand);
+ }
return SVN_NO_ERROR;
}
Modified: subversion/branches/shelve-checkpoint/subversion/svn/svn.c
URL:
http://svn.apache.org/viewvc/subversion/branches/shelve-checkpoint/subversion/svn/svn.c?rev=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/subversion/svn/svn.c (original)
+++ subversion/branches/shelve-checkpoint/subversion/svn/svn.c Fri Nov 17
22:02:35 2017
@@ -146,7 +146,6 @@ typedef enum svn_cl__longopt_t {
opt_adds_as_modification,
opt_vacuum_pristines,
opt_delete,
- opt_keep_shelved,
opt_list
} svn_cl__longopt_t;
@@ -468,9 +467,8 @@ const apr_getopt_option_t svn_cl__option
{"vacuum-pristines", opt_vacuum_pristines, 0,
N_("remove unreferenced pristines from .svn
directory")},
- {"list", opt_list, 0, N_("list shelved patches")},
- {"keep-shelved", opt_keep_shelved, 0, N_("do not delete the shelved patch")},
- {"delete", opt_delete, 0, N_("delete the shelved patch")},
+ {"list", opt_list, 0, N_("list shelves or checkpoints")},
+ {"delete", opt_delete, 0, N_("delete a shelf")},
/* Long-opt Aliases
*
@@ -617,31 +615,6 @@ const svn_opt_subcommand_desc2_t svn_cl_
" reporting the action taken.\n"),
{'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals} },
- { "checkpoint", svn_cl__checkpoint, {0}, N_
- ("Checkpoint the local changes.\n"
- "usage: 1. checkpoint save\n"
- " 2. checkpoint revert\n"
- " 3. checkpoint rollback NUMBER\n"
- " 4. checkpoint list|--list\n"
- "\n"
- " 1. Save the working state as a new checkpoint.\n"
- " 2. Revert the working state to the current checkpoint.\n"
- " 3. Roll back the working state to checkpoint NUMBER.\n"
- " 4. List all checkpoints. A synonym for 'svn checkpoints'.\n"),
- {'q',
- /*'-N', opt_depth, opt_targets, opt_changelist,*/
- SVN_CL__LOG_MSG_OPTIONS,
- opt_list},
- { {opt_list, N_("list all checkpoints")} }
- },
-
- { "checkpoints", svn_cl__checkpoints, {0}, N_
- ("List all checkpoints.\n"
- "usage: checkpoints\n"
- "\n"
- " A synonym for 'svn checkpoint list'.\n"),
- {} },
-
{ "cleanup", svn_cl__cleanup, {0}, N_
("Either recover from an interrupted operation that left the working copy
locked,\n"
"or remove unwanted files.\n"
@@ -1680,22 +1653,48 @@ 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} },
+ { "savepoint", svn_cl__checkpoint, {"sp", "checkpoint"}, N_
+ ("Save and restore local changes.\n"
+ "usage: 1. savepoint save NAME [PATH...]\n"
+ " 2. savepoint restore NAME [VERSION]\n"
+ " 3. savepoint list|--list NAME\n"
+ "\n"
+ " 1. Save local changes in the given PATHs as a new version of shelf
NAME.\n"
+ " A new log message can be given with -m, -F, etc.\n"
+ "\n"
+ " The same as 'svn shelve --keep-local'.\n"
+ "\n"
+ " 2. Apply the VERSION (default: latest) of shelf NAME to the working
copy.\n"
+ "\n"
+ " The same as 'svn unshelve'.\n"
+ "\n"
+ " 3. List all versions of shelf NAME.\n"
+ "\n"
+ "The default PATH is the current working directory.\n"),
+ {'q', opt_dry_run,
+ opt_depth, opt_targets, opt_changelist,
+ SVN_CL__LOG_MSG_OPTIONS,
+ opt_list},
+ { {opt_list, N_("list all versions of a shelf")} }
+ },
+
{ "shelve", svn_cl__shelve, {0}, N_
- ("Put a local change aside, as if putting it on a shelf.\n"
+ ("Put local changes aside, as if putting them on a shelf.\n"
"usage: 1. shelve [--keep-local] NAME [PATH...]\n"
" 2. shelve --delete NAME\n"
" 3. shelve --list\n"
"\n"
- " 1. Save the local change in the given PATHs to a patch file, and\n"
- " revert that change from the WC unless '--keep-local' is given.\n"
- " If a log message is given with '-m' or '-F', include it at the\n"
- " beginning of the patch file.\n"
+ " 1. Save the local changes in the given PATHs to a shelf named NAME.\n"
+ " Revert those changes from the WC unless '--keep-local' is given.\n"
+ " If a log message is given with '-m' or '-F', replace the shelf's\n"
+ " current log message (if any).\n"
+ "\n"
+ " 'svn shelve --keep-local' is like 'svn checkpoint save'.\n"
"\n"
- " 2. Delete the shelved change NAME.\n"
- " (A backup is kept, named with a '.bak' extension.)\n"
+ " 2. Delete the shelf named NAME.\n"
"\n"
- " 3. List shelved changes. Include the first line of any log message\n"
- " and some details about the contents of the change, unless '-q' is\n"
+ " 3. List shelves. Include the first line of any log message\n"
+ " and some details about the contents of the shelf, unless '-q' is\n"
" given.\n"
"\n"
" The kinds of change you can shelve are those supported by 'svn diff'\n"
@@ -1703,9 +1702,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
" mergeinfo changes, copies, moves, mkdir, rmdir,\n"
" 'binary' content, uncommittable states\n"
"\n"
- " To bring back a shelved change, use 'svn unshelve NAME'.\n"
+ " To bring back shelved changes, use 'svn unshelve NAME'.\n"
"\n"
- " A shelved change is stored as a patch file, .svn/shelves/NAME.patch\n"
+ " Shelves are stored in <WC>/.svn/shelves/\n"
),
{opt_delete, opt_list, 'q', opt_dry_run, opt_keep_local,
opt_depth, opt_targets, opt_changelist,
@@ -1714,29 +1713,29 @@ const svn_opt_subcommand_desc2_t svn_cl_
} },
{ "unshelve", svn_cl__unshelve, {0}, N_
- ("Bring a shelved change back to a local change in the WC.\n"
- "usage: 1. unshelve [--keep-shelved] [NAME]\n"
+ ("Bring shelved changes back into the WC.\n"
+ "usage: 1. unshelve [NAME]\n"
" 2. unshelve --list\n"
"\n"
- " 1. Apply the shelved change NAME to the working copy.\n"
- " Delete the patch unless the '--keep-shelved' option is given.\n"
- " (A backup is kept, named with a '.bak' extension.)\n"
- " NAME defaults to the most recent shelved change.\n"
+ " 1. Apply the shelf named NAME to the working copy.\n"
+ " NAME defaults to the most recent shelf.\n"
"\n"
- " 2. List shelved changes. Include the first line of any log message\n"
- " and some details about the contents of the change, unless '-q' is\n"
+ " Like 'svn checkpoint restore'.\n"
+ "\n"
+ " 2. List shelves. Include the first line of any log message\n"
+ " and some details about the contents of the shelf, unless '-q' is\n"
" given.\n"
"\n"
" Any conflict between the change being unshelved and a change\n"
" already in the WC is handled the same way as by 'svn patch',\n"
" creating a 'reject' file.\n"
),
- {opt_keep_shelved, opt_list, 'q', opt_dry_run} },
+ {opt_list, 'q', opt_dry_run} },
{ "shelves", svn_cl__shelves, {0}, N_
- ("List shelved changes.\n"
+ ("List shelves.\n"
"usage: shelves\n"),
- },
+ {'q'} },
{ "status", svn_cl__status, {"stat", "st"}, N_
("Print the status of working copy files and directories.\n"
@@ -2486,7 +2485,6 @@ sub_main(int *exit_code, int argc, const
opt_state.keep_changelists = TRUE;
break;
case opt_keep_local:
- case opt_keep_shelved:
opt_state.keep_local = TRUE;
break;
case opt_with_all_revprops:
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=1815634&r1=1815633&r2=1815634&view=diff
==============================================================================
--- subversion/branches/shelve-checkpoint/tools/client-side/bash_completion
(original)
+++ subversion/branches/shelve-checkpoint/tools/client-side/bash_completion Fri
Nov 17 22:02:35 2017
@@ -248,7 +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 checkpoint checkpoints"
+ cmds="$cmds checkpoint savepoint sp"
cmds="$cmds shelve shelves unshelve"
# help options have a strange command status...
@@ -1023,16 +1023,13 @@ _svn()
cmdOpts="$qOpts $pOpts"
;;
checkpoint)
- cmdOpts="$qOpts save revert rollback list --list"
- ;;
- checkpoints)
- cmdOpts="$qOpts"
+ cmdOpts="$qOpts save restore list --list"
;;
shelve)
cmdOpts="$qOpts --keep-local --delete --list $qOpts --dry-run
--depth --targets $cOpts"
;;
unshelve)
- cmdOpts="$qOpts --keep-shelved --list $qOpts --dry-run"
+ cmdOpts="$qOpts --list $qOpts --dry-run"
;;
shelves)
cmdOpts="$qOpts"