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 2018/05/18 10:32:14 UTC
svn commit: r1831844 - in /subversion/trunk/subversion: include/svn_client.h
libsvn_client/shelf.c
Author: julianfoad
Date: Fri May 18 10:32:14 2018
New Revision: 1831844
URL: http://svn.apache.org/viewvc?rev=1831844&view=rev
Log:
Shelving: Store metadata (kind, Modify/Add/Delete/Replace status) of every
changed path. Store a patch file per node instead of a single patch file.
This is preparation for moving away from patch file storage and its
limitations.
* subversion/include/svn_client.h
(svn_client_shelf_version_t): Remove the patch file path; add the storage
dir path.
* subversion/libsvn_client/shelf.c
Many changes to implement the above.
Modified:
subversion/trunk/subversion/include/svn_client.h
subversion/trunk/subversion/libsvn_client/shelf.c
Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1831844&r1=1831843&r2=1831844&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Fri May 18 10:32:14 2018
@@ -6883,7 +6883,7 @@ typedef struct svn_client_shelf_version_
apr_time_t mtime; /** time-stamp of this version */
/* Private fields */
- const char *patch_abspath; /** abspath of the patch file */
+ const char *files_dir_abspath; /** abspath of the storage area */
int version_number; /** version number starting from 1 */
} svn_client_shelf_version_t;
Modified: subversion/trunk/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/shelf.c?rev=1831844&r1=1831843&r2=1831844&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/shelf.c (original)
+++ subversion/trunk/subversion/libsvn_client/shelf.c Fri May 18 10:32:14 2018
@@ -133,46 +133,68 @@ shelf_version_files_dir_abspath(const ch
return SVN_NO_ERROR;
}
-/* Set *PATCH_ABSPATH to the abspath of the patch file for SHELF
- * version VERSION, no matter whether it exists.
+/* Create a shelf-version object for a version that may or may not already
+ * exist on disk.
*/
static svn_error_t *
-get_patch_abspath(const char **abspath,
- svn_client_shelf_t *shelf,
- int version,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+shelf_version_create(svn_client_shelf_version_t **new_version_p,
+ svn_client_shelf_t *shelf,
+ int version_number,
+ apr_pool_t *result_pool)
{
- char *codename;
- char *filename;
+ svn_client_shelf_version_t *shelf_version
+ = apr_pcalloc(result_pool, sizeof(*shelf_version));
- SVN_ERR(shelf_name_encode(&codename, shelf->name, result_pool));
- filename = apr_psprintf(scratch_pool, "%s-%03d.patch", codename, version);
- *abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool);
+ shelf_version->shelf = shelf;
+ shelf_version->version_number = version_number;
+ SVN_ERR(shelf_version_files_dir_abspath(&shelf_version->files_dir_abspath,
+ shelf, version_number,
+ result_pool, result_pool));
+ *new_version_p = shelf_version;
return SVN_NO_ERROR;
}
-/* 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)
+get_metadata_abspath(char **abspath,
+ svn_client_shelf_version_t *shelf_version,
+ const char *wc_relpath,
+ 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);
+ wc_relpath = apr_psprintf(scratch_pool, "%s.meta", wc_relpath);
+ *abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
+ result_pool);
+ return SVN_NO_ERROR;
+}
- SVN_ERR(get_patch_abspath(abspath, shelf, version,
- result_pool, scratch_pool));
+/* */
+static svn_error_t *
+get_working_file_abspath(char **work_abspath,
+ svn_client_shelf_version_t *shelf_version,
+ const char *wc_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ wc_relpath = apr_psprintf(scratch_pool, "%s.work", wc_relpath);
+ *work_abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
+ result_pool);
+ return SVN_NO_ERROR;
+}
+
+/* Set *PATCH_ABSPATH to the abspath of the patch file for SHELF_VERSION
+ * node at RELPATH, no matter whether it exists.
+ */
+static svn_error_t *
+get_patch_abspath(const char **abspath,
+ svn_client_shelf_version_t *shelf_version,
+ const char *wc_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ wc_relpath = apr_psprintf(scratch_pool, "%s.patch", wc_relpath);
+ *abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
+ result_pool);
return SVN_NO_ERROR;
}
@@ -182,13 +204,8 @@ shelf_version_delete(svn_client_shelf_t
int version,
apr_pool_t *scratch_pool)
{
- const char *patch_abspath;
const char *files_dir_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));
SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath,
shelf, version,
scratch_pool, scratch_pool));
@@ -362,14 +379,131 @@ shelf_write_current(svn_client_shelf_t *
return SVN_NO_ERROR;
}
+/* Return the single character representation of STATUS.
+ * (Similar to subversion/svn/status.c:generate_status_code()
+ * and subversion/tests/libsvn_client/client-test.c:status_to_char().) */
+static char
+status_to_char(enum svn_wc_status_kind status)
+{
+ switch (status)
+ {
+ case svn_wc_status_none: return '.';
+ case svn_wc_status_unversioned: return '?';
+ case svn_wc_status_normal: return ' ';
+ case svn_wc_status_added: return 'A';
+ case svn_wc_status_missing: return '!';
+ case svn_wc_status_deleted: return 'D';
+ case svn_wc_status_replaced: return 'R';
+ case svn_wc_status_modified: return 'M';
+ case svn_wc_status_merged: return 'G';
+ case svn_wc_status_conflicted: return 'C';
+ case svn_wc_status_ignored: return 'I';
+ case svn_wc_status_obstructed: return '~';
+ case svn_wc_status_external: return 'X';
+ case svn_wc_status_incomplete: return ':';
+ default: return '*';
+ }
+}
+
+static enum svn_wc_status_kind
+char_to_status(char status)
+{
+ switch (status)
+ {
+ case '.': return svn_wc_status_none;
+ case '?': return svn_wc_status_unversioned;
+ case ' ': return svn_wc_status_normal;
+ case 'A': return svn_wc_status_added;
+ case '!': return svn_wc_status_missing;
+ case 'D': return svn_wc_status_deleted;
+ case 'R': return svn_wc_status_replaced;
+ case 'M': return svn_wc_status_modified;
+ case 'G': return svn_wc_status_merged;
+ case 'C': return svn_wc_status_conflicted;
+ case 'I': return svn_wc_status_ignored;
+ case '~': return svn_wc_status_obstructed;
+ case 'X': return svn_wc_status_external;
+ case ':': return svn_wc_status_incomplete;
+ default: return (enum svn_wc_status_kind)0;
+ }
+}
+
+/* Write status to shelf storage.
+ */
+static svn_error_t *
+status_write(svn_client_shelf_version_t *shelf_version,
+ const char *relpath,
+ const svn_wc_status3_t *s,
+ apr_pool_t *scratch_pool)
+{
+ char *file_abspath;
+ apr_file_t *file;
+ svn_stream_t *stream;
+
+ SVN_ERR(get_metadata_abspath(&file_abspath, shelf_version, relpath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_io_file_open(&file, file_abspath,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE,
+ APR_FPROT_OS_DEFAULT, scratch_pool));
+ stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, scratch_pool);
+ SVN_ERR(svn_stream_printf(stream, scratch_pool, "%c %c%c%c",
+ s->kind == svn_node_dir ? 'd'
+ : s->kind == svn_node_file ? 'f'
+ : s->kind == svn_node_symlink ? 'l'
+ : '?',
+ status_to_char(s->node_status),
+ status_to_char(s->text_status),
+ status_to_char(s->prop_status)));
+ SVN_ERR(svn_stream_close(stream));
+ return SVN_NO_ERROR;
+}
+
+/* Read status from shelf storage.
+ */
+static svn_error_t *
+status_read(svn_wc_status3_t **status,
+ svn_client_shelf_version_t *shelf_version,
+ const char *relpath,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_status3_t *s = apr_pcalloc(scratch_pool, sizeof(*s));
+ char *file_abspath;
+ apr_file_t *file;
+ svn_stream_t *stream;
+ char string[5];
+ apr_size_t len = 5;
+
+ SVN_ERR(get_metadata_abspath(&file_abspath, shelf_version, relpath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_io_file_open(&file, file_abspath,
+ APR_FOPEN_READ,
+ APR_FPROT_OS_DEFAULT, scratch_pool));
+ stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, scratch_pool);
+ SVN_ERR(svn_stream_read_full(stream, string, &len));
+ SVN_ERR(svn_stream_close(stream));
+
+ s->kind = (string[0] == 'd' ? svn_node_dir
+ : string[0] == 'f' ? svn_node_file
+ : string[0] == 'l' ? svn_node_symlink
+ : svn_node_unknown);
+ s->node_status = char_to_status(string[2]);
+ s->text_status = char_to_status(string[3]);
+ s->prop_status = char_to_status(string[4]);
+ *status = s;
+ return SVN_NO_ERROR;
+}
+
/* A visitor function type for use with walk_shelved_files(). */
+/* The same as svn_wc_status_func4_t except relpath instead of abspath. */
typedef svn_error_t *(*shelved_files_walk_func_t)(void *baton,
const char *relpath,
+ svn_wc_status3_t *s,
apr_pool_t *scratch_pool);
-/* Baton for io_visit_shelved_file(). */
+/* Baton for shelved_files_walk_visitor(). */
struct shelved_files_walk_baton_t
{
+ svn_client_shelf_version_t *shelf_version;
const char *walk_root_abspath;
shelved_files_walk_func_t walk_func;
void *walk_baton;
@@ -388,9 +522,14 @@ shelved_files_walk_visitor(void *baton,
const char *relpath;
relpath = svn_dirent_skip_ancestor(b->walk_root_abspath, abspath);
- if (finfo->filetype == APR_REG)
+ if (finfo->filetype == APR_REG
+ && (strlen(relpath) >= 5 && strcmp(relpath+strlen(relpath)-5, ".meta") == 0))
{
- SVN_ERR(b->walk_func(b->walk_baton, relpath, scratch_pool));
+ svn_wc_status3_t *s;
+
+ relpath = apr_pstrndup(scratch_pool, relpath, strlen(relpath) - 5);
+ SVN_ERR(status_read(&s, b->shelf_version, relpath, scratch_pool));
+ SVN_ERR(b->walk_func(b->walk_baton, relpath, s, scratch_pool));
}
return SVN_NO_ERROR;
}
@@ -404,18 +543,14 @@ walk_shelved_files(svn_client_shelf_vers
void *walk_baton,
apr_pool_t *scratch_pool)
{
- const char *files_dir_abspath;
struct shelved_files_walk_baton_t baton;
svn_error_t *err;
- SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath,
- shelf_version->shelf,
- shelf_version->version_number,
- scratch_pool, scratch_pool));
- baton.walk_root_abspath = files_dir_abspath;
+ baton.shelf_version = shelf_version;
+ baton.walk_root_abspath = shelf_version->files_dir_abspath;
baton.walk_func = walk_func;
baton.walk_baton = walk_baton;
- err = svn_io_dir_walk2(files_dir_abspath, 0 /*wanted*/,
+ err = svn_io_dir_walk2(baton.walk_root_abspath, 0 /*wanted*/,
shelved_files_walk_visitor, &baton,
scratch_pool);
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
@@ -429,9 +564,7 @@ walk_shelved_files(svn_client_shelf_vers
/* A baton for use with walk_callback(). */
typedef struct walk_baton_t {
const char *wc_root_abspath;
- const char *files_dir_abspath;
- svn_stream_t *outstream;
- svn_stream_t *errstream;
+ svn_client_shelf_version_t *shelf_version;
svn_client_ctx_t *ctx;
svn_boolean_t any_shelved; /* were any paths successfully shelved? */
apr_array_header_t *unshelvable; /* paths unshelvable */
@@ -477,22 +610,34 @@ is_binary_file(svn_boolean_t *is_binary,
}
/* Copy the WC working file at FROM_WC_ABSPATH to a storage location within
- * the shelf-version storage area at FILES_DIR_ABSPATH.
+ * the shelf-version storage area in SHELF_VERSION.
+ * If STORE_WHOLE_FILE is true, we will store the whole file (and we will
+ * not be storing a patch of the file text).
*/
static svn_error_t *
store_file(const char *from_wc_abspath,
const char *wc_relpath,
- const char *files_dir_abspath,
+ svn_client_shelf_version_t *shelf_version,
+ const svn_wc_status3_t *status,
+ svn_boolean_t store_whole_file,
apr_pool_t *scratch_pool)
{
- const char *stored_abspath = svn_dirent_join(files_dir_abspath, wc_relpath,
- scratch_pool);
+ char *stored_abspath;
+ SVN_ERR(get_working_file_abspath(&stored_abspath,
+ shelf_version, wc_relpath,
+ scratch_pool, scratch_pool));
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(stored_abspath,
scratch_pool),
scratch_pool));
- SVN_ERR(svn_io_copy_file(from_wc_abspath, stored_abspath,
- TRUE /*copy_perms*/, scratch_pool));
+ SVN_ERR(status_write(shelf_version, wc_relpath,
+ status, scratch_pool));
+
+ if (store_whole_file)
+ {
+ SVN_ERR(svn_io_copy_file(from_wc_abspath, stored_abspath,
+ TRUE /*copy_perms*/, scratch_pool));
+ }
return SVN_NO_ERROR;
}
@@ -530,36 +675,55 @@ walk_callback(void *baton,
store_whole_file = TRUE;
}
}
- /* Store 'binary' files (except deletes) as complete files;
- store everything else in the patch. */
- /* ### Need a way to record 'modified' vs. 'replaced' */
- /* ### Need a way to store 'deleted' efficiently */
- if (store_whole_file)
- {
- SVN_ERR(store_file(local_abspath, wc_relpath, wb->files_dir_abspath,
- scratch_pool));
- }
- SVN_ERR(svn_client_diff_peg7(NULL /*options*/,
- local_abspath,
- &peg_revision,
- &start_revision,
- &end_revision,
- wb->wc_root_abspath,
- svn_depth_empty,
- TRUE /*notice_ancestry*/,
- FALSE /*no_diff_added*/,
- FALSE /*no_diff_deleted*/,
- TRUE /*show_copies_as_adds*/,
- FALSE /*ignore_content_type*/,
- FALSE /*ignore_properties*/,
- store_whole_file /*properties_only*/,
- binary /*use_git_diff_format*/,
- FALSE /*pretty_print_mergeinfo*/,
- SVN_APR_LOCALE_CHARSET,
- wb->outstream,
- wb->errstream,
- NULL /*changelists*/,
- wb->ctx, scratch_pool));
+ /* Store all files (working version, for now) as complete files. */
+ SVN_ERR(store_file(local_abspath, wc_relpath, wb->shelf_version,
+ status, store_whole_file, scratch_pool));
+ /* Store a patch */
+ {
+ const char *patch_abspath;
+ apr_int32_t flag;
+ apr_file_t *outfile;
+ svn_stream_t *outstream;
+ svn_stream_t *errstream;
+
+ SVN_ERR(get_patch_abspath(&patch_abspath,
+ wb->shelf_version, wc_relpath,
+ 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 | APR_FOPEN_TRUNCATE;
+ 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);
+ errstream = svn_stream_empty(scratch_pool);
+
+ SVN_ERR(svn_client_diff_peg7(NULL /*options*/,
+ local_abspath,
+ &peg_revision,
+ &start_revision,
+ &end_revision,
+ wb->wc_root_abspath,
+ svn_depth_empty,
+ TRUE /*notice_ancestry*/,
+ FALSE /*no_diff_added*/,
+ FALSE /*no_diff_deleted*/,
+ TRUE /*show_copies_as_adds*/,
+ FALSE /*ignore_content_type*/,
+ FALSE /*ignore_properties*/,
+ store_whole_file /*properties_only*/,
+ binary /*use_git_diff_format*/,
+ FALSE /*pretty_print_mergeinfo*/,
+ SVN_APR_LOCALE_CHARSET,
+ outstream, errstream,
+ NULL /*changelists*/,
+ wb->ctx, scratch_pool));
+ SVN_ERR(svn_stream_close(outstream));
+ SVN_ERR(svn_stream_close(errstream));
+ }
wb->any_shelved = TRUE;
break;
}
@@ -669,7 +833,7 @@ wc_walk_status_multi(const apr_array_hea
return SVN_NO_ERROR;
}
-/** Write local changes to a patch file.
+/** Write local changes to the shelf storage.
*
* @a paths, @a depth, @a changelists: The selection of local paths to diff.
*
@@ -680,48 +844,31 @@ wc_walk_status_multi(const apr_array_hea
* This might also solve the buffering problem.
*/
static svn_error_t *
-write_patch(svn_boolean_t *any_shelved,
- apr_array_header_t **unshelvable,
- const char *files_dir_abspath,
- const char *patch_abspath,
- const apr_array_header_t *paths,
- svn_depth_t depth,
- const apr_array_header_t *changelists,
- const char *wc_root_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+shelf_write_changes(svn_boolean_t *any_shelved,
+ apr_array_header_t **unshelvable,
+ svn_client_shelf_version_t *shelf_version,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ const char *wc_root_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
walk_baton_t walk_baton = { 0 };
- apr_int32_t flag;
- apr_file_t *outfile;
walk_baton.wc_root_abspath = wc_root_abspath;
- walk_baton.files_dir_abspath = files_dir_abspath;
+ walk_baton.shelf_version = shelf_version;
walk_baton.ctx = ctx;
walk_baton.any_shelved = FALSE;
walk_baton.unshelvable = apr_array_make(result_pool, 0, sizeof(char *));
walk_baton.pool = result_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 | APR_FOPEN_TRUNCATE;
- SVN_ERR(svn_io_file_open(&outfile, patch_abspath,
- flag, APR_FPROT_OS_DEFAULT, scratch_pool));
- walk_baton.outstream = svn_stream_from_aprfile2(outfile, FALSE /*disown*/,
- scratch_pool);
- walk_baton.errstream = svn_stream_empty(scratch_pool);
-
/* Walk the WC */
SVN_ERR(wc_walk_status_multi(paths, depth, changelists,
walk_callback, &walk_baton,
ctx, scratch_pool));
- SVN_ERR(svn_stream_close(walk_baton.outstream));
- SVN_ERR(svn_stream_close(walk_baton.errstream));
-
*any_shelved = walk_baton.any_shelved;
*unshelvable = walk_baton.unshelvable;
return SVN_NO_ERROR;
@@ -841,7 +988,6 @@ svn_client_shelf_delete(const char *name
struct paths_changed_walk_baton_t
{
apr_hash_t *paths_hash;
- apr_array_header_t *paths_array;
svn_boolean_t as_abspath;
const char *wc_root_abspath;
apr_pool_t *pool;
@@ -852,6 +998,7 @@ struct paths_changed_walk_baton_t
static svn_error_t *
paths_changed_visitor(void *baton,
const char *relpath,
+ svn_wc_status3_t *s,
apr_pool_t *scratch_pool)
{
struct paths_changed_walk_baton_t *b = baton;
@@ -859,8 +1006,7 @@ paths_changed_visitor(void *baton,
relpath = (b->as_abspath
? svn_dirent_join(b->wc_root_abspath, relpath, b->pool)
: apr_pstrdup(b->pool, relpath));
- if (b->paths_hash)
- svn_hash_sets(b->paths_hash, relpath, relpath);
+ svn_hash_sets(b->paths_hash, relpath, relpath);
return SVN_NO_ERROR;
}
@@ -876,48 +1022,16 @@ shelf_paths_changed(apr_hash_t **paths_h
apr_pool_t *scratch_pool)
{
svn_client_shelf_t *shelf = shelf_version->shelf;
- svn_patch_file_t *patch_file;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *paths_hash = apr_hash_make(result_pool);
+ struct paths_changed_walk_baton_t baton;
- /* Add the paths stored in the patch file */
- SVN_ERR(svn_diff_open_patch_file(&patch_file, shelf_version->patch_abspath,
- result_pool));
- while (1)
- {
- svn_patch_t *patch;
- char *path;
-
- svn_pool_clear(iterpool);
- SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
- FALSE /*reverse*/,
- FALSE /*ignore_whitespace*/,
- iterpool, iterpool));
- if (! patch)
- break;
-
- path = (as_abspath
- ? svn_dirent_join(shelf->wc_root_abspath,
- patch->new_filename, result_pool)
- : apr_pstrdup(result_pool, patch->new_filename));
- svn_hash_sets(paths_hash, path, path);
- }
- SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
-
- /* Add the paths of files stored outside the patch file ('binary' files) */
- {
- struct paths_changed_walk_baton_t baton;
-
- baton.paths_hash = paths_hash;
- baton.as_abspath = as_abspath;
- baton.wc_root_abspath = shelf->wc_root_abspath;
- baton.pool = result_pool;
- SVN_ERR(walk_shelved_files(shelf_version,
- paths_changed_visitor, &baton,
- scratch_pool));
- }
-
- svn_pool_destroy(iterpool);
+ baton.paths_hash = paths_hash;
+ baton.as_abspath = as_abspath;
+ baton.wc_root_abspath = shelf->wc_root_abspath;
+ baton.pool = result_pool;
+ SVN_ERR(walk_shelved_files(shelf_version,
+ paths_changed_visitor, &baton,
+ scratch_pool));
if (paths_hash_p)
*paths_hash_p = paths_hash;
@@ -942,7 +1056,7 @@ svn_client_shelf_paths_changed(apr_hash_
/* Baton for apply_file_visitor(). */
struct apply_files_baton_t
{
- const char *files_dir_abspath, *wc_root_abspath;
+ svn_client_shelf_version_t *shelf_version;
const char *file_relpath; /* only process this file, no others */
svn_boolean_t found; /* was FILE_RELPATH found? */
svn_boolean_t test_only; /* only check whether it would conflict */
@@ -956,19 +1070,28 @@ struct apply_files_baton_t
*
* Make any missing parent directories.
*
+ * In test mode (BATON->test_only): set BATON->conflict if we can't apply
+ * the change to WC at RELPATH without conflict. But in fact, just check
+ * if WC at RELPATH is locally modified.
+ *
* Implements shelved_files_walk_func_t. */
static svn_error_t *
apply_file_visitor(void *baton,
const char *relpath,
+ svn_wc_status3_t *s,
apr_pool_t *scratch_pool)
{
struct apply_files_baton_t *b = baton;
- const char *stored_abspath = svn_dirent_join(b->files_dir_abspath, relpath,
- scratch_pool);
- const char *to_wc_abspath = svn_dirent_join(b->wc_root_abspath, relpath,
+ const char *wc_root_abspath = b->shelf_version->shelf->wc_root_abspath;
+ char *stored_abspath;
+ const char *to_wc_abspath = svn_dirent_join(wc_root_abspath, relpath,
scratch_pool);
const char *to_dir_abspath = svn_dirent_dirname(to_wc_abspath, scratch_pool);
+ svn_node_kind_t kind;
+ SVN_ERR(get_working_file_abspath(&stored_abspath,
+ b->shelf_version, relpath,
+ scratch_pool, scratch_pool));
if (b->file_relpath && strcmp(relpath, b->file_relpath) != 0)
{
return SVN_NO_ERROR;
@@ -993,121 +1116,98 @@ apply_file_visitor(void *baton,
return SVN_NO_ERROR;
}
- SVN_ERR(svn_io_make_dir_recursively(to_dir_abspath, scratch_pool));
- SVN_ERR(svn_io_copy_file(stored_abspath, to_wc_abspath,
- TRUE /*copy_perms*/, scratch_pool));
- /* If it was not already versioned, schedule the file for addition.
- (Do not apply autoprops, because this isn't a user-facing "add" but
- restoring a previously saved state.) */
- SVN_ERR(svn_client_add5(to_wc_abspath, svn_depth_infinity,
- TRUE /*force: ok if already versioned*/,
- TRUE /*no_ignore*/,
- TRUE /*no_autoprops*/,
- TRUE /*add_parents*/,
- b->ctx, scratch_pool));
+ /* If we stored a whole file, copy it into the WC and ensure it's versioned.
+ ### TODO: merge it if possible
+ ### TODO: handle deletes
+ ### TODO: handle directories
+ ### TODO: handle props
+ */
+ SVN_ERR(svn_io_check_path(stored_abspath, &kind, scratch_pool));
+ if (kind == svn_node_file
+ && s->kind == svn_node_file
+ && s->node_status != svn_wc_status_deleted)
+ {
+ SVN_ERR(svn_io_make_dir_recursively(to_dir_abspath, scratch_pool));
+ SVN_ERR(svn_io_copy_file(stored_abspath, to_wc_abspath,
+ TRUE /*copy_perms*/, scratch_pool));
+ /* If it was not already versioned, schedule the file for addition.
+ (Do not apply autoprops, because this isn't a user-facing "add" but
+ restoring a previously saved state.) */
+ SVN_ERR(svn_client_add5(to_wc_abspath, svn_depth_infinity,
+ TRUE /*force: ok if already versioned*/,
+ TRUE /*no_ignore*/,
+ TRUE /*no_autoprops*/,
+ TRUE /*add_parents*/,
+ b->ctx, scratch_pool));
+ }
+ /* Apply the changes stored in the per-node patch file */
+ {
+ const char *patch_abspath;
+
+ SVN_ERR(get_patch_abspath(&patch_abspath,
+ b->shelf_version, relpath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_patch(patch_abspath,
+ wc_root_abspath,
+ FALSE /*dry_run*/, 0 /*strip*/,
+ FALSE /*reverse*/,
+ FALSE /*ignore_whitespace*/,
+ TRUE /*remove_tempfiles*/, NULL, NULL,
+ b->ctx, scratch_pool));
+ }
+
return SVN_NO_ERROR;
}
-/* A filter to only apply the patch to a particular file. */
-struct patch_filter_baton_t
+/* Baton for diff_visitor(). */
+struct diff_baton_t
{
- /* The single path to be selected for patching */
- const char *path;
+ svn_client_shelf_version_t *shelf_version;
+ svn_stream_t *outstream;
};
+/* Write a diff to BATON->outstream.
+ * Implements shelved_files_walk_func_t. */
static svn_error_t *
-patch_filter(void *baton,
- svn_boolean_t *filtered,
- const char *canon_path_from_patchfile,
- const char *patch_abspath,
- const char *reject_abspath,
+diff_visitor(void *baton,
+ const char *relpath,
+ svn_wc_status3_t *s,
apr_pool_t *scratch_pool)
{
- struct patch_filter_baton_t *fb = baton;
+ struct diff_baton_t *b = baton;
+ const char *patch_abspath;
+ svn_stream_t *instream;
- *filtered = (strcmp(canon_path_from_patchfile, fb->path) != 0);
+ SVN_ERR(get_patch_abspath(&patch_abspath,
+ b->shelf_version, relpath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&instream, patch_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(instream,
+ svn_stream_disown(b->outstream, scratch_pool),
+ NULL, NULL, scratch_pool));
return SVN_NO_ERROR;
}
-/* Intercept patch notifications to detect when there is a conflict */
-struct patch_notify_baton_t
-{
- svn_boolean_t conflict;
-};
-
-/* 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 *nb = baton;
-
- if (notify->action == svn_wc_notify_patch_rejected_hunk
- || notify->action == svn_wc_notify_skip)
- nb->conflict = TRUE;
-}
-
svn_error_t *
svn_client_shelf_test_apply_file(svn_boolean_t *conflict_p,
svn_client_shelf_version_t *shelf_version,
const char *file_relpath,
apr_pool_t *scratch_pool)
{
- svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
- svn_wc_notify_func2_t ctx_notify_func;
- void *ctx_notify_baton;
- struct patch_filter_baton_t fb;
- struct patch_notify_baton_t nb;
-
- /* Apply the whole files stored outside the patch file ('binary' files) */
- {
- const char *files_dir_abspath;
- struct apply_files_baton_t baton = {0};
-
- SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath,
- shelf_version->shelf,
- shelf_version->version_number,
- scratch_pool, scratch_pool));
- baton.files_dir_abspath = files_dir_abspath;
- baton.wc_root_abspath = shelf_version->shelf->wc_root_abspath;
- baton.file_relpath = file_relpath;
- baton.found = FALSE;
- baton.test_only = TRUE;
- baton.conflict = FALSE;
- baton.ctx = shelf_version->shelf->ctx;
- SVN_ERR(walk_shelved_files(shelf_version,
- apply_file_visitor, &baton,
- scratch_pool));
- if (baton.found)
- {
- *conflict_p = baton.conflict;
- return SVN_NO_ERROR;
- }
- }
+ struct apply_files_baton_t baton = {0};
- /* Apply the patch. Any files added above can now have their props set. */
- fb.path = file_relpath;
-
- nb.conflict = FALSE;
- ctx_notify_func = ctx->notify_func2;
- ctx_notify_baton = ctx->notify_baton2;
- ctx->notify_func2 = patch_notify;
- ctx->notify_baton2 = &nb;
-
- SVN_ERR(svn_client_patch(shelf_version->patch_abspath,
- shelf_version->shelf->wc_root_abspath,
- TRUE /*dry_run*/, 0 /*strip*/,
- FALSE /*reverse*/,
- FALSE /*ignore_whitespace*/,
- TRUE /*remove_tempfiles*/,
- patch_filter, &fb,
- shelf_version->shelf->ctx, scratch_pool));
-
- ctx->notify_func2 = ctx_notify_func;
- ctx->notify_baton2 = ctx_notify_baton;
+ baton.shelf_version = shelf_version;
+ baton.file_relpath = file_relpath;
+ baton.found = FALSE;
+ baton.test_only = TRUE;
+ baton.conflict = FALSE;
+ baton.ctx = shelf_version->shelf->ctx;
+ SVN_ERR(walk_shelved_files(shelf_version,
+ apply_file_visitor, &baton,
+ scratch_pool));
+ *conflict_p = baton.conflict;
- *conflict_p = nb.conflict;
return SVN_NO_ERROR;
}
@@ -1116,31 +1216,13 @@ svn_client_shelf_apply(svn_client_shelf_
svn_boolean_t dry_run,
apr_pool_t *scratch_pool)
{
- /* Apply the whole files stored outside the patch file ('binary' files) */
- {
- const char *files_dir_abspath;
- struct apply_files_baton_t baton = {0};
-
- SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath,
- shelf_version->shelf,
- shelf_version->version_number,
- scratch_pool, scratch_pool));
- baton.files_dir_abspath = files_dir_abspath;
- baton.wc_root_abspath = shelf_version->shelf->wc_root_abspath;
- baton.ctx = shelf_version->shelf->ctx;
- SVN_ERR(walk_shelved_files(shelf_version,
- apply_file_visitor, &baton,
- scratch_pool));
- }
-
- /* Apply the changes stored in the patch file */
- SVN_ERR(svn_client_patch(shelf_version->patch_abspath,
- shelf_version->shelf->wc_root_abspath,
- dry_run, 0 /*strip*/,
- FALSE /*reverse*/,
- FALSE /*ignore_whitespace*/,
- TRUE /*remove_tempfiles*/, NULL, NULL,
- shelf_version->shelf->ctx, scratch_pool));
+ struct apply_files_baton_t baton = {0};
+
+ baton.shelf_version = shelf_version;
+ baton.ctx = shelf_version->shelf->ctx;
+ SVN_ERR(walk_shelved_files(shelf_version,
+ apply_file_visitor, &baton,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -1205,13 +1287,13 @@ svn_client_shelf_export_patch(svn_client
svn_stream_t *outstream,
apr_pool_t *scratch_pool)
{
- svn_stream_t *instream;
+ struct diff_baton_t baton = {0};
- SVN_ERR(svn_stream_open_readonly(&instream, shelf_version->patch_abspath,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_stream_copy3(instream,
- svn_stream_disown(outstream, scratch_pool),
- NULL, NULL, scratch_pool));
+ baton.shelf_version = shelf_version;
+ baton.outstream = outstream;
+ SVN_ERR(walk_shelved_files(shelf_version,
+ diff_visitor, &baton,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -1224,20 +1306,17 @@ svn_client_shelf_save_new_version2(svn_c
apr_pool_t *scratch_pool)
{
int next_version = shelf->max_version + 1;
- const char *files_dir_abspath;
- const char *patch_abspath;
+ svn_client_shelf_version_t *new_shelf_version;
svn_boolean_t any_shelved;
apr_array_header_t *unshelvable;
- SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath, shelf, next_version,
- scratch_pool, scratch_pool));
- SVN_ERR(get_patch_abspath(&patch_abspath, shelf, next_version,
- scratch_pool, scratch_pool));
- SVN_ERR(write_patch(&any_shelved, &unshelvable,
- files_dir_abspath, patch_abspath,
- paths, depth, changelists,
- shelf->wc_root_abspath,
- shelf->ctx, scratch_pool, scratch_pool));
+ SVN_ERR(shelf_version_create(&new_shelf_version,
+ shelf, next_version, scratch_pool));
+ SVN_ERR(shelf_write_changes(&any_shelved, &unshelvable,
+ new_shelf_version,
+ paths, depth, changelists,
+ shelf->wc_root_abspath,
+ shelf->ctx, scratch_pool, scratch_pool));
if (unshelvable->nelts > 0)
{
@@ -1354,21 +1433,17 @@ svn_client_shelf_version_open(svn_client
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_client_shelf_version_t *shelf_version
- = apr_palloc(result_pool, sizeof(*shelf_version));
+ svn_client_shelf_version_t *shelf_version;
const svn_io_dirent2_t *dirent;
- shelf_version->shelf = shelf;
- SVN_ERR(get_existing_patch_abspath(&shelf_version->patch_abspath,
- shelf, version_number,
- result_pool, scratch_pool));
+ SVN_ERR(shelf_version_create(&shelf_version,
+ shelf, version_number, result_pool));
SVN_ERR(svn_io_stat_dirent2(&dirent,
- shelf_version->patch_abspath,
+ shelf_version->files_dir_abspath,
FALSE /*verify_truename*/,
TRUE /*ignore_enoent*/,
result_pool, scratch_pool));
shelf_version->mtime = dirent->mtime;
- shelf_version->version_number = version_number;
*shelf_version_p = shelf_version;
return SVN_NO_ERROR;
}