/* Create a packed filesystem in DIR. Set the shard size to SHARD_SIZE and create NUM_REVS number of revisions (in addition to r0). Use POOL for allocations. After this function successfully completes, the filesystem's youngest revision number will be the same as NUM_REVS. */ static svn_error_t * create_packed_filesystem(const char *dir, const svn_test_opts_t *opts, int num_revs, int shard_size, apr_pool_t *pool) { svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *iterpool; int version; /* Create a filesystem, then close it */ SVN_ERR(svn_test__create_fs(&fs, dir, opts, subpool)); svn_pool_destroy(subpool); subpool = svn_pool_create(pool); /* Rewrite the format file */ SVN_ERR(svn_io_read_version_file(&version, svn_dirent_join(dir, "format", subpool), subpool)); SVN_ERR(write_format(dir, version, shard_size, subpool)); /* Reopen the filesystem */ SVN_ERR(svn_fs_open(&fs, dir, NULL, subpool)); /* Revision 1: the Greek tree */ SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_test__create_greek_tree(txn_root, subpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); /* Revisions 2 thru NUM_REVS-1: content tweaks to "iota". */ iterpool = svn_pool_create(subpool); while (after_rev < num_revs) { svn_pool_clear(iterpool); SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, iterpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool)); SVN_ERR(svn_test__set_file_contents(txn_root, "iota", get_rev_contents(after_rev + 1, iterpool), iterpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, iterpool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); } svn_pool_destroy(iterpool); svn_pool_destroy(subpool); /* Now pack the FS */ return svn_fs_pack(dir, NULL, NULL, NULL, NULL, pool); }
/* Create a packed filesystem in DIR. Set the shard size to SHARD_SIZE and create MAX_REV number of revisions. Use POOL for allocations. */ static svn_error_t * create_packed_filesystem(const char *dir, svn_test_opts_t *opts, int max_rev, int shard_size, apr_pool_t *pool) { svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *iterpool; /* Create a filesystem, then close it */ SVN_ERR(svn_test__create_fs(&fs, dir, opts, subpool)); svn_pool_destroy(subpool); subpool = svn_pool_create(pool); /* Rewrite the format file */ SVN_ERR(write_format(dir, SVN_FS_FS__MIN_PACKED_FORMAT, shard_size, subpool)); /* Reopen the filesystem */ SVN_ERR(svn_fs_open(&fs, dir, NULL, subpool)); /* Revision 1: the Greek tree */ SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_test__create_greek_tree(txn_root, subpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool)); /* Revisions 2-11: A bunch of random changes. */ iterpool = svn_pool_create(subpool); while (after_rev < max_rev + 1) { svn_pool_clear(iterpool); SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, iterpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool)); SVN_ERR(svn_test__set_file_contents(txn_root, "iota", get_rev_contents(after_rev + 1, iterpool), iterpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, iterpool)); } svn_pool_destroy(iterpool); svn_pool_destroy(subpool); /* Now pack the FS */ return svn_fs_pack(dir, NULL, NULL, NULL, NULL, pool); }
svn_error_t * svn_repos_fs_commit_txn(const char **conflict_p, svn_repos_t *repos, svn_revnum_t *new_rev, svn_fs_txn_t *txn, apr_pool_t *pool) { svn_error_t *err; const char *txn_name; /* Run pre-commit hooks. */ SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_repos__hooks_pre_commit(repos, txn_name, pool)); /* Commit. */ SVN_ERR(svn_fs_commit_txn(conflict_p, new_rev, txn, pool)); /* Run post-commit hooks. Notice that we're wrapping the error with a -specific- errorcode, so that our caller knows not to try and abort the transaction. */ if ((err = svn_repos__hooks_post_commit(repos, *new_rev, pool))) return svn_error_create (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, _("Commit succeeded, but post-commit hook failed")); return SVN_NO_ERROR; }
svn_error_t * svn_repos_fs_commit_txn(const char **conflict_p, svn_repos_t *repos, svn_revnum_t *new_rev, svn_fs_txn_t *txn, apr_pool_t *pool) { svn_error_t *err, *err2; const char *txn_name; *new_rev = SVN_INVALID_REVNUM; /* Run pre-commit hooks. */ SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_repos__hooks_pre_commit(repos, txn_name, pool)); /* Commit. */ err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool); if (! SVN_IS_VALID_REVNUM(*new_rev)) return err; /* Run post-commit hooks. */ if ((err2 = svn_repos__hooks_post_commit(repos, *new_rev, pool))) { err2 = svn_error_create (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2, _("Commit succeeded, but post-commit hook failed")); } return svn_error_compose_create(err, err2); }
static svn_error_t * commit_packed_fs(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "fsfs") != 0) || (opts->server_minor_version && (opts->server_minor_version < 6))) return SVN_NO_ERROR; /* Create the packed FS and open it. */ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, 5, pool)); SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, pool)); /* Now do a commit. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_test__set_file_contents(txn_root, "iota", "How much better is it to get wisdom than gold! and to get " "understanding rather to be chosen than silver!", pool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, pool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); return SVN_NO_ERROR; }
svn_error_t * svn_repos_fs_commit_txn(const char **conflict_p, svn_repos_t *repos, svn_revnum_t *new_rev, svn_fs_txn_t *txn, apr_pool_t *pool) { svn_error_t *err, *err2; const char *txn_name; apr_hash_t *props; apr_pool_t *iterpool; apr_hash_index_t *hi; apr_hash_t *hooks_env; *new_rev = SVN_INVALID_REVNUM; /* Parse the hooks-env file (if any). */ SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, pool, pool)); /* Run pre-commit hooks. */ SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool)); /* Remove any ephemeral transaction properties. */ SVN_ERR(svn_fs_txn_proplist(&props, txn, pool)); iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { const void *key; apr_hash_this(hi, &key, NULL, NULL); svn_pool_clear(iterpool); if (strncmp(key, SVN_PROP_TXN_PREFIX, (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0) { SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool)); } } svn_pool_destroy(iterpool); /* Commit. */ err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool); if (! SVN_IS_VALID_REVNUM(*new_rev)) return err; /* Run post-commit hooks. */ if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env, *new_rev, txn_name, pool))) { err2 = svn_error_create (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2, _("Commit succeeded, but post-commit hook failed")); } return svn_error_compose_create(err, err2); }
static svn_error_t * recover_fully_packed(const svn_test_opts_t *opts, apr_pool_t *pool) { apr_pool_t *subpool; svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; svn_error_t *err; /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "fsfs") != 0) || (opts->server_minor_version && (opts->server_minor_version < 7))) return SVN_NO_ERROR; /* Create a packed FS for which every revision will live in a pack digest file, and then recover it. */ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool)); SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool)); /* Add another revision, re-pack, re-recover. */ subpool = svn_pool_create(pool); SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, subpool)); SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_test__set_file_contents(txn_root, "A/mu", "new-mu", subpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); svn_pool_destroy(subpool); SVN_ERR(svn_fs_pack(REPO_NAME, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool)); /* Now, delete the youngest revprop file, and recover again. This time we want to see an error! */ SVN_ERR(svn_io_remove_file2( svn_dirent_join_many(pool, REPO_NAME, PATH_REVPROPS_DIR, apr_psprintf(pool, "%ld/%ld", after_rev / SHARD_SIZE, after_rev), NULL), FALSE, pool)); err = svn_fs_recover(REPO_NAME, NULL, NULL, pool); if (! err) return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "Expected SVN_ERR_FS_CORRUPT error; got none"); if (err->apr_err != SVN_ERR_FS_CORRUPT) return svn_error_create(SVN_ERR_TEST_FAILED, err, "Expected SVN_ERR_FS_CORRUPT error; got:"); svn_error_clear(err); return SVN_NO_ERROR; }
static svn_error_t * get_set_revprop_packed_fs(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; svn_string_t *prop_value; apr_pool_t *subpool; /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "fsfs") != 0) || (opts->server_minor_version && (opts->server_minor_version < 7))) return SVN_NO_ERROR; /* Create the packed FS and open it. */ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool)); SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, pool)); subpool = svn_pool_create(pool); /* Do a commit to trigger packing. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_test__set_file_contents(txn_root, "iota", "new-iota", subpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); svn_pool_clear(subpool); /* Pack the repository. */ SVN_ERR(svn_fs_pack(REPO_NAME, NULL, NULL, NULL, NULL, pool)); /* Try to get revprop for revision 0. */ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR, pool)); /* Try to change revprop for revision 0. */ SVN_ERR(svn_fs_change_rev_prop(fs, 0, SVN_PROP_REVISION_AUTHOR, svn_string_create("tweaked-author", pool), pool)); return SVN_NO_ERROR; }
svn_error_t * svn_fs__editor_commit(svn_revnum_t *revision, svn_error_t **post_commit_err, const char **conflict_path, svn_editor_t *editor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct edit_baton *eb = svn_editor_get_baton(editor); const char *inner_conflict_path; svn_error_t *err = NULL; /* make sure people are using the correct sequencing. */ if (eb->completed) return svn_error_create(SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION, NULL, NULL); *revision = SVN_INVALID_REVNUM; *post_commit_err = NULL; *conflict_path = NULL; /* Clean up internal resources (eg. eb->root). This also allows the editor infrastructure to know this editor is "complete". */ err = svn_editor_complete(editor); /* Note: docco for svn_fs_commit_txn() states that CONFLICT_PATH will be allocated in the txn's pool. But it lies. Regardless, we want it placed into RESULT_POOL. */ if (!err) err = svn_fs_commit_txn(&inner_conflict_path, revision, eb->txn, scratch_pool); if (SVN_IS_VALID_REVNUM(*revision)) { if (err) { /* Case 3. ERR is a post-commit (cleanup) error. */ /* Pass responsibility via POST_COMMIT_ERR. */ *post_commit_err = err; err = SVN_NO_ERROR; } /* else: Case 1. */ } else { SVN_ERR_ASSERT(err != NULL); if (err->apr_err == SVN_ERR_FS_CONFLICT) { /* Case 2. */ /* Copy this into the correct pool (see note above). */ *conflict_path = apr_pstrdup(result_pool, inner_conflict_path); /* Return sucess. The caller should inspect CONFLICT_PATH to determine this particular case. */ svn_error_clear(err); err = SVN_NO_ERROR; } /* else: Case 4. */ /* Abort the TXN. Nobody wants to use it. */ /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ err = svn_error_compose_create( err, svn_fs_abort_txn(eb->txn, scratch_pool)); } /* For safety, clear the now-useless txn. */ eb->txn = NULL; return svn_error_trace(err); }
static svn_error_t * close_revision(void *baton) { struct revision_baton *rb = baton; struct parse_baton *pb = rb->pb; const char *conflict_msg = NULL; svn_revnum_t committed_rev; svn_error_t *err; const char *txn_name = NULL; apr_hash_t *hooks_env; /* If we're skipping this revision we're done here. */ if (rb->skipped) return SVN_NO_ERROR; if (rb->rev == 0) { /* Special case: set revision 0 properties when loading into an 'empty' filesystem. */ svn_revnum_t youngest_rev; SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool)); if (youngest_rev == 0) { apr_hash_t *orig_props; apr_hash_t *new_props; apr_array_header_t *diff; int i; SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, 0, rb->pool)); new_props = svn_prop_array_to_hash(rb->revprops, rb->pool); SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool)); for (i = 0; i < diff->nelts; i++) { const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t); SVN_ERR(change_rev_prop(pb->repos, 0, prop->name, prop->value, pb->validate_props, rb->pool)); } } return SVN_NO_ERROR; } /* If the dumpstream doesn't have an 'svn:date' property and we aren't ignoring the dates in the dumpstream altogether, remove any 'svn:date' revision property that was set by FS layer when the TXN was created. */ if (! (pb->ignore_dates || rb->datestamp)) { svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t); prop->name = SVN_PROP_REVISION_DATE; prop->value = NULL; } /* Apply revision property changes. */ if (rb->pb->validate_props) SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); else SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); /* Get the txn name and hooks environment if they will be needed. */ if (pb->use_pre_commit_hook || pb->use_post_commit_hook) { SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path, rb->pool, rb->pool)); err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool); if (err) { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); return svn_error_trace(err); } } /* Run the pre-commit hook, if so commanded. */ if (pb->use_pre_commit_hook) { err = svn_repos__hooks_pre_commit(pb->repos, hooks_env, txn_name, rb->pool); if (err) { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); return svn_error_trace(err); } } /* Commit. */ err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool); if (SVN_IS_VALID_REVNUM(committed_rev)) { if (err) { /* ### Log any error, but better yet is to rev ### close_revision()'s API to allow both committed_rev and err ### to be returned, see #3768. */ svn_error_clear(err); } } else { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); if (conflict_msg) return svn_error_quick_wrap(err, conflict_msg); else return svn_error_trace(err); } /* Run post-commit hook, if so commanded. */ if (pb->use_post_commit_hook) { if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env, committed_rev, txn_name, rb->pool))) return svn_error_create (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, _("Commit succeeded, but post-commit hook failed")); } /* After a successful commit, must record the dump-rev -> in-repos-rev mapping, so that copyfrom instructions in the dump file can look up the correct repository revision to copy from. */ set_revision_mapping(pb->rev_map, rb->rev, committed_rev); /* If the incoming dump stream has non-contiguous revisions (e.g. from using svndumpfilter --drop-empty-revs without --renumber-revs) then we must account for the missing gaps in PB->REV_MAP. Otherwise we might not be able to map all mergeinfo source revisions to the correct revisions in the target repos. */ if ((pb->last_rev_mapped != SVN_INVALID_REVNUM) && (rb->rev != pb->last_rev_mapped + 1)) { svn_revnum_t i; for (i = pb->last_rev_mapped + 1; i < rb->rev; i++) { set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped); } } /* Update our "last revision mapped". */ pb->last_rev_mapped = rb->rev; /* Deltify the predecessors of paths changed in this revision. */ SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool)); if (pb->notify_func) { /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ svn_repos_notify_t *notify = svn_repos_notify_create( svn_repos_notify_load_txn_committed, pb->notify_pool); notify->new_revision = committed_rev; notify->old_revision = ((committed_rev == rb->rev) ? SVN_INVALID_REVNUM : rb->rev); pb->notify_func(pb->notify_baton, notify, pb->notify_pool); svn_pool_clear(pb->notify_pool); } return SVN_NO_ERROR; }
static svn_error_t * close_revision(void *baton) { struct revision_baton *rb = baton; struct parse_baton *pb = rb->pb; const char *conflict_msg = NULL; svn_revnum_t committed_rev; svn_error_t *err; const char *txn_name = NULL; apr_hash_t *hooks_env; /* If we're skipping this revision or it has an invalid revision number, we're done here. */ if (rb->skipped || (rb->rev <= 0)) return SVN_NO_ERROR; /* Get the txn name and hooks environment if they will be needed. */ if (pb->use_pre_commit_hook || pb->use_post_commit_hook) { SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path, rb->pool, rb->pool)); err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool); if (err) { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); return svn_error_trace(err); } } /* Run the pre-commit hook, if so commanded. */ if (pb->use_pre_commit_hook) { err = svn_repos__hooks_pre_commit(pb->repos, hooks_env, txn_name, rb->pool); if (err) { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); return svn_error_trace(err); } } /* Commit. */ err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool); if (SVN_IS_VALID_REVNUM(committed_rev)) { if (err) { /* ### Log any error, but better yet is to rev ### close_revision()'s API to allow both committed_rev and err ### to be returned, see #3768. */ svn_error_clear(err); } } else { svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); if (conflict_msg) return svn_error_quick_wrap(err, conflict_msg); else return svn_error_trace(err); } /* Run post-commit hook, if so commanded. */ if (pb->use_post_commit_hook) { if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env, committed_rev, txn_name, rb->pool))) return svn_error_create (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, _("Commit succeeded, but post-commit hook failed")); } /* After a successful commit, must record the dump-rev -> in-repos-rev mapping, so that copyfrom instructions in the dump file can look up the correct repository revision to copy from. */ set_revision_mapping(pb->rev_map, rb->rev, committed_rev); /* If the incoming dump stream has non-contiguous revisions (e.g. from using svndumpfilter --drop-empty-revs without --renumber-revs) then we must account for the missing gaps in PB->REV_MAP. Otherwise we might not be able to map all mergeinfo source revisions to the correct revisions in the target repos. */ if ((pb->last_rev_mapped != SVN_INVALID_REVNUM) && (rb->rev != pb->last_rev_mapped + 1)) { svn_revnum_t i; for (i = pb->last_rev_mapped + 1; i < rb->rev; i++) { set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped); } } /* Update our "last revision mapped". */ pb->last_rev_mapped = rb->rev; /* Deltify the predecessors of paths changed in this revision. */ SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool)); /* Grrr, svn_fs_commit_txn rewrites the datestamp property to the current clock-time. We don't want that, we want to preserve history exactly. Good thing revision props aren't versioned! Note that if rb->datestamp is NULL, that's fine -- if the dump data doesn't carry a datestamp, we want to preserve that fact in the load. */ SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE, rb->datestamp, pb->validate_props, rb->pool)); if (pb->notify_func) { /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ svn_repos_notify_t *notify = svn_repos_notify_create( svn_repos_notify_load_txn_committed, pb->notify_pool); notify->new_revision = committed_rev; notify->old_revision = ((committed_rev == rb->rev) ? SVN_INVALID_REVNUM : rb->rev); pb->notify_func(pb->notify_baton, notify, pb->notify_pool); svn_pool_clear(pb->notify_pool); } return SVN_NO_ERROR; }
static svn_error_t * changes_fetch_ordering(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { svn_fs_t *fs; svn_revnum_t youngest_rev = 0; const char *txn_name; svn_fs_txn_t *txn; svn_fs_root_t *txn_root, *rev_root; struct changes_args args; apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi; *msg = "verify ordered-ness of fetched compressed changes"; if (msg_only) return SVN_NO_ERROR; /* Create a new fs and repos */ SVN_ERR(svn_test__create_fs (&fs, "test-repo-changes-fetch-ordering", "bdb", pool)); /*** REVISION 1: Make some files and dirs. ***/ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); { static svn_test__txn_script_command_t script_entries[] = { { 'a', "dir1", 0 }, { 'a', "file1", "This is the file 'file1'.\n" }, { 'a', "dir1/file2", "This is the file 'file2'.\n" }, { 'a', "dir1/file3", "This is the file 'file3'.\n" }, { 'a', "dir1/file4", "This is the file 'file4'.\n" }, }; SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool)); } SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); svn_pool_clear(subpool); /*** REVISION 2: Delete and add some stuff, non-depth-first. ***/ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); /* Don't use subpool, txn_name is used after subpool is cleared */ SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); { static svn_test__txn_script_command_t script_entries[] = { { 'd', "file1", "This is the file 'file1'.\n" }, { 'd', "dir1/file2", "This is the file 'file2'.\n" }, { 'd', "dir1/file3", "This is the file 'file3'.\n" }, { 'a', "dir1/file5", "This is the file 'file4'.\n" }, { 'a', "dir1/dir2", 0 }, { 'd', "dir1", 0 }, { 'a', "dir3", 0 }, }; SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 7, subpool)); } SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); svn_pool_clear(subpool); /*** TEST: We should have only three changes, the deletion of 'file1' the deletion of 'dir1', and the addition of 'dir3'. ***/ args.fs = fs; args.key = txn_name; SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args, subpool)); if ((! args.changes) || (apr_hash_count(args.changes) != 3)) return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "expected changes"); for (hi = apr_hash_first(subpool, args.changes); hi; hi = apr_hash_next(hi)) { const void *key; void *val; svn_fs_path_change_t *change; /* KEY will be the path, VAL the change. */ apr_hash_this(hi, &key, NULL, &val); change = val; if ((change->change_kind == svn_fs_path_change_add) && (strcmp(key, "/dir3") == 0)) ; else if ((change->change_kind == svn_fs_path_change_delete) && ((strcmp(key, "/dir1") == 0) || (strcmp(key, "/file1") == 0))) ; else return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "got wrong changes"); } /*** REVISION 3: Do the same stuff as in revision 1. ***/ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); { static svn_test__txn_script_command_t script_entries[] = { { 'a', "dir1", 0 }, { 'a', "file1", "This is the file 'file1'.\n" }, { 'a', "dir1/file2", "This is the file 'file2'.\n" }, { 'a', "dir1/file3", "This is the file 'file3'.\n" }, { 'a', "dir1/file4", "This is the file 'file4'.\n" }, }; SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool)); } SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); svn_pool_clear(subpool); /*** REVISION 4: Do the same stuff as in revision 2, but use a copy overwrite of the top directory (instead of a delete) to test that the 'replace' change type works, too. (And add 'dir4' instead of 'dir3', since 'dir3' still exists). ***/ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); /* Don't use subpool, txn_name is used after subpool is cleared */ SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, subpool)); { static svn_test__txn_script_command_t script_entries[] = { { 'd', "file1", "This is the file 'file1'.\n" }, { 'd', "dir1/file2", "This is the file 'file2'.\n" }, { 'd', "dir1/file3", "This is the file 'file3'.\n" }, { 'a', "dir1/file5", "This is the file 'file4'.\n" }, { 'a', "dir1/dir2", 0 }, }; SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool)); SVN_ERR(svn_fs_copy(rev_root, "dir1", txn_root, "dir1", subpool)); SVN_ERR(svn_fs_make_dir(txn_root, "dir4", subpool)); } SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); svn_pool_clear(subpool); /*** TEST: We should have only three changes, the deletion of 'file1' the replacement of 'dir1', and the addition of 'dir4'. ***/ args.fs = fs; args.key = txn_name; SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args, subpool)); if ((! args.changes) || (apr_hash_count(args.changes) != 3)) return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "expected changes"); for (hi = apr_hash_first(subpool, args.changes); hi; hi = apr_hash_next(hi)) { const void *key; void *val; svn_fs_path_change_t *change; /* KEY will be the path, VAL the change. */ apr_hash_this(hi, &key, NULL, &val); change = val; if ((change->change_kind == svn_fs_path_change_add) && (strcmp(key, "/dir4") == 0)) ; else if ((change->change_kind == svn_fs_path_change_replace) && (strcmp(key, "/dir1") == 0)) ; else if ((change->change_kind == svn_fs_path_change_delete) && (strcmp(key, "/file1") == 0)) ; else return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "got wrong changes"); } return SVN_NO_ERROR; }