int main(int argc, const char *argv[]) { apr_pool_t *pool; int exit_code = EXIT_SUCCESS; svn_error_t *err; const char *url; svn_revnum_t revision; const char *propname; svn_string_t *propval; svn_string_t *old_propval; char *digits_end = NULL; svn_boolean_t want_error; const char *config_dir; if (argc != 7) { fprintf(stderr, USAGE_MSG, argv[0], KEY_OLD_PROPVAL, KEY_NEW_PROPVAL); exit(1); } if (apr_initialize() != APR_SUCCESS) { fprintf(stderr, "apr_initialize() failed.\n"); exit(1); } /* set up the global pool */ pool = svn_pool_create(NULL); /* Parse argv. */ url = svn_uri_canonicalize(argv[1], pool); revision = strtol(argv[2], &digits_end, 10); propname = argv[3]; SVN_INT_ERR(extract_values_from_skel(&old_propval, &propval, argv[4], pool)); want_error = !strcmp(argv[5], "1"); config_dir = svn_dirent_canonicalize(argv[6], pool); if ((! SVN_IS_VALID_REVNUM(revision)) || (! digits_end) || *digits_end) SVN_INT_ERR(svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Invalid revision number supplied"))); /* Do something. */ err = change_rev_prop(url, revision, propname, propval, old_propval, want_error, config_dir, pool); if (err) { svn_handle_error2(err, stderr, FALSE, "atomic-ra-revprop-change: "); svn_error_clear(err); exit_code = EXIT_FAILURE; } /* Clean up, and get outta here */ svn_pool_destroy(pool); apr_terminate(); return exit_code; }
static svn_error_t * set_revision_property(void *baton, const char *name, const svn_string_t *value) { struct revision_baton *rb = baton; /* If we're skipping this revision, we're done here. */ if (rb->skipped) return SVN_NO_ERROR; if (rb->rev > 0) { if (rb->pb->validate_props) SVN_ERR(svn_repos_fs_change_txn_prop(rb->txn, name, value, rb->pool)); else SVN_ERR(svn_fs_change_txn_prop(rb->txn, name, value, rb->pool)); /* Remember any datestamp that passes through! (See comment in close_revision() below.) */ if (! strcmp(name, SVN_PROP_REVISION_DATE)) rb->datestamp = svn_string_dup(value, rb->pool); } else if (rb->rev == 0) { /* Special case: set revision 0 properties when loading into an 'empty' filesystem. */ struct parse_baton *pb = rb->pb; svn_revnum_t youngest_rev; SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool)); if (youngest_rev == 0) SVN_ERR(change_rev_prop(pb->repos, 0, name, value, pb->validate_props, rb->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 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; }