summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_base/bdb/changes-table.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_base/bdb/changes-table.c')
-rw-r--r--subversion/libsvn_fs_base/bdb/changes-table.c81
1 files changed, 53 insertions, 28 deletions
diff --git a/subversion/libsvn_fs_base/bdb/changes-table.c b/subversion/libsvn_fs_base/bdb/changes-table.c
index 80ff468074e8..b20631863369 100644
--- a/subversion/libsvn_fs_base/bdb/changes-table.c
+++ b/subversion/libsvn_fs_base/bdb/changes-table.c
@@ -121,12 +121,32 @@ svn_fs_bdb__changes_delete(svn_fs_t *fs,
return SVN_NO_ERROR;
}
+/* Return a deep FS API type copy of SOURCE in internal format and allocate
+ * the result in RESULT_POOL.
+ */
+static svn_fs_path_change2_t *
+change_to_fs_change(const change_t *change,
+ apr_pool_t *result_pool)
+{
+ svn_fs_path_change2_t *result = svn_fs__path_change_create_internal(
+ svn_fs_base__id_copy(change->noderev_id,
+ result_pool),
+ change->kind,
+ result_pool);
+ result->text_mod = change->text_mod;
+ result->prop_mod = change->prop_mod;
+ result->node_kind = svn_node_unknown;
+ result->copyfrom_known = FALSE;
+
+ return result;
+}
/* Merge the internal-use-only CHANGE into a hash of public-FS
svn_fs_path_change2_t CHANGES, collapsing multiple changes into a
single succinct change per path. */
static svn_error_t *
fold_change(apr_hash_t *changes,
+ apr_hash_t *deletions,
const change_t *change)
{
apr_pool_t *pool = apr_hash_pool_get(changes);
@@ -185,7 +205,7 @@ fold_change(apr_hash_t *changes,
case svn_fs_path_change_reset:
/* A reset here will simply remove the path change from the
hash. */
- old_change = NULL;
+ new_change = NULL;
break;
case svn_fs_path_change_delete:
@@ -194,14 +214,21 @@ fold_change(apr_hash_t *changes,
/* If the path was introduced in this transaction via an
add, and we are deleting it, just remove the path
altogether. */
- old_change = NULL;
+ new_change = NULL;
+ }
+ else if (old_change->change_kind == svn_fs_path_change_replace)
+ {
+ /* A deleting a 'replace' restore the original deletion. */
+ new_change = svn_hash_gets(deletions, path);
+ SVN_ERR_ASSERT(new_change);
}
else
{
/* A deletion overrules all previous changes. */
- old_change->change_kind = svn_fs_path_change_delete;
- old_change->text_mod = change->text_mod;
- old_change->prop_mod = change->prop_mod;
+ new_change = old_change;
+ new_change->change_kind = svn_fs_path_change_delete;
+ new_change->text_mod = change->text_mod;
+ new_change->prop_mod = change->prop_mod;
}
break;
@@ -209,38 +236,33 @@ fold_change(apr_hash_t *changes,
case svn_fs_path_change_replace:
/* An add at this point must be following a previous delete,
so treat it just like a replace. */
- old_change->change_kind = svn_fs_path_change_replace;
- old_change->node_rev_id = svn_fs_base__id_copy(change->noderev_id,
- pool);
- old_change->text_mod = change->text_mod;
- old_change->prop_mod = change->prop_mod;
+
+ new_change = change_to_fs_change(change, pool);
+ new_change->change_kind = svn_fs_path_change_replace;
+
+ /* Remember the original deletion.
+ * Make sure to allocate the hash key in a durable pool. */
+ svn_hash_sets(deletions,
+ apr_pstrdup(apr_hash_pool_get(deletions), path),
+ old_change);
break;
case svn_fs_path_change_modify:
default:
+ new_change = old_change;
if (change->text_mod)
- old_change->text_mod = TRUE;
+ new_change->text_mod = TRUE;
if (change->prop_mod)
- old_change->prop_mod = TRUE;
+ new_change->prop_mod = TRUE;
break;
}
-
- /* Point our new_change to our (possibly modified) old_change. */
- new_change = old_change;
}
else
{
/* This change is new to the hash, so make a new public change
structure from the internal one (in the hash's pool), and dup
the path into the hash's pool, too. */
- new_change = svn_fs__path_change_create_internal(
- svn_fs_base__id_copy(change->noderev_id, pool),
- change->kind,
- pool);
- new_change->text_mod = change->text_mod;
- new_change->prop_mod = change->prop_mod;
- new_change->node_kind = svn_node_unknown;
- new_change->copyfrom_known = FALSE;
+ new_change = change_to_fs_change(change, pool);
path = apr_pstrdup(pool, change->path);
}
@@ -265,6 +287,8 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
svn_error_t *err = SVN_NO_ERROR;
apr_hash_t *changes = apr_hash_make(pool);
apr_pool_t *subpool = svn_pool_create(pool);
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_hash_t *deletions = apr_hash_make(subpool);
/* Get a cursor on the first record matching KEY, and then loop over
the records, adding them to the return array. */
@@ -286,11 +310,11 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
svn_skel_t *result_skel;
/* Clear the per-iteration subpool. */
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
/* RESULT now contains a change record associated with KEY. We
need to parse that skel into an change_t structure ... */
- result_skel = svn_skel__parse(result.data, result.size, subpool);
+ result_skel = svn_skel__parse(result.data, result.size, iterpool);
if (! result_skel)
{
err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
@@ -298,12 +322,12 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
key);
goto cleanup;
}
- err = svn_fs_base__parse_change_skel(&change, result_skel, subpool);
+ err = svn_fs_base__parse_change_skel(&change, result_skel, iterpool);
if (err)
goto cleanup;
/* ... and merge it with our return hash. */
- err = fold_change(changes, change);
+ err = fold_change(changes, deletions, change);
if (err)
goto cleanup;
@@ -319,7 +343,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
{
apr_hash_index_t *hi;
- for (hi = apr_hash_first(subpool, changes);
+ for (hi = apr_hash_first(iterpool, changes);
hi;
hi = apr_hash_next(hi))
{
@@ -347,6 +371,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
}
/* Destroy the per-iteration subpool. */
+ svn_pool_destroy(iterpool);
svn_pool_destroy(subpool);
/* If there are no (more) change records for this KEY, we're