📄 revs-txns.c
字号:
args.id = txn->id;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_txn_proplist, &args, pool));
/* And then the prop from that list (if there was a list). */
*value_p = NULL;
if (table)
*value_p = apr_hash_get (table, propname, APR_HASH_KEY_STRING);
return SVN_NO_ERROR;
}
struct change_txn_prop_args {
svn_fs_t *fs;
const char *id;
const char *name;
const svn_string_t *value;
};
svn_error_t *
svn_fs_base__set_txn_prop (svn_fs_t *fs,
const char *txn_name,
const char *name,
const svn_string_t *value,
trail_t *trail)
{
transaction_t *txn;
SVN_ERR (get_txn (&txn, fs, txn_name, FALSE, trail));
if (txn->kind != transaction_kind_normal)
return svn_fs_base__err_txn_not_mutable (fs, txn_name);
/* If there's no proplist, but we're just deleting a property, exit now. */
if ((! txn->proplist) && (! value))
return SVN_NO_ERROR;
/* Now, if there's no proplist, we know we need to make one. */
if (! txn->proplist)
txn->proplist = apr_hash_make (trail->pool);
/* Set the property. */
apr_hash_set (txn->proplist, name, APR_HASH_KEY_STRING, value);
/* Now overwrite the transaction. */
return put_txn (fs, txn, txn_name, trail);
}
static svn_error_t *
txn_body_change_txn_prop (void *baton, trail_t *trail)
{
struct change_txn_prop_args *args = baton;
return svn_fs_base__set_txn_prop (trail->fs, args->id, args->name,
args->value, trail);
}
svn_error_t *
svn_fs_base__change_txn_prop (svn_fs_txn_t *txn,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct change_txn_prop_args args;
svn_fs_t *fs = txn->fs;
SVN_ERR (svn_fs_base__check_fs (fs));
args.id = txn->id;
args.name = name;
args.value = value;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_change_txn_prop, &args, pool));
return SVN_NO_ERROR;
}
/* Creating a transaction */
txn_vtable_t txn_vtable = {
svn_fs_base__commit_txn,
svn_fs_base__abort_txn,
svn_fs_base__txn_prop,
svn_fs_base__txn_proplist,
svn_fs_base__change_txn_prop,
svn_fs_base__txn_root
};
/* Allocate and return a new transaction object in POOL for FS whose
transaction ID is ID. ID is not copied. */
static svn_fs_txn_t *
make_txn (svn_fs_t *fs,
const char *id,
svn_revnum_t base_rev,
apr_pool_t *pool)
{
svn_fs_txn_t *txn = apr_pcalloc (pool, sizeof (*txn));
txn->fs = fs;
txn->id = id;
txn->base_rev = base_rev;
txn->vtable = &txn_vtable;
txn->fsap_data = NULL;
return txn;
}
struct begin_txn_args
{
svn_fs_txn_t **txn_p;
svn_revnum_t rev;
};
static svn_error_t *
txn_body_begin_txn (void *baton,
trail_t *trail)
{
struct begin_txn_args *args = baton;
const svn_fs_id_t *root_id;
const char *txn_id;
SVN_ERR (svn_fs_base__rev_get_root (&root_id, trail->fs, args->rev, trail));
SVN_ERR (svn_fs_bdb__create_txn (&txn_id, trail->fs, root_id, trail));
*args->txn_p = make_txn (trail->fs, txn_id, args->rev, trail->pool);
return SVN_NO_ERROR;
}
/* Note: it is acceptable for this function to call back into
public FS API interfaces because it does not itself use trails. */
svn_error_t *
svn_fs_base__begin_txn (svn_fs_txn_t **txn_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_fs_txn_t *txn;
struct begin_txn_args args;
svn_string_t date;
SVN_ERR (svn_fs_base__check_fs (fs));
args.txn_p = &txn;
args.rev = rev;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_begin_txn, &args, pool));
*txn_p = txn;
/* Put a datestamp on the newly created txn, so we always know
exactly how old it is. (This will help sysadmins identify
long-abandoned txns that may need to be manually removed.) When
a txn is promoted to a revision, this property will be
automatically overwritten with a revision datestamp. */
date.data = svn_time_to_cstring (apr_time_now(), pool);
date.len = strlen (date.data);
SVN_ERR (svn_fs_base__change_txn_prop (txn, SVN_PROP_REVISION_DATE,
&date, pool));
return SVN_NO_ERROR;
}
struct open_txn_args
{
svn_fs_txn_t **txn_p;
const char *name;
};
static svn_error_t *
txn_body_open_txn (void *baton,
trail_t *trail)
{
struct open_txn_args *args = baton;
transaction_t *fstxn;
svn_revnum_t base_rev = SVN_INVALID_REVNUM;
const char *txn_id;
SVN_ERR (get_txn (&fstxn, trail->fs, args->name, FALSE, trail));
if (fstxn->kind != transaction_kind_committed)
{
txn_id = svn_fs_base__id_txn_id (fstxn->base_id);
SVN_ERR (svn_fs_base__txn_get_revision (&base_rev, trail->fs, txn_id,
trail));
}
*args->txn_p = make_txn (trail->fs, args->name, base_rev, trail->pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__open_txn (svn_fs_txn_t **txn_p,
svn_fs_t *fs,
const char *name,
apr_pool_t *pool)
{
svn_fs_txn_t *txn;
struct open_txn_args args;
SVN_ERR (svn_fs_base__check_fs (fs));
args.txn_p = &txn;
args.name = name;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_open_txn, &args, pool));
*txn_p = txn;
return SVN_NO_ERROR;
}
struct cleanup_txn_args
{
transaction_t **txn_p;
const char *name;
};
static svn_error_t *
txn_body_cleanup_txn (void *baton,
trail_t *trail)
{
struct cleanup_txn_args *args = baton;
return get_txn (args->txn_p, trail->fs, args->name, TRUE, trail);
}
static svn_error_t *
txn_body_cleanup_txn_copy (void *baton, trail_t *trail)
{
svn_error_t *err = svn_fs_bdb__delete_copy (trail->fs, baton, trail);
/* Copy doesn't exist? No sweat. */
if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_COPY))
{
svn_error_clear (err);
err = SVN_NO_ERROR;
}
return err;
}
static svn_error_t *
txn_body_cleanup_txn_changes (void *baton, trail_t *trail)
{
return svn_fs_bdb__changes_delete (trail->fs, baton, trail);
}
struct get_dirents_args
{
apr_hash_t **dirents;
const svn_fs_id_t *id;
const char *txn_id;
};
static svn_error_t *
txn_body_get_dirents (void *baton, trail_t *trail)
{
struct get_dirents_args *args = baton;
dag_node_t *node;
/* Get the node. */
SVN_ERR (svn_fs_base__dag_get_node (&node, trail->fs, args->id, trail));
/* If immutable, do nothing and return. */
if (! svn_fs_base__dag_check_mutable (node, args->txn_id))
return SVN_NO_ERROR;
/* If a directory, do nothing and return. */
*(args->dirents) = NULL;
if (svn_fs_base__dag_node_kind (node) != svn_node_dir)
return SVN_NO_ERROR;
/* Else it's mutable. Get it's dirents. */
return svn_fs_base__dag_dir_entries (args->dirents, node, trail);
}
struct remove_node_args
{
const svn_fs_id_t *id;
const char *txn_id;
};
static svn_error_t *
txn_body_remove_node (void *baton, trail_t *trail)
{
struct remove_node_args *args = baton;
return svn_fs_base__dag_remove_node (trail->fs, args->id, args->txn_id,
trail);
}
static svn_error_t *
delete_txn_tree (svn_fs_t *fs,
const svn_fs_id_t *id,
const char *txn_id,
apr_pool_t *pool)
{
struct get_dirents_args dirent_args;
struct remove_node_args rm_args;
apr_hash_t *dirents = NULL;
apr_hash_index_t *hi;
svn_error_t *err;
/* If this sucker isn't mutable, there's nothing to do. */
if (svn_fs_base__key_compare (svn_fs_base__id_txn_id (id), txn_id) != 0)
return SVN_NO_ERROR;
/* See if the thing has dirents that need to be recursed upon. If
you can't find the thing itself, don't sweat it. We probably
already cleaned it up. */
dirent_args.dirents = &dirents;
dirent_args.id = id;
dirent_args.txn_id = txn_id;
err = svn_fs_base__retry_txn (fs, txn_body_get_dirents, &dirent_args, pool);
if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND))
{
svn_error_clear (err);
return SVN_NO_ERROR;
}
SVN_ERR (err);
/* If there are dirents upon which to recurse ... recurse. */
if (dirents)
{
apr_pool_t *subpool = svn_pool_create (pool);
/* Loop over hash entries */
for (hi = apr_hash_first (pool, dirents); hi; hi = apr_hash_next (hi))
{
void *val;
svn_fs_dirent_t *dirent;
svn_pool_clear (subpool);
apr_hash_this (hi, NULL, NULL, &val);
dirent = val;
SVN_ERR (delete_txn_tree (fs, dirent->id, txn_id, subpool));
}
svn_pool_destroy (subpool);
}
/* Remove the node. */
rm_args.id = id;
rm_args.txn_id = txn_id;
return svn_fs_base__retry_txn (fs, txn_body_remove_node, &rm_args, pool);
}
static svn_error_t *
txn_body_delete_txn (void *baton, trail_t *trail)
{
return svn_fs_bdb__delete_txn (trail->fs, baton, trail);
}
svn_error_t *
svn_fs_base__purge_txn (svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
struct cleanup_txn_args args;
transaction_t *txn;
int i;
SVN_ERR (svn_fs_base__check_fs (fs));
/* Open the transaction, expecting it to be dead. */
args.txn_p = &txn;
args.name = txn_id;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_cleanup_txn, &args, pool));
/* Delete the mutable portion of the tree hanging from the
transaction (which should gracefully recover if we've already
done this). */
SVN_ERR (delete_txn_tree (fs, txn->root_id, txn_id, pool));
/* Kill the transaction's changes (which should gracefully recover
if...). */
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_cleanup_txn_changes,
(void *)txn_id, pool));
/* Kill the transaction's copies (which should gracefully...). */
if (txn->copies)
{
for (i = 0; i < txn->copies->nelts; i++)
{
SVN_ERR (svn_fs_base__retry_txn
(fs, txn_body_cleanup_txn_copy,
(void *)APR_ARRAY_IDX (txn->copies, i, const char *),
pool));
}
}
/* Kill the transaction itself (which ... just kidding -- this has
no graceful failure mode). */
return svn_fs_base__retry_txn (fs, txn_body_delete_txn, (void *)txn_id,
pool);
}
static svn_error_t *
txn_body_abort_txn (void *baton, trail_t *trail)
{
svn_fs_txn_t *txn = baton;
transaction_t *fstxn;
/* Get the transaction by its id, set it to "dead", and store the
transaction. */
SVN_ERR (get_txn (&fstxn, txn->fs, txn->id, FALSE, trail));
if (fstxn->kind != transaction_kind_normal)
return svn_fs_base__err_txn_not_mutable (txn->fs, txn->id);
fstxn->kind = transaction_kind_dead;
return put_txn (txn->fs, fstxn, txn->id, trail);
}
svn_error_t *
svn_fs_base__abort_txn (svn_fs_txn_t *txn,
apr_pool_t *pool)
{
SVN_ERR (svn_fs_base__check_fs (txn->fs));
/* Set the transaction to "dead". */
SVN_ERR (svn_fs_base__retry_txn (txn->fs, txn_body_abort_txn, txn, pool));
/* Now, purge it. */
SVN_ERR_W (svn_fs_base__purge_txn (txn->fs, txn->id, pool),
"Transaction aborted, but cleanup failed");
return SVN_NO_ERROR;
}
struct list_transactions_args
{
apr_array_header_t **names_p;
apr_pool_t *pool;
};
static svn_error_t *
txn_body_list_transactions (void* baton,
trail_t *trail)
{
struct list_transactions_args *args = baton;
SVN_ERR (svn_fs_bdb__get_txn_list (args->names_p, trail->fs,
args->pool, trail));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__list_transactions (apr_array_header_t **names_p,
svn_fs_t *fs,
apr_pool_t *pool)
{
apr_array_header_t *names;
struct list_transactions_args args;
SVN_ERR (svn_fs_base__check_fs (fs));
args.names_p = &names;
args.pool = pool;
SVN_ERR (svn_fs_base__retry (fs, txn_body_list_transactions, &args, pool));
*names_p = names;
return SVN_NO_ERROR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -