Fix an issue where calling `svn copy` between two different working copies
could fail if they had different --store-pristine settings.
Before this change, we were transferring the low-level pristine information
(sqlite entries and files) from one db to another. This is no longer enough
for some of the cases. For example, there is a case where we need to copy
from a WC that doesn't store pristines locally into a WC that does, and the
source pristine contents should be read from an unmodified working file.
This changeset switches to a more generic approach where we read and install
the pristine contents using the wc_textbase layer that automatically handles
such cases.
* subversion/libsvn_wc/copy.c
(copy_versioned_file): Accept the `within_one_wc` parameter.
Use the textbase layer to read and install the pristines when the copy
happens between two different WCs.
(copy_versioned_dir): Accept and propagate the `within_one_wc` parameter.
(copy_or_move): No longer call svn_wc__db_pristine_transfer(). Pass the
local `within_one_wc` variable further.
* subversion/libsvn_wc/wc_db.h
(svn_wc__db_pristine_transfer): Remove.
* subversion/libsvn_wc/wc_db_pristine.c
(maybe_transfer_one_pristine,
pristine_transfer_txn,
svn_wc__db_pristine_transfer): Remove.
* subversion/libsvn_wc/wc-queries.sql
(STMT_SELECT_COPY_PRISTINES_F31,
STMT_SELECT_COPY_PRISTINES_F32): Remove. Also remove these statements …
* subversion/tests/libsvn_wc/wc-queries-test.c
(stmt_matches_wc_format): …from here.
* subversion/tests/cmdline/store_pristine_tests.py
(copy_cross_wc_without_src_pristine): New regression test.
(copy_cross_wc_without_dst_pristine): New test.
(test_list): Run the new tests.
* subversion/tests/libsvn_client/client-test.c
(test_wc_add_scenarios): Skip when running for a working copy without
pristines. Despite being located in client-test.c, here we test the
low-level svn_wc APIs that require svn_wc_textbase_sync() calls to work
for WCs without pristines. For now, let's keep testing the old behavior,
just for WCs that store all pristines locally.
git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1908573 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_wc/copy.c b/subversion/libsvn_wc/copy.c
index b51f969..8bba011 100644
--- a/subversion/libsvn_wc/copy.c
+++ b/subversion/libsvn_wc/copy.c
@@ -215,6 +215,8 @@
If IS_MOVE is true, record move information in working copy meta
data in addition to copying the file.
+ WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root)
+
If the versioned file has a text conflict, and the .mine file exists in
the filesystem, copy the .mine file to DST_ABSPATH. Otherwise, copy the
versioned file itself.
@@ -237,6 +239,7 @@
svn_boolean_t metadata_only,
svn_boolean_t conflicted,
svn_boolean_t is_move,
+ svn_boolean_t within_one_wc,
const svn_io_dirent2_t *dirent,
svn_filesize_t recorded_size,
apr_time_t recorded_time,
@@ -248,8 +251,45 @@
{
svn_skel_t *work_items = NULL;
- /* In case we are copying from one WC to another (e.g. an external dir),
- ensure the destination WC has a copy of the pristine text. */
+ if (within_one_wc)
+ {
+ /* In case we are copying within one WC, it already has the pristine. */
+ }
+ else
+ {
+ /* In case we are copying from one WC to another (e.g. an external dir),
+ ensure the destination WC has a copy of the pristine text. */
+
+ svn_stream_t *contents;
+
+ SVN_ERR(svn_wc__textbase_get_contents(&contents, db, src_abspath, NULL,
+ TRUE, scratch_pool, scratch_pool));
+ if (contents)
+ {
+ svn_stream_t *install_stream;
+ svn_wc__db_install_data_t *install_data;
+ svn_checksum_t *install_sha1_checksum;
+ svn_checksum_t *install_md5_checksum;
+ svn_error_t *err;
+
+ SVN_ERR(svn_wc__textbase_prepare_install(&install_stream,
+ &install_data,
+ &install_sha1_checksum,
+ &install_md5_checksum,
+ db, dst_abspath, FALSE,
+ scratch_pool, scratch_pool));
+
+ err = svn_stream_copy3(contents, install_stream, NULL, NULL, scratch_pool);
+ if (err)
+ return svn_error_compose_create(err,
+ svn_wc__db_pristine_install_abort(install_data, scratch_pool));
+
+ SVN_ERR(svn_wc__db_pristine_install(install_data,
+ install_sha1_checksum,
+ install_md5_checksum,
+ scratch_pool));
+ }
+ }
/* Prepare a temp copy of the filesystem node. It is usually a file, but
copy recursively if it's a dir. */
@@ -349,6 +389,7 @@
const char *tmpdir_abspath,
svn_boolean_t metadata_only,
svn_boolean_t is_move,
+ svn_boolean_t within_one_wc,
const svn_io_dirent2_t *dirent,
svn_cancel_func_t cancel_func,
void *cancel_baton,
@@ -454,7 +495,7 @@
dst_op_root_abspath,
tmpdir_abspath,
metadata_only, info->conflicted,
- is_move,
+ is_move, within_one_wc,
disk_children
? svn_hash_gets(disk_children,
child_name)
@@ -469,7 +510,7 @@
SVN_ERR(copy_versioned_dir(db,
child_src_abspath, child_dst_abspath,
dst_op_root_abspath, tmpdir_abspath,
- metadata_only, is_move,
+ metadata_only, is_move, within_one_wc,
disk_children
? svn_hash_gets(disk_children,
child_name)
@@ -839,17 +880,12 @@
is_move = FALSE;
}
- if (!within_one_wc)
- SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath,
- cancel_func, cancel_baton,
- scratch_pool));
-
if (src_db_kind == svn_node_file
|| src_db_kind == svn_node_symlink)
{
err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,
- tmpdir_abspath,
- metadata_only, conflicted, is_move,
+ tmpdir_abspath, metadata_only, conflicted,
+ is_move, within_one_wc,
NULL, recorded_size, recorded_time,
cancel_func, cancel_baton,
notify_func, notify_baton,
@@ -888,7 +924,7 @@
err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,
tmpdir_abspath, metadata_only, is_move,
- NULL /* dirent */,
+ within_one_wc, NULL /* dirent */,
cancel_func, cancel_baton,
notify_func, notify_baton,
scratch_pool);
diff --git a/subversion/libsvn_wc/wc-queries.sql b/subversion/libsvn_wc/wc-queries.sql
index be263b7..dd42af7 100644
--- a/subversion/libsvn_wc/wc-queries.sql
+++ b/subversion/libsvn_wc/wc-queries.sql
@@ -925,44 +925,6 @@
DELETE FROM pristine
WHERE checksum = ?1 AND refcount = 0
--- STMT_SELECT_COPY_PRISTINES_F31
-/* For the root itself */
-SELECT n.checksum, md5_checksum, size, 1
-FROM nodes_current n
-LEFT JOIN pristine p ON n.checksum = p.checksum
-WHERE wc_id = ?1
- AND n.local_relpath = ?2
- AND n.checksum IS NOT NULL
-UNION ALL
-/* And all descendants */
-SELECT n.checksum, md5_checksum, size, 1
-FROM nodes n
-LEFT JOIN pristine p ON n.checksum = p.checksum
-WHERE wc_id = ?1
- AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
- AND op_depth >=
- (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
- AND n.checksum IS NOT NULL
-
--- STMT_SELECT_COPY_PRISTINES_F32
-/* For the root itself */
-SELECT n.checksum, md5_checksum, size, p.hydrated
-FROM nodes_current n
-LEFT JOIN pristine p ON n.checksum = p.checksum
-WHERE wc_id = ?1
- AND n.local_relpath = ?2
- AND n.checksum IS NOT NULL
-UNION ALL
-/* And all descendants */
-SELECT n.checksum, md5_checksum, size, p.hydrated
-FROM nodes n
-LEFT JOIN pristine p ON n.checksum = p.checksum
-WHERE wc_id = ?1
- AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
- AND op_depth >=
- (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
- AND n.checksum IS NOT NULL
-
-- STMT_UPDATE_PRISTINE_HYDRATED
UPDATE pristine SET hydrated = ?2
WHERE checksum = ?1
diff --git a/subversion/libsvn_wc/wc_db.h b/subversion/libsvn_wc/wc_db.h
index ca1c548..32e14e6 100644
--- a/subversion/libsvn_wc/wc_db.h
+++ b/subversion/libsvn_wc/wc_db.h
@@ -1041,16 +1041,6 @@
apr_pool_t *scratch_pool);
-/* If necessary transfers the PRISTINE files of the tree rooted at
- SRC_LOCAL_ABSPATH to the working copy identified by DST_WRI_ABSPATH. */
-svn_error_t *
-svn_wc__db_pristine_transfer(svn_wc__db_t *db,
- const char *src_local_abspath,
- const char *dst_wri_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool);
-
/* Remove the pristine text with SHA-1 checksum SHA1_CHECKSUM from the
* pristine store, iff it is not referenced by any of the (other) WC DB
* tables. */
diff --git a/subversion/libsvn_wc/wc_db_pristine.c b/subversion/libsvn_wc/wc_db_pristine.c
index 1b36afb..2e56268 100644
--- a/subversion/libsvn_wc/wc_db_pristine.c
+++ b/subversion/libsvn_wc/wc_db_pristine.c
@@ -585,197 +585,6 @@
return svn_error_trace(svn_sqlite__reset(stmt));
}
-/* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
- pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM, SIZE and
- HYDRATED. */
-static svn_error_t *
-maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
- svn_wc__db_wcroot_t *dst_wcroot,
- const svn_checksum_t *checksum,
- const svn_checksum_t *md5_checksum,
- apr_int64_t size,
- svn_boolean_t hydrated,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- int stmt_num;
- svn_sqlite__stmt_t *stmt;
- int affected_rows;
-
- stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
- ? STMT_INSERT_OR_IGNORE_PRISTINE_F32
- : STMT_INSERT_OR_IGNORE_PRISTINE_F31);
- SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb, stmt_num));
- SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
- SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
- SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
- if (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE)
- SVN_ERR(svn_sqlite__bind_int(stmt, 4, hydrated));
-
- SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
-
- if (affected_rows == 0)
- return SVN_NO_ERROR;
-
- if (hydrated)
- {
- const char *pristine_abspath;
- svn_stream_t *src_stream;
- svn_stream_t *dst_stream;
- const char *tmp_abspath;
- const char *src_abspath;
- svn_error_t *err;
-
- SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
- pristine_get_tempdir(dst_wcroot,
- scratch_pool,
- scratch_pool),
- svn_io_file_del_on_pool_cleanup,
- scratch_pool, scratch_pool));
-
- SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
- scratch_pool, scratch_pool));
-
- SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
- scratch_pool, scratch_pool));
-
- /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
- SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
- cancel_func, cancel_baton,
- scratch_pool));
-
- SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
- scratch_pool, scratch_pool));
-
- /* Move the file to its target location. (If it is already there, it is
- * an orphan file and it doesn't matter if we overwrite it.) */
- err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
- scratch_pool);
-
- /* Maybe the directory doesn't exist yet? */
- if (err && APR_STATUS_IS_ENOENT(err->apr_err))
- {
- svn_error_t *err2;
-
- err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
- scratch_pool),
- APR_OS_DEFAULT, scratch_pool);
-
- if (err2)
- /* Creating directory didn't work: Return all errors */
- return svn_error_trace(svn_error_compose_create(err, err2));
- else
- /* We could create a directory: retry install */
- svn_error_clear(err);
-
- SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
- scratch_pool));
- }
- else
- SVN_ERR(err);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Transaction implementation of svn_wc__db_pristine_transfer().
- We have a lock on DST_WCROOT.
- */
-static svn_error_t *
-pristine_transfer_txn(svn_wc__db_wcroot_t *src_wcroot,
- svn_wc__db_wcroot_t *dst_wcroot,
- const char *src_relpath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- int stmt_num;
- svn_sqlite__stmt_t *stmt;
- svn_boolean_t got_row;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
- stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
- ? STMT_SELECT_COPY_PRISTINES_F32
- : STMT_SELECT_COPY_PRISTINES_F31);
- SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb, stmt_num));
- SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
-
- /* This obtains an sqlite read lock on src_wcroot */
- SVN_ERR(svn_sqlite__step(&got_row, stmt));
-
- while (got_row)
- {
- const svn_checksum_t *checksum;
- const svn_checksum_t *md5_checksum;
- apr_int64_t size;
- svn_boolean_t hydrated;
- svn_error_t *err;
-
- svn_pool_clear(iterpool);
-
- SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
- SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
- size = svn_sqlite__column_int64(stmt, 2);
- hydrated = svn_sqlite__column_boolean(stmt, 3);
-
- err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
- checksum, md5_checksum, size,
- hydrated,
- cancel_func, cancel_baton,
- iterpool);
-
- if (err)
- return svn_error_trace(svn_error_compose_create(
- err,
- svn_sqlite__reset(stmt)));
-
- SVN_ERR(svn_sqlite__step(&got_row, stmt));
- }
- SVN_ERR(svn_sqlite__reset(stmt));
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_wc__db_pristine_transfer(svn_wc__db_t *db,
- const char *src_local_abspath,
- const char *dst_wri_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- svn_wc__db_wcroot_t *src_wcroot, *dst_wcroot;
- const char *src_relpath, *dst_relpath;
-
- SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&src_wcroot, &src_relpath,
- db, src_local_abspath,
- scratch_pool, scratch_pool));
- VERIFY_USABLE_WCROOT(src_wcroot);
- SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&dst_wcroot, &dst_relpath,
- db, dst_wri_abspath,
- scratch_pool, scratch_pool));
- VERIFY_USABLE_WCROOT(dst_wcroot);
-
- if (src_wcroot == dst_wcroot
- || src_wcroot->sdb == dst_wcroot->sdb)
- {
- return SVN_NO_ERROR; /* Nothing to transfer */
- }
-
- SVN_WC__DB_WITH_TXN(
- pristine_transfer_txn(src_wcroot, dst_wcroot, src_relpath,
- cancel_func, cancel_baton, scratch_pool),
- dst_wcroot);
-
- return SVN_NO_ERROR;
-}
-
-
-
-
/* If the pristine text referenced by SHA1_CHECKSUM in WCROOT/SDB, whose path
* within the pristine store is PRISTINE_ABSPATH, has a reference count of
* zero, delete it (both the database row and the disk file).
diff --git a/subversion/tests/cmdline/store_pristine_tests.py b/subversion/tests/cmdline/store_pristine_tests.py
index 115d33c..0ce4d4c 100755
--- a/subversion/tests/cmdline/store_pristine_tests.py
+++ b/subversion/tests/cmdline/store_pristine_tests.py
@@ -850,6 +850,100 @@
'info', '--show-item=store-pristine', '--no-newline',
sbox.wc_dir)
+def copy_cross_wc_without_src_pristine(sbox):
+ "cross-wc copy without src pristine"
+
+ sbox.build(empty=True, create_wc=False)
+
+ expected_output = svntest.wc.State(sbox.wc_dir, {})
+ expected_wc = svntest.wc.State('', {})
+ svntest.actions.run_and_verify_checkout(sbox.repo_url,
+ sbox.wc_dir,
+ expected_output,
+ expected_wc,
+ [],
+ '--store-pristine=no')
+ svntest.actions.run_and_verify_svn(
+ ['no'], [],
+ 'info', '--show-item=store-pristine', '--no-newline',
+ sbox.wc_dir)
+
+ sbox.simple_append('file', 'foo')
+ sbox.simple_add('file')
+ sbox.simple_commit(message='r1')
+
+ wc_dir2 = sbox.add_wc_path("other")
+ expected_output = svntest.wc.State(wc_dir2, {})
+ expected_wc = svntest.wc.State('', {})
+ svntest.actions.run_and_verify_checkout(sbox.repo_url,
+ wc_dir2,
+ expected_output,
+ expected_wc,
+ [],
+ '--store-pristine=yes', '-r0')
+ svntest.actions.run_and_verify_svn(
+ ['yes'], [],
+ 'info', '--show-item=store-pristine', '--no-newline',
+ wc_dir2)
+
+ svntest.actions.run_and_verify_svn(None, [], 'copy',
+ sbox.ospath('file'),
+ wc_dir2)
+
+ expected_status = svntest.wc.State(wc_dir2, {
+ '' : Item(status=' ', wc_rev=0),
+ 'file' : Item(status='A ', wc_rev='-', copied='+'),
+ })
+ svntest.actions.run_and_verify_status(wc_dir2,
+ expected_status)
+
+def copy_cross_wc_without_dst_pristine(sbox):
+ "cross-wc copy without dst pristine"
+
+ sbox.build(empty=True, create_wc=False)
+
+ expected_output = svntest.wc.State(sbox.wc_dir, {})
+ expected_wc = svntest.wc.State('', {})
+ svntest.actions.run_and_verify_checkout(sbox.repo_url,
+ sbox.wc_dir,
+ expected_output,
+ expected_wc,
+ [],
+ '--store-pristine=yes')
+ svntest.actions.run_and_verify_svn(
+ ['yes'], [],
+ 'info', '--show-item=store-pristine', '--no-newline',
+ sbox.wc_dir)
+
+ sbox.simple_append('file', 'foo')
+ sbox.simple_add('file')
+ sbox.simple_commit(message='r1')
+
+ wc_dir2 = sbox.add_wc_path("other")
+ expected_output = svntest.wc.State(wc_dir2, {})
+ expected_wc = svntest.wc.State('', {})
+ svntest.actions.run_and_verify_checkout(sbox.repo_url,
+ wc_dir2,
+ expected_output,
+ expected_wc,
+ [],
+ '--store-pristine=no', '-r0')
+ svntest.actions.run_and_verify_svn(
+ ['no'], [],
+ 'info', '--show-item=store-pristine', '--no-newline',
+ wc_dir2)
+
+ svntest.actions.run_and_verify_svn(None, [], 'copy',
+ sbox.ospath('file'),
+ wc_dir2)
+
+ expected_status = svntest.wc.State(wc_dir2, {
+ '' : Item(status=' ', wc_rev=0),
+ 'file' : Item(status='A ', wc_rev='-', copied='+'),
+ })
+ svntest.actions.run_and_verify_status(wc_dir2,
+ expected_status)
+
########################################################################
# Run the tests
@@ -879,6 +973,8 @@
move_modified_file_with_pristine,
move_modified_file_without_pristine,
checkout_incompatible_setting,
+ copy_cross_wc_without_src_pristine,
+ copy_cross_wc_without_dst_pristine,
]
serial_only = True
diff --git a/subversion/tests/libsvn_client/client-test.c b/subversion/tests/libsvn_client/client-test.c
index 5409902..bc97b9a 100644
--- a/subversion/tests/libsvn_client/client-test.c
+++ b/subversion/tests/libsvn_client/client-test.c
@@ -449,6 +449,7 @@
const char *ex_file_path;
const char *ex_dir_path;
const char *ex2_dir_path;
+ svn_boolean_t store_pristine;
/* Create a filesystem and repository containing the Greek tree. */
SVN_ERR(create_greek_repos(&repos_url, "test-wc-add-repos", opts, pool));
@@ -472,6 +473,12 @@
opts->store_pristine,
ctx, pool));
+ SVN_ERR(svn_wc__get_settings(NULL, &store_pristine, ctx->wc_ctx,
+ wc_path, pool));
+ if (!store_pristine)
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "Test assumes a working copy with pristine");
+
/* Now checkout again as wc_path/NEW */
new_dir_path = svn_dirent_join(wc_path, "NEW", pool);
SVN_ERR(svn_client_checkout4(NULL, repos_url, new_dir_path, &peg_rev, &rev,
diff --git a/subversion/tests/libsvn_wc/wc-queries-test.c b/subversion/tests/libsvn_wc/wc-queries-test.c
index ccb748c..f95b1eb 100644
--- a/subversion/tests/libsvn_wc/wc-queries-test.c
+++ b/subversion/tests/libsvn_wc/wc-queries-test.c
@@ -244,12 +244,9 @@
case STMT_INSERT_OR_IGNORE_PRISTINE_F31:
case STMT_UPSERT_PRISTINE_F31:
case STMT_SELECT_PRISTINE_F31:
- case STMT_SELECT_COPY_PRISTINES_F31:
- return (wc_format <= 31);
case STMT_INSERT_OR_IGNORE_PRISTINE_F32:
case STMT_UPSERT_PRISTINE_F32:
case STMT_SELECT_PRISTINE_F32:
- case STMT_SELECT_COPY_PRISTINES_F32:
case STMT_UPDATE_PRISTINE_HYDRATED:
case STMT_TEXTBASE_ADD_REF:
case STMT_TEXTBASE_REMOVE_REF: