Backport branch for issue #2608:
* r21406-21407, r21540
Fix issue #2608: Can set directory props on the root of an FSFS
filesystem from an out-of-date repository.
Justification:
Minor data loss bug. Has been in trunk for a year (so probably
safe). Now reported by a user
(http://svn.haxx.se/dev/archive-2007-09/0441.shtml). Has unit
and end-to-end tests.
Votes:
+1: glasser, dlr
This produced by:
$ svnmerge merge -r21406-21407,21540
and then:
* subversion/tests/cmdline/prop_tests.py
(is_this_fsfs): Remove helper function added in r21407, moved
elsewhere in a later commit, and made irrelevant by r21540.
(test_list): Resolve trivial conflict.
git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.4.x-issue-2608@866974 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_fs_fs/dag.c b/subversion/libsvn_fs_fs/dag.c
index 80206f9..e37f3e5 100644
--- a/subversion/libsvn_fs_fs/dag.c
+++ b/subversion/libsvn_fs_fs/dag.c
@@ -53,6 +53,12 @@
/* The node revision ID for this dag node, allocated in POOL. */
svn_fs_id_t *id;
+ /* In the special case that this node is the root of a transaction
+ that has not yet been modified, the node revision ID for this dag
+ node's predecessor; otherwise NULL. (Used in
+ svn_fs_node_created_rev.) */
+ const svn_fs_id_t *fresh_root_predecessor_id;
+
/* The node's type (file, dir, etc.) */
svn_node_kind_t kind;
@@ -185,6 +191,11 @@
new_node->kind = noderev->kind;
new_node->created_path = apr_pstrdup(pool, noderev->created_path);
+ if (noderev->is_fresh_txn_root)
+ new_node->fresh_root_predecessor_id = noderev->predecessor_id;
+ else
+ new_node->fresh_root_predecessor_id = NULL;
+
/* Return a fresh new node */
*node = new_node;
return SVN_NO_ERROR;
@@ -196,8 +207,14 @@
dag_node_t *node,
apr_pool_t *pool)
{
+ /* In the special case that this is an unmodified transaction root,
+ we need to actually get the revision of the noderev's predecessor
+ (the revision root); see Issue #2608. */
+ const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
+ ? node->fresh_root_predecessor_id : node->id;
+
/* Look up the committed revision from the Node-ID. */
- *rev = svn_fs_fs__id_rev(node->id);
+ *rev = svn_fs_fs__id_rev(correct_id);
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_fs_fs/fs.h b/subversion/libsvn_fs_fs/fs.h
index 7c83e28..3daa47f 100644
--- a/subversion/libsvn_fs_fs/fs.h
+++ b/subversion/libsvn_fs_fs/fs.h
@@ -236,6 +236,9 @@
/* path at which this node first came into existence. */
const char *created_path;
+ /* is this the unmodified root of a transaction? */
+ svn_boolean_t is_fresh_txn_root;
+
} node_revision_t;
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index 45af607..811aa0b 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -83,6 +83,7 @@
#define HEADER_PRED "pred"
#define HEADER_COPYFROM "copyfrom"
#define HEADER_COPYROOT "copyroot"
+#define HEADER_FRESHTXNRT "is-fresh-txn-root"
/* Kinds that a change can be. */
#define ACTION_MODIFY "modify"
@@ -1134,6 +1135,10 @@
noderev->copyfrom_path = apr_pstrdup(pool, last_str);
}
+ /* Get whether this is a fresh txn root. */
+ value = apr_hash_get(headers, HEADER_FRESHTXNRT, APR_HASH_KEY_STRING);
+ noderev->is_fresh_txn_root = (value != NULL);
+
*noderev_p = noderev;
return SVN_NO_ERROR;
@@ -1213,6 +1218,9 @@
noderev->copyroot_rev,
noderev->copyroot_path));
+ if (noderev->is_fresh_txn_root)
+ SVN_ERR(svn_stream_printf(outfile, pool, HEADER_FRESHTXNRT ": y\n"));
+
SVN_ERR(svn_stream_printf(outfile, pool, "\n"));
return SVN_NO_ERROR;
@@ -1222,11 +1230,14 @@
svn_fs_fs__put_node_revision(svn_fs_t *fs,
const svn_fs_id_t *id,
node_revision_t *noderev,
+ svn_boolean_t fresh_txn_root,
apr_pool_t *pool)
{
apr_file_t *noderev_file;
const char *txn_id = svn_fs_fs__id_txn_id(id);
+ noderev->is_fresh_txn_root = fresh_txn_root;
+
if (! txn_id)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Attempted to write to non-transaction"));
@@ -2768,7 +2779,8 @@
}
/* Copy a revision node-rev SRC into the current transaction TXN_ID in
- the filesystem FS. Allocations are from POOL. */
+ the filesystem FS. This is only used to create the root of a transaction.
+ Allocations are from POOL. */
static svn_error_t *
create_new_txn_noderev_from_rev(svn_fs_t *fs,
const char *txn_id,
@@ -2795,7 +2807,7 @@
copy_id = svn_fs_fs__id_copy_id(noderev->id);
noderev->id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
- SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, TRUE, pool));
return SVN_NO_ERROR;
}
@@ -3081,7 +3093,7 @@
noderev->id = id;
- SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
*id_p = id;
@@ -3200,7 +3212,7 @@
rep->txn_id = txn_id;
parent_noderev->data_rep = rep;
SVN_ERR(svn_fs_fs__put_node_revision(fs, parent_noderev->id,
- parent_noderev, pool));
+ parent_noderev, FALSE, pool));
}
else
{
@@ -3564,7 +3576,7 @@
b->noderev->data_rep = rep;
/* Write out the new node-rev information. */
- SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev,
+ SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev, FALSE,
b->pool));
SVN_ERR(svn_io_file_close(b->file, b->pool));
@@ -3638,7 +3650,7 @@
new_noderev->copyroot_rev = svn_fs_fs__id_rev(new_noderev->id);
}
- SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev,
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev, FALSE,
pool));
*new_id_p = id;
@@ -3669,7 +3681,7 @@
{
noderev->prop_rep = apr_pcalloc(pool, sizeof(*noderev->prop_rep));
noderev->prop_rep->txn_id = svn_fs_fs__id_txn_id(noderev->id);
- SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
}
return SVN_NO_ERROR;
@@ -3908,7 +3920,7 @@
/* Write out our new node-revision. */
SVN_ERR(write_noderev_txn(file, noderev, pool));
- SVN_ERR(svn_fs_fs__put_node_revision(fs, id, noderev, pool));
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, id, noderev, FALSE, pool));
/* Return our ID that references the revision file. */
*new_id_p = noderev->id;
diff --git a/subversion/libsvn_fs_fs/fs_fs.h b/subversion/libsvn_fs_fs/fs_fs.h
index 186c112..21d509b 100644
--- a/subversion/libsvn_fs_fs/fs_fs.h
+++ b/subversion/libsvn_fs_fs/fs_fs.h
@@ -40,10 +40,12 @@
apr_pool_t *pool);
/* Store NODEREV as the node-revision for the node whose id is ID in
- FS. Do any necessary temporary allocation in POOL. */
+ FS, after setting its is_fresh_txn_root to FRESH_TXN_ROOT. Do any
+ necessary temporary allocation in POOL. */
svn_error_t *svn_fs_fs__put_node_revision(svn_fs_t *fs,
const svn_fs_id_t *id,
node_revision_t *noderev,
+ svn_boolean_t fresh_txn_root,
apr_pool_t *pool);
/* Set *YOUNGEST to the youngest revision in filesystem FS. Do any
diff --git a/subversion/libsvn_fs_fs/tree.c b/subversion/libsvn_fs_fs/tree.c
index 5a42b43..66e8e49 100644
--- a/subversion/libsvn_fs_fs/tree.c
+++ b/subversion/libsvn_fs_fs/tree.c
@@ -1195,7 +1195,7 @@
noderev->predecessor_count = source_pred_count;
if (noderev->predecessor_count != -1)
noderev->predecessor_count++;
- SVN_ERR(svn_fs_fs__put_node_revision(fs, target_id, noderev, pool));
+ SVN_ERR(svn_fs_fs__put_node_revision(fs, target_id, noderev, FALSE, pool));
return SVN_NO_ERROR;
}
diff --git a/subversion/tests/cmdline/prop_tests.py b/subversion/tests/cmdline/prop_tests.py
index 5994b7c..ebe0d23 100755
--- a/subversion/tests/cmdline/prop_tests.py
+++ b/subversion/tests/cmdline/prop_tests.py
@@ -355,6 +355,33 @@
svntest.actions.run_and_verify_status(wc_dir, expected_status)
#----------------------------------------------------------------------
+def commit_conflict_dirprops(sbox):
+ "commit with conflicting dirprops"
+
+ # Issue #2608: failure to see conflicting dirprops on root of
+ # repository.
+
+ # Bootstrap
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ svntest.main.run_svn(None, 'propset', 'foo', 'bar', wc_dir)
+
+ # Commit the file and directory
+ svntest.main.run_svn(None, 'ci', '-m', 'r2', wc_dir)
+
+ # Update to rev 1
+ svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)
+
+ # Add conflicting properties
+ svntest.main.run_svn(None, 'propset', 'foo', 'eek', wc_dir)
+
+ svntest.actions.run_and_verify_commit(wc_dir, None, None,
+ "Out of date: '' in transaction",
+ None, None, None, None,
+ wc_dir)
+
+#----------------------------------------------------------------------
# Issue #742: we used to screw up when committing a file replacement
# that also had properties. It was fixed by teaching
@@ -1138,6 +1165,7 @@
downdate_props,
remove_props,
update_conflict_props,
+ commit_conflict_dirprops,
commit_replacement_props,
revert_replacement_props,
inappropriate_props,
diff --git a/subversion/tests/libsvn_repos/repos-test.c b/subversion/tests/libsvn_repos/repos-test.c
index dbc2f37..3bce7b6 100644
--- a/subversion/tests/libsvn_repos/repos-test.c
+++ b/subversion/tests/libsvn_repos/repos-test.c
@@ -705,6 +705,58 @@
return SVN_NO_ERROR;
}
+/* See Issue #2608. */
+
+static svn_error_t *
+created_rev_root(const char **msg,
+ svn_boolean_t msg_only,
+ svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ apr_pool_t *spool = svn_pool_create(pool);
+ svn_repos_t *repos;
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ svn_revnum_t youngest_rev = 0, rev;
+
+ *msg = "test svn_fs_node_created_rev on root";
+
+ if (msg_only)
+ return SVN_NO_ERROR;
+
+ /* Create a filesystem and repository. */
+ SVN_ERR(svn_test__create_repos(&repos, "test-repo-created-rev-root",
+ opts->fs_type, pool));
+ fs = svn_repos_fs(repos);
+
+ /* Created the greek tree in revision 1. */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
+ SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
+ SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
+ svn_pool_clear(spool);
+
+ SVN_ERR(svn_repos_fs_begin_txn_for_commit(&txn,
+ repos,
+ 1,
+ "someuser", "log",
+ spool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
+ SVN_ERR(svn_fs_node_created_rev(&rev, txn_root, "", spool));
+ svn_pool_clear(spool);
+
+ if (rev == SVN_INVALID_REVNUM)
+ return svn_error_createf
+ (SVN_ERR_FS_GENERAL, NULL, "Root has invalid created revnum");
+
+ /* Destroy the subpool. */
+ svn_pool_destroy(spool);
+
+ return SVN_NO_ERROR;
+}
+
+
struct locations_info
@@ -1759,6 +1811,7 @@
SVN_TEST_PASS(dir_deltas),
SVN_TEST_PASS(node_tree_delete_under_copy),
SVN_TEST_PASS(revisions_changed),
+ SVN_TEST_PASS(created_rev_root),
SVN_TEST_PASS(node_locations),
SVN_TEST_PASS(node_locations2),
SVN_TEST_PASS(rmlocks),