blob: 7dc087e3857bac6d7a3c07f9da9a68c3a1d14193 [file] [log] [blame]
/* deltify.c --- deltification and undeltification of nodes.
*
* ====================================================================
* Copyright (c) 2000-2002 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 <string.h>
#include <db.h>
#include "svn_fs.h"
#include "svn_pools.h"
#include "svn_path.h"
#include "fs.h"
#include "nodes-table.h"
#include "node-rev.h"
#include "reps-strings.h"
#include "dag.h"
#include "id.h"
/* Stable nodes and deltification. */
/* In FS, change TARGET's representation to be a delta against SOURCE,
as part of TRAIL. If TARGET or SOURCE does not exist, do nothing
and return success. */
static svn_error_t *
deltify (svn_fs_id_t *target_id,
svn_fs_id_t *source_id,
svn_fs_t *fs,
int props_only,
trail_t *trail)
{
skel_t
*source_nr, /* source node revision */
*target_nr; /* target node revision */
const char
*target_pkey, /* target property rep key */
*target_dkey, /* target data rep key */
*source_pkey, /* source property rep key */
*source_dkey; /* source data rep key */
skel_t
*target_pkey_skel, /* target property rep key skel */
*target_dkey_skel, /* target data rep key skel */
*source_pkey_skel, /* source property rep key skel */
*source_dkey_skel; /* source data rep key skel */
/* Turn those IDs into skels, so we can get the rep keys. */
SVN_ERR (svn_fs__get_node_revision (&target_nr, fs, target_id, trail));
SVN_ERR (svn_fs__get_node_revision (&source_nr, fs, source_id, trail));
/* Check that target and source exist. It is not an error to
attempt to deltify something that does not exist, or deltify
against a non-existent base. However, nothing happens. */
if ((target_nr == NULL) || (source_nr == NULL))
return SVN_NO_ERROR;
/* We have a target and a source. Get all the rep keys... */
{
/* Target property key. */
target_pkey_skel = SVN_FS__NR_PROP_KEY (target_nr);
if (target_pkey_skel->len != 0) {
target_pkey = apr_pstrndup (trail->pool,
target_pkey_skel->data,
target_pkey_skel->len);
}
else
target_pkey = NULL;
/* Target data key. */
target_dkey_skel = SVN_FS__NR_DATA_KEY (target_nr);
if (target_dkey_skel->len != 0) {
target_dkey = apr_pstrndup (trail->pool,
target_dkey_skel->data,
target_dkey_skel->len);
}
else
target_dkey = NULL;
/* Source property key. */
source_pkey_skel = SVN_FS__NR_PROP_KEY (source_nr);
if (source_pkey_skel->len != 0) {
source_pkey = apr_pstrndup (trail->pool,
source_pkey_skel->data,
source_pkey_skel->len);
}
else
source_pkey = NULL;
/* Source data key. */
source_dkey_skel = SVN_FS__NR_DATA_KEY (source_nr);
if (source_dkey_skel->len != 0) {
source_dkey = apr_pstrndup (trail->pool,
source_dkey_skel->data,
source_dkey_skel->len);
}
else
source_dkey = NULL;
}
if ((target_pkey && source_pkey)
&& (strcmp (target_pkey, source_pkey)))
SVN_ERR (svn_fs__rep_deltify (fs, target_pkey, source_pkey, trail));
if ((target_dkey && source_dkey) && (! props_only)
&& (strcmp (target_dkey, source_dkey)))
SVN_ERR (svn_fs__rep_deltify (fs, target_dkey, source_dkey, trail));
return SVN_NO_ERROR;
}
/* In FS, change ID's representation to be a fulltext representation
as part of TRAIL. If ID does not exist, do nothing and return
success. */
static svn_error_t *
undeltify (svn_fs_id_t *id,
svn_fs_t *fs,
trail_t *trail)
{
skel_t *node_rev;
const char *prop_key = NULL, *data_key = NULL;
skel_t *pkey_skel, *dkey_skel;
/* Turn ID into a skel so we can get the rep keys. */
SVN_ERR (svn_fs__get_node_revision (&node_rev, fs, id, trail));
/* Check that target exists. If not, no big deal -- just do
nothing. */
if (node_rev == NULL)
return SVN_NO_ERROR;
/* Get the property key. */
pkey_skel = SVN_FS__NR_PROP_KEY (node_rev);
if (pkey_skel->len != 0)
prop_key = apr_pstrndup (trail->pool, pkey_skel->data, pkey_skel->len);
/* Get the data key. */
dkey_skel = SVN_FS__NR_DATA_KEY (node_rev);
if (dkey_skel->len != 0)
data_key = apr_pstrndup (trail->pool, dkey_skel->data, dkey_skel->len);
/* Undeltify the properties. */
if (prop_key)
SVN_ERR (svn_fs__rep_undeltify (fs, prop_key, trail));
/* Undeltify the data (entries list for directories, file contents
for files). */
if (data_key)
SVN_ERR (svn_fs__rep_undeltify (fs, data_key, trail));
return SVN_NO_ERROR;
}
/* Deltify TARGET_ID in FS against its immediately successor (also in
FS). Pass IS_DIR through to deltify(), and do all of this stuff as
part of TRAIL. */
static svn_error_t *
deltify_by_id (svn_fs_t *fs,
svn_fs_id_t *target_id,
int is_dir,
trail_t *trail)
{
svn_fs_id_t *source_id = NULL, *tmp_id;
apr_size_t len = svn_fs__id_length (target_id);
dag_node_t *node;
/* Increment TMP_ID as a regular successor of TARGET_ID, and see if
it exists in FS. */
tmp_id = svn_fs__id_copy (target_id, trail->pool);
tmp_id[len - 1]++;
if (SVN_NO_ERROR == svn_fs__dag_get_node (&node, fs, tmp_id, trail))
{
source_id = tmp_id;
}
else
{
/* If that doesn't exist, we'll branch TARGET_ID, and see if
that exists. */
apr_size_t i;
tmp_id = apr_pcalloc (trail->pool, sizeof (*tmp_id) * (len + 3));
for (i = 0; i < len; i++)
{
tmp_id[i] = target_id[i];
}
tmp_id[len] = 1;
tmp_id[len + 1] = 1;
tmp_id[len + 2] = -1;
if (SVN_NO_ERROR == svn_fs__dag_get_node (&node, fs, tmp_id, trail))
{
source_id = tmp_id;
}
}
/* If we found a valid source ID, perform the deltification step. */
if (source_id)
SVN_ERR (deltify (target_id, source_id, fs, is_dir, trail));
return SVN_NO_ERROR;
}
/* Deltify or undeltify a directory PATH under ROOT in FS (whose
node-id is ID). If DO_DELTIFY is non-zero, drive deltify_by_id(),
else drive undeltify(). Use RECURSE to determine if this function
should recursively deltify or undeltify directories. */
static svn_error_t *
deltify_undeltify (svn_fs_t *fs,
svn_fs_root_t *root,
const char *path,
svn_fs_id_t *id,
int do_deltify,
int recurse,
trail_t *trail)
{
apr_pool_t *subpool = svn_pool_create (trail->pool);
svn_stringbuf_t *full_path = svn_stringbuf_create (path, subpool);
dag_node_t *node;
int is_dir = 0;
/* Get the node from its ID, and figure out if it is a directory. */
SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
is_dir = svn_fs__dag_is_directory (node);
/* If this is a directory and we have been asked to recurse, then
call this function for each of this ID's entries. */
if (is_dir && recurse)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
SVN_ERR (svn_fs__dag_dir_entries_hash (&entries, node, trail));
for (hi = apr_hash_first (subpool, entries); hi; hi = apr_hash_next (hi))
{
const void *key;
void *val;
apr_ssize_t klen;
svn_fs_dirent_t *entry;
/* KEY will be the entry name in source, VAL the dirent */
apr_hash_this (hi, &key, &klen, &val);
entry = val;
/* Construct the full path of this entry, and recurse. */
svn_stringbuf_set (full_path, path);
svn_path_add_component_nts (full_path, entry->name);
SVN_ERR (deltify_undeltify (fs, root, full_path->data, entry->id,
do_deltify, recurse, trail));
}
}
/* Do the real work of ... */
if (do_deltify)
{
/* ... deltification. */
SVN_ERR (deltify_by_id (fs, id, is_dir, trail));
}
else
{
/* ... un-deltification. */
SVN_ERR (undeltify (id, fs, trail));
}
/* Destroy per-iteration subpool. */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
struct deltify_args {
svn_fs_t *fs;
svn_fs_root_t *root;
const char *path;
svn_fs_id_t *id;
int recursive;
};
static svn_error_t *
txn_body_deltify (void *baton, trail_t *trail)
{
struct deltify_args *args = baton;
int is_dir = 0;
dag_node_t *node;
/* Use the ID to determine if the target here is a directory. */
SVN_ERR (svn_fs__dag_get_node (&node, args->fs, args->id, trail));
is_dir = svn_fs__dag_is_directory (node);
/* Perform the deltification step. */
SVN_ERR (deltify_undeltify (args->fs, args->root, args->path, args->id,
1, args->recursive, trail));
return SVN_NO_ERROR;
}
static svn_error_t *
txn_body_undeltify (void *baton, trail_t *trail)
{
struct deltify_args *args = baton;
int is_dir = 0;
dag_node_t *node;
/* Use the ID to determine if the target here is a directory. */
SVN_ERR (svn_fs__dag_get_node (&node, args->fs, args->id, trail));
is_dir = svn_fs__dag_is_directory (node);
/* Perform the un-deltification step. */
SVN_ERR (deltify_undeltify (args->fs, args->root, args->path, args->id,
0, args->recursive, trail));
return SVN_NO_ERROR;
}
/*** Public (or semi-public) interfaces. ***/
svn_error_t *
svn_fs_deltify (svn_fs_root_t *root,
const char *path,
int recursive,
apr_pool_t *pool)
{
struct deltify_args args;
if (! svn_fs_is_revision_root (root))
return svn_error_create (SVN_ERR_FS_NOT_REVISION_ROOT, 0, NULL, pool,
"svn_fs_deltify: root is not a revision root");
args.fs = svn_fs_root_fs (root);
args.root = root;
args.recursive = recursive;
args.path = path;
/* Get the ID of the target, which is the node we're changing. */
SVN_ERR (svn_fs_node_id (&(args.id), root, path, pool));
SVN_ERR (svn_fs__retry_txn (args.fs, txn_body_deltify, &args, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_undeltify (svn_fs_root_t *root,
const char *path,
int recursive,
apr_pool_t *pool)
{
struct deltify_args args;
if (! svn_fs_is_revision_root (root))
return svn_error_create (SVN_ERR_FS_NOT_REVISION_ROOT, 0, NULL, pool,
"svn_fs_deltify: root is not a revision root");
args.fs = svn_fs_root_fs (root);
args.root = root;
args.recursive = recursive;
args.path = path;
/* Get the ID of the target, which is the node we're changing. */
SVN_ERR (svn_fs_node_id (&(args.id), root, path, pool));
SVN_ERR (svn_fs__retry_txn (args.fs, txn_body_undeltify, &args, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs__stable_node (svn_fs_t *fs,
svn_fs_id_t *id,
trail_t *trail)
{
svn_fs_id_t *predecessor_id = svn_fs__id_predecessor (id, trail->pool);
int is_dir = 0;
dag_node_t *node;
SVN_ERR (svn_fs__dag_get_node (&node, fs, id, trail));
is_dir = svn_fs__dag_is_directory (node);
if (predecessor_id != NULL)
SVN_ERR (deltify (predecessor_id, id, fs, is_dir ? 1 : 0, trail));
return SVN_NO_ERROR;
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
*/