📄 revs-txns.c
字号:
/* revs-txns.c : operations on revision and transactions
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <assert.h>
#include <string.h>
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_pools.h>
#include "svn_pools.h"
#include "svn_time.h"
#include "svn_fs.h"
#include "fs.h"
#include "dag.h"
#include "err.h"
#include "trail.h"
#include "tree.h"
#include "revs-txns.h"
#include "key-gen.h"
#include "id.h"
#include "bdb/rev-table.h"
#include "bdb/txn-table.h"
#include "bdb/copies-table.h"
#include "bdb/changes-table.h"
#include "../libsvn_fs/fs-loader.h"
/*** Helpers ***/
/* Set *txn_p to a transaction object allocated in TRAIL->pool for the
transaction in FS whose id is TXN_ID. If EXPECT_DEAD is set, this
transaction must be a dead one, else an error is returned. If
EXPECT_DEAD is not set, an error is thrown if the transaction is
*not* dead. */
static svn_error_t *
get_txn (transaction_t **txn_p,
svn_fs_t *fs,
const char *txn_id,
svn_boolean_t expect_dead,
trail_t *trail)
{
transaction_t *txn;
SVN_ERR (svn_fs_bdb__get_txn (&txn, fs, txn_id, trail));
if (expect_dead && (txn->kind != transaction_kind_dead))
return svn_error_createf (SVN_ERR_FS_TRANSACTION_NOT_DEAD, 0,
"Transaction is not dead: '%s'", txn_id);
if ((! expect_dead) && (txn->kind == transaction_kind_dead))
return svn_error_createf (SVN_ERR_FS_TRANSACTION_DEAD, 0,
"Transaction is dead: '%s'", txn_id);
*txn_p = txn;
return SVN_NO_ERROR;
}
/* This is only for symmetry with the get_txn() helper. */
#define put_txn svn_fs_bdb__put_txn
/*** Revisions ***/
/* Return the committed transaction record *TXN_P and its ID *TXN_ID
(as long as those parameters aren't NULL) for the revision REV in
FS as part of TRAIL. */
static svn_error_t *
get_rev_txn (transaction_t **txn_p,
const char **txn_id,
svn_fs_t *fs,
svn_revnum_t rev,
trail_t *trail)
{
revision_t *revision;
transaction_t *txn;
SVN_ERR (svn_fs_bdb__get_rev (&revision, fs, rev, trail));
if (revision->txn_id == NULL)
return svn_fs_base__err_corrupt_fs_revision (fs, rev);
SVN_ERR (get_txn (&txn, fs, revision->txn_id, FALSE, trail));
if (txn->revision != rev)
return svn_fs_base__err_corrupt_txn (fs, revision->txn_id);
if (txn_p)
*txn_p = txn;
if (txn_id)
*txn_id = revision->txn_id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__rev_get_root (const svn_fs_id_t **root_id_p,
svn_fs_t *fs,
svn_revnum_t rev,
trail_t *trail)
{
transaction_t *txn;
SVN_ERR (get_rev_txn (&txn, NULL, fs, rev, trail));
if (txn->root_id == NULL)
return svn_fs_base__err_corrupt_fs_revision (fs, rev);
*root_id_p = txn->root_id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__rev_get_txn_id (const char **txn_id_p,
svn_fs_t *fs,
svn_revnum_t rev,
trail_t *trail)
{
revision_t *revision;
SVN_ERR (svn_fs_bdb__get_rev (&revision, fs, rev, trail));
if (revision->txn_id == NULL)
return svn_fs_base__err_corrupt_fs_revision (fs, rev);
*txn_id_p = revision->txn_id;
return SVN_NO_ERROR;
}
static svn_error_t *
txn_body_youngest_rev (void *baton,
trail_t *trail)
{
return svn_fs_bdb__youngest_rev (baton, trail->fs, trail);
}
svn_error_t *
svn_fs_base__youngest_rev (svn_revnum_t *youngest_p,
svn_fs_t *fs,
apr_pool_t *pool)
{
svn_revnum_t youngest;
SVN_ERR (svn_fs_base__check_fs (fs));
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_youngest_rev, &youngest,
pool));
*youngest_p = youngest;
return SVN_NO_ERROR;
}
struct revision_proplist_args {
apr_hash_t **table_p;
svn_revnum_t rev;
};
static svn_error_t *
txn_body_revision_proplist (void *baton, trail_t *trail)
{
struct revision_proplist_args *args = baton;
transaction_t *txn;
SVN_ERR (get_rev_txn (&txn, NULL, trail->fs, args->rev, trail));
*(args->table_p) = txn->proplist;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__revision_proplist (apr_hash_t **table_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
struct revision_proplist_args args;
apr_hash_t *table;
SVN_ERR (svn_fs_base__check_fs (fs));
args.table_p = &table;
args.rev = rev;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_revision_proplist, &args,
pool));
*table_p = table ? table : apr_hash_make (pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__revision_prop (svn_string_t **value_p,
svn_fs_t *fs,
svn_revnum_t rev,
const char *propname,
apr_pool_t *pool)
{
struct revision_proplist_args args;
apr_hash_t *table;
SVN_ERR (svn_fs_base__check_fs (fs));
/* Get the proplist. */
args.table_p = &table;
args.rev = rev;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_revision_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;
}
svn_error_t *
svn_fs_base__set_rev_prop (svn_fs_t *fs,
svn_revnum_t rev,
const char *name,
const svn_string_t *value,
trail_t *trail)
{
transaction_t *txn;
const char *txn_id;
SVN_ERR (get_rev_txn (&txn, &txn_id, fs, rev, trail));
/* 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);
/* Overwrite the revision. */
return put_txn (fs, txn, txn_id, trail);
}
struct change_rev_prop_args {
svn_revnum_t rev;
const char *name;
const svn_string_t *value;
};
static svn_error_t *
txn_body_change_rev_prop (void *baton, trail_t *trail)
{
struct change_rev_prop_args *args = baton;
SVN_ERR (svn_fs_base__set_rev_prop (trail->fs, args->rev,
args->name, args->value, trail));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__change_rev_prop (svn_fs_t *fs,
svn_revnum_t rev,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct change_rev_prop_args args;
SVN_ERR (svn_fs_base__check_fs (fs));
args.rev = rev;
args.name = name;
args.value = value;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_change_rev_prop, &args, pool));
return SVN_NO_ERROR;
}
/*** Transactions ***/
svn_error_t *
svn_fs_base__txn_make_committed (svn_fs_t *fs,
const char *txn_name,
svn_revnum_t revision,
trail_t *trail)
{
transaction_t *txn;
/* Don't you dare call this with an invalid REVISION. */
assert (SVN_IS_VALID_REVNUM (revision));
/* Make sure the TXN is not committed already. */
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);
/* Convert TXN to a committed transaction. */
txn->base_id = NULL;
txn->revision = revision;
txn->kind = transaction_kind_committed;
return put_txn (fs, txn, txn_name, trail);
}
svn_error_t *
svn_fs_base__txn_get_revision (svn_revnum_t *revision,
svn_fs_t *fs,
const char *txn_name,
trail_t *trail)
{
transaction_t *txn;
SVN_ERR (get_txn (&txn, fs, txn_name, FALSE, trail));
*revision = txn->revision;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__get_txn_ids (const svn_fs_id_t **root_id_p,
const svn_fs_id_t **base_root_id_p,
svn_fs_t *fs,
const char *txn_name,
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);
*root_id_p = txn->root_id;
*base_root_id_p = txn->base_id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__set_txn_root (svn_fs_t *fs,
const char *txn_name,
const svn_fs_id_t *new_id,
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 (! svn_fs_base__id_eq (txn->root_id, new_id))
{
txn->root_id = new_id;
SVN_ERR (put_txn (fs, txn, txn_name, trail));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__set_txn_base (svn_fs_t *fs,
const char *txn_name,
const svn_fs_id_t *new_id,
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 (! svn_fs_base__id_eq (txn->base_id, new_id))
{
txn->base_id = new_id;
SVN_ERR (put_txn (fs, txn, txn_name, trail));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__add_txn_copy (svn_fs_t *fs,
const char *txn_name,
const char *copy_id,
trail_t *trail)
{
transaction_t *txn;
/* Get the transaction and ensure its mutability. */
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);
/* Allocate a new array if this transaction has no copies. */
if (! txn->copies)
txn->copies = apr_array_make (trail->pool, 1, sizeof (copy_id));
/* Add COPY_ID to the array. */
(*((const char **)(apr_array_push (txn->copies)))) = copy_id;
/* Finally, write out the transaction. */
return put_txn (fs, txn, txn_name, trail);
}
/* Generic transaction operations. */
struct txn_proplist_args {
apr_hash_t **table_p;
const char *id;
svn_revnum_t rev;
};
static svn_error_t *
txn_body_txn_proplist (void *baton, trail_t *trail)
{
transaction_t *txn;
struct txn_proplist_args *args = baton;
SVN_ERR (get_txn (&txn, trail->fs, args->id, FALSE, trail));
if (txn->kind != transaction_kind_normal)
return svn_fs_base__err_txn_not_mutable (trail->fs, args->id);
*(args->table_p) = txn->proplist;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__txn_proplist (apr_hash_t **table_p,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
struct txn_proplist_args args;
apr_hash_t *table;
svn_fs_t *fs = txn->fs;
SVN_ERR (svn_fs_base__check_fs (fs));
args.table_p = &table;
args.id = txn->id;
SVN_ERR (svn_fs_base__retry_txn (fs, txn_body_txn_proplist, &args, pool));
*table_p = table ? table : apr_hash_make (pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_base__txn_prop (svn_string_t **value_p,
svn_fs_txn_t *txn,
const char *propname,
apr_pool_t *pool)
{
struct txn_proplist_args args;
apr_hash_t *table;
svn_fs_t *fs = txn->fs;
SVN_ERR (svn_fs_base__check_fs (fs));
/* Get the proplist. */
args.table_p = &table;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -