66
70
#include "../libsvn_fs/fs-loader.h"
69
/* ### I believe this constant will become internal to reps-strings.c.
70
### see the comment in window_consumer() for more information. */
72
/* ### the comment also seems to need tweaking: the log file stuff
73
### is no longer an issue... */
74
/* Data written to the filesystem through the svn_fs_apply_textdelta()
75
interface is cached in memory until the end of the data stream, or
76
until a size trigger is hit. Define that trigger here (in bytes).
77
Setting the value to 0 will result in no filesystem buffering at
78
all. The value only really matters when dealing with file contents
79
bigger than the value itself. Above that point, large values here
80
allow the filesystem to buffer more data in memory before flushing
81
to the database, which increases memory usage but greatly decreases
82
the amount of disk access (and log-file generation) in database.
83
Smaller values will limit your overall memory consumption, but can
84
drastically hurt throughput by necessitating more write operations
85
to the database (which also generates more log-files). */
86
#define WRITE_BUFFER_SIZE 512000
90
74
/* The root structures.
100
84
kept in the FS object and shared among multiple revision root
103
typedef struct fs_rev_root_data_t
105
/* A dag node for the revision's root directory. */
106
dag_node_t *root_dir;
108
/* Cache structure for mapping const char * PATH to const char
109
*COPYFROM_STRING, so that paths_changed can remember all the
110
copyfrom information in the changes file.
111
COPYFROM_STRING has the format "REV PATH", or is the empty string if
112
the path was added without history. */
113
apr_hash_t *copyfrom_cache;
115
} fs_rev_root_data_t;
87
typedef dag_node_t fs_rev_root_data_t;
117
89
typedef struct fs_txn_root_data_t
91
/* TXN_ID value from the main struct but as a struct instead of a string */
92
svn_fs_fs__id_part_t txn_id;
121
94
/* Cache of txn DAG nodes (without their nested noderevs, because
122
95
* it's mutable). Same keys/values as ffd->rev_node_cache. */
196
177
/* Property lookups etc. have a very high locality (75% re-hit).
197
178
Thus, remember the last hit location for optimistic lookup. */
198
179
apr_size_t last_hit;
181
/* Position of the last bucket hit that actually had a DAG node in it.
182
LAST_HIT may refer to a bucket that matches path@rev but has not
183
its NODE element set, yet.
184
This value is a mere hint for optimistic lookup and any value is
185
valid (as long as it is < BUCKET_COUNT). */
186
apr_size_t last_non_empty;
201
189
fs_fs_dag_cache_t*
245
233
&& (result->path_len == path_len)
246
234
&& !memcmp(result->path, path, path_len))
236
/* Remember the position of the last node we found in this cache. */
238
cache->last_non_empty = cache->last_hit;
251
243
/* need to do a full lookup. Calculate the hash value
252
(HASH_VALUE has been initialized to REVISION). */
244
(HASH_VALUE has been initialized to REVISION).
246
Note that the actual hash function is arbitrary as long as its result
247
in HASH_VALUE only depends on REVISION and *PATH. However, we try to
248
make as much of *PATH influence the result as possible to get an "even"
249
spread across the hash buckets (maximizes our cache retention rate and
252
When chunked access is possible (independent of the PATH pointer's
253
value!), we read 4 bytes at once and multiply the hash value with a
254
FACTOR that mirror / pattern / shift all 4 input bytes to various bits
255
of the result. The final result will be taken from the MSBs.
257
When chunked access is not possible (not supported by CPU or odd bytes
258
at the end of *PATH), we use the simple traditional "* 33" hash
259
function that works very well with texts / paths and that e.g. APR uses.
261
Please note that the bytewise and the chunked calculation are *NOT*
262
interchangeable as they will yield different results for the same input.
263
For any given machine and *PATH, we must use a fixed combination of the
254
267
#if SVN_UNALIGNED_ACCESS_IS_OK
255
268
/* We relax the dependency chain between iterations by processing
296
309
cache->insertions++;
311
else if (result->node)
313
/* This bucket is valid & has a suitable DAG node in it.
314
Remember its location. */
315
cache->last_non_empty = bucket_index;
321
/* Optimistic lookup using the last seen non-empty location in CACHE.
322
Return the node of that entry, if it is still in use and matches PATH.
323
Return NULL otherwise. Since the caller usually already knows the path
324
length, provide it in PATH_LEN. */
326
cache_lookup_last_path(fs_fs_dag_cache_t *cache,
330
cache_entry_t *result = &cache->buckets[cache->last_non_empty];
331
assert(strlen(path) == path_len);
334
&& (result->path_len == path_len)
335
&& !memcmp(result->path, path, path_len))
302
343
/* 2nd level cache */
304
345
/* Find and return the DAG node cache for ROOT and the key that
305
should be used for PATH. */
346
should be used for PATH.
348
Pool will only be used for allocating a new keys if necessary */
307
350
locate_cache(svn_cache__t **cache,
308
351
const char **key,
313
356
if (root->is_txn_root)
315
358
fs_txn_root_data_t *frd = root->fsap_data;
316
if (cache) *cache = frd->txn_node_cache;
317
if (key && path) *key = path;
361
*cache = frd->txn_node_cache;
321
367
fs_fs_data_t *ffd = root->fs->fsap_data;
322
if (cache) *cache = ffd->rev_node_cache;
323
if (key && path) *key
324
= svn_fs_fs__combine_number_and_string(root->rev, path, pool);
370
*cache = ffd->rev_node_cache;
372
*key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
328
/* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
329
isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
376
/* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL
377
if the node isn't cached. *NODE_P is allocated in POOL. */
330
378
static svn_error_t *
331
379
dag_node_cache_get(dag_node_t **node_p,
332
380
svn_fs_root_t *root,
404
452
SVN_ERR_ASSERT(*path == '/');
406
/* Do *not* attempt to dup and put the node into L1.
407
* dup() is twice as expensive as an L2 lookup (which will set also L1).
409
454
locate_cache(&cache, &key, root, path, pool);
411
455
return svn_cache__set(cache, key, node, pool);
415
/* Baton for find_descendents_in_cache. */
459
/* Baton for find_descendants_in_cache. */
416
460
struct fdic_baton {
417
461
const char *path;
418
462
apr_array_header_t *list;
419
463
apr_pool_t *pool;
422
/* If the given item is a descendent of BATON->PATH, push
466
/* If the given item is a descendant of BATON->PATH, push
423
467
* it onto BATON->LIST (copying into BATON->POOL). Implements
424
468
* the svn_iter_apr_hash_cb_t prototype. */
425
469
static svn_error_t *
426
find_descendents_in_cache(void *baton,
470
find_descendants_in_cache(void *baton,
428
472
apr_ssize_t klen,
458
502
locate_cache(&cache, NULL, root, NULL, b.pool);
461
SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
505
SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
464
508
iterpool = svn_pool_create(b.pool);
466
510
for (i = 0; i < b.list->nelts; i++)
468
const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
512
const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
469
513
svn_pool_clear(iterpool);
470
SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
514
SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
473
517
svn_pool_destroy(iterpool);
534
588
if (root->is_txn_root)
536
590
/* It's a transaction root. Open a fresh copy. */
537
return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
591
return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
541
596
/* It's a revision root, so we already have its root directory
543
fs_rev_root_data_t *frd = root->fsap_data;
544
*node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
598
dag_node_t *root_dir = root->fsap_data;
599
*node_p = svn_fs_fs__dag_dup(root_dir, pool);
545
600
return SVN_NO_ERROR;
557
612
apr_pool_t *pool)
559
614
if (root->is_txn_root)
560
return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
616
/* It's a transaction root. Open a fresh copy. */
617
return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
562
621
/* If it's not a transaction root, we can't change its contents. */
563
622
return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
645
704
the inheritance method is copy_id_inherit_new, also return a
646
705
*COPY_SRC_PATH on which to base the new copy ID (else return NULL
647
706
for that path). CHILD must have a parent (it cannot be the root
648
node). TXN_ID is the transaction in which these items might be
649
mutable. Allocations are taken from POOL. */
707
node). Allocations are taken from POOL. */
650
708
static svn_error_t *
651
709
get_copy_inheritance(copy_id_inherit_t *inherit_p,
652
710
const char **copy_src_path,
654
712
parent_path_t *child,
656
713
apr_pool_t *pool)
658
715
const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
659
const char *child_copy_id, *parent_copy_id;
716
const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
660
717
const char *id_path = NULL;
661
718
svn_fs_root_t *copyroot_root;
662
719
dag_node_t *copyroot_node;
663
720
svn_revnum_t copyroot_rev;
664
721
const char *copyroot_path;
666
SVN_ERR_ASSERT(child && child->parent && txn_id);
723
SVN_ERR_ASSERT(child && child->parent);
668
725
/* Initialize some convenience variables. */
669
726
child_id = svn_fs_fs__dag_get_id(child->node);
687
744
/* Special case: if the child's copy ID is '0', use the parent's
689
if (strcmp(child_copy_id, "0") == 0)
746
if (svn_fs_fs__id_part_is_root(child_copy_id))
690
747
return SVN_NO_ERROR;
692
749
/* Compare the copy IDs of the child and its parent. If they are
693
750
the same, then the child is already on the same branch as the
694
751
parent, and should use the same mutability copy ID that the
695
752
parent will use. */
696
if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
753
if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
697
754
return SVN_NO_ERROR;
699
756
/* If the child is on the same branch that the parent is on, the
764
821
/* The caller does not care about the parent node chain but only
765
822
the final DAG node. */
766
open_path_node_only = 4
823
open_path_node_only = 4,
825
/* The caller wants a NULL path object instead of an error if the
826
path cannot be found. */
827
open_path_allow_null = 8
767
828
} open_path_flags_t;
830
/* Try a short-cut for the open_path() function using the last node accessed.
831
* If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
832
* its "created path", return the node in *NODE_P. Set it to NULL otherwise.
834
* This function is used to support ra_serf-style access patterns where we
835
* are first asked for path@rev and then for path@c_rev of the same node.
836
* The shortcut works by ignoring the "rev" part of the cache key and then
837
* checking whether we got lucky. Lookup and verification are both quick
838
* plus there are many early outs for common types of mismatch.
841
try_match_last_node(dag_node_t **node_p,
845
apr_pool_t *scratch_pool)
847
fs_fs_data_t *ffd = root->fs->fsap_data;
849
/* Optimistic lookup: if the last node returned from the cache applied to
850
the same PATH, return it in NODE. */
852
= cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
854
/* Did we get a bucket with a committed node? */
855
if (node && !svn_fs_fs__dag_check_mutable(node))
857
/* Get the path&rev pair at which this node was created.
858
This is repository location for which this node is _known_ to be
859
the right lookup result irrespective of how we found it. */
860
const char *created_path
861
= svn_fs_fs__dag_get_created_path(node);
862
svn_revnum_t revision;
863
SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
865
/* Is it an exact match? */
866
if (revision == root->rev && strcmp(created_path, path) == 0)
868
/* Cache it under its full path@rev access path. */
869
SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
770
881
/* Open the node identified by PATH in ROOT, allocating in POOL. Set
771
882
*PARENT_PATH_P to a path from the node up to ROOT. The resulting
773
884
*element, for the root directory. PATH must be in canonical form.
775
886
If resulting *PARENT_PATH_P will eventually be made mutable and
776
modified, or if copy ID inheritance information is otherwise
777
needed, TXN_ID should be the ID of the mutability transaction. If
778
TXN_ID is NULL, no copy ID inheritance information will be
779
calculated for the *PARENT_PATH_P chain.
887
modified, or if copy ID inheritance information is otherwise needed,
888
IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, no copy ID
889
inheritance information will be calculated for the *PARENT_PATH_P chain.
781
891
If FLAGS & open_path_last_optional is zero, return the error
782
892
SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
800
910
svn_fs_root_t *root,
801
911
const char *path,
913
svn_boolean_t is_txn_path,
804
914
apr_pool_t *pool)
806
916
svn_fs_t *fs = root->fs;
807
917
dag_node_t *here = NULL; /* The directory we're currently looking at. */
808
918
parent_path_t *parent_path; /* The path from HERE up to the root. */
809
const char *rest; /* The portion of PATH we haven't traversed yet. */
811
/* ensure a canonical path representation */
812
const char *path_so_far = "/";
919
const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
813
920
apr_pool_t *iterpool = svn_pool_create(pool);
815
/* callers often traverse the tree in some path-based order. That means
816
a sibling of PATH has been presently accessed. Try to start the lookup
817
directly at the parent node, if the caller did not requested the full
819
const char *directory;
922
/* path to the currently processed entry without trailing '/'.
923
We will reuse this across iterations by simply putting a NUL terminator
924
at the respective position and replacing that with a '/' in the next
925
iteration. This is correct as we assert() PATH to be canonical. */
926
svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
927
apr_size_t path_len = path_so_far->len;
929
/* Callers often traverse the DAG in some path-based order or along the
930
history segments. That allows us to try a few guesses about where to
931
find the next item. This is only useful if the caller didn't request
932
the full parent chain. */
820
933
assert(svn_fs__is_canonical_abspath(path));
934
path_so_far->len = 0; /* "" */
821
935
if (flags & open_path_node_only)
937
const char *directory;
939
/* First attempt: Assume that we access the DAG for the same path as
940
in the last lookup but for a different revision that happens to be
941
the last revision that touched the respective node. This is a
942
common pattern when e.g. checking out over ra_serf. Note that this
943
will only work for committed data as the revision info for nodes in
946
This shortcut is quick and will exit this function upon success.
948
if (!root->is_txn_root)
951
SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
953
/* Did the shortcut work? */
956
/* Construct and return the result. */
957
svn_pool_destroy(iterpool);
959
parent_path = make_parent_path(node, 0, 0, pool);
960
parent_path->copy_inherit = copy_id_inherit_self;
961
*parent_path_p = parent_path;
967
/* Second attempt: Try starting the lookup immediately at the parent
968
node. We will often have recently accessed either a sibling or
969
said parent DIRECTORY itself for the same revision. */
823
970
directory = svn_dirent_dirname(path, pool);
824
971
if (directory[1] != 0) /* root nodes are covered anyway */
825
SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
973
SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
975
/* Did the shortcut work? */
978
apr_size_t dirname_len = strlen(directory);
979
path_so_far->len = dirname_len;
980
rest = path + dirname_len + 1;
828
985
/* did the shortcut work? */
831
path_so_far = directory;
832
rest = path + strlen(directory) + 1;
836
988
/* Make a parent_path item for the root node, using its own current
858
1011
/* Parse out the next entry from the path. */
859
1012
entry = svn_fs__next_entry_name(&next, rest, pool);
861
/* Calculate the path traversed thus far. */
862
path_so_far = svn_fspath__join(path_so_far, entry, pool);
1014
/* Update the path traversed thus far. */
1015
path_so_far->data[path_so_far->len] = '/';
1016
path_so_far->len += strlen(entry) + 1;
1017
path_so_far->data[path_so_far->len] = '\0';
864
1019
if (*entry == '\0')
882
1036
element if we already know the lookup to fail for the
883
1037
complete path. */
884
1038
if (next || !(flags & open_path_uncached))
885
SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
1039
SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
887
1041
if (cached_node)
888
1042
child = cached_node;
890
err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
1044
SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
892
1046
/* "file not found" requires special handling. */
893
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
895
1049
/* If this was the last path component, and the caller
896
1050
said it was optional, then don't return an error;
897
1051
just put a NULL node pointer in the path. */
899
svn_error_clear(err);
901
1053
if ((flags & open_path_last_optional)
902
1054
&& (! next || *next == '\0'))
916
/* Other errors we return normally. */
919
1073
if (flags & open_path_node_only)
921
/* Shortcut: the caller only wan'ts the final DAG node. */
1075
/* Shortcut: the caller only wants the final DAG node. */
922
1076
parent_path->node = child;
926
1080
/* Now, make a parent_path item for CHILD. */
927
1081
parent_path = make_parent_path(child, entry, parent_path, pool);
930
1084
SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
931
parent_path, txn_id, iterpool));
1085
parent_path, iterpool));
932
1086
parent_path->copy_inherit = inherit;
933
1087
parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
1025
1181
child_id = svn_fs_fs__dag_get_id(parent_path->node);
1026
1182
copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1027
if (strcmp(svn_fs_fs__id_node_id(child_id),
1028
svn_fs_fs__id_node_id(copyroot_id)) != 0)
1183
if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
1184
svn_fs_fs__id_node_id(copyroot_id)))
1029
1185
is_parent_copyroot = TRUE;
1031
1187
/* Now make this node mutable. */
1077
/* Canonicalize the input PATH. */
1078
if (! svn_fs__is_canonical_abspath(path))
1080
path = svn_fs__canonicalize_abspath(path, pool);
1082
/* Try again with the corrected path. */
1083
SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1233
/* Canonicalize the input PATH. As it turns out, >95% of all paths
1234
* seen here during e.g. svnadmin verify are non-canonical, i.e.
1235
* miss the leading '/'. Unconditional canonicalization has a net
1236
* performance benefit over previously checking path for being
1238
path = svn_fs__canonicalize_abspath(path, pool);
1239
SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1107
1262
/* Add a change to the changes table in FS, keyed on transaction id
1108
1263
TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1109
1264
PATH (whose node revision id is--or was, in the case of a
1110
deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1111
occurred. If the change resulted from a copy, COPYFROM_REV and
1112
COPYFROM_PATH specify under which revision and path the node was
1113
copied from. If this was not part of a copy, COPYFROM_REV should
1114
be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1265
deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or
1266
MERGEINFO_MODs occurred. If the change resulted from a copy,
1267
COPYFROM_REV and COPYFROM_PATH specify under which revision and path
1268
the node was copied from. If this was not part of a copy, COPYFROM_REV
1269
should be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1115
1270
static svn_error_t *
1116
1271
add_change(svn_fs_t *fs,
1272
const svn_fs_fs__id_part_t *txn_id,
1118
1273
const char *path,
1119
1274
const svn_fs_id_t *noderev_id,
1120
1275
svn_fs_path_change_kind_t change_kind,
1121
1276
svn_boolean_t text_mod,
1122
1277
svn_boolean_t prop_mod,
1278
svn_boolean_t mergeinfo_mod,
1123
1279
svn_node_kind_t node_kind,
1124
1280
svn_revnum_t copyfrom_rev,
1125
1281
const char *copyfrom_path,
1128
1284
return svn_fs_fs__add_change(fs, txn_id,
1129
1285
svn_fs__canonicalize_abspath(path, pool),
1130
noderev_id, change_kind, text_mod, prop_mod,
1286
noderev_id, change_kind,
1287
text_mod, prop_mod, mergeinfo_mod,
1131
1288
node_kind, copyfrom_rev, copyfrom_path,
1151
1308
The root directory ("" or "/") node is stored in the
1152
1309
svn_fs_root_t object, and never changes when it's a revision
1153
1310
root, so we can just reach in and grab it directly. */
1154
fs_rev_root_data_t *frd = root->fsap_data;
1155
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
1311
dag_node_t *root_dir = root->fsap_data;
1312
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
1164
1321
return SVN_NO_ERROR;
1324
static svn_error_t *
1325
fs_node_relation(svn_fs_node_relation_t *relation,
1326
svn_fs_root_t *root_a, const char *path_a,
1327
svn_fs_root_t *root_b, const char *path_b,
1331
const svn_fs_id_t *id_a, *id_b;
1332
svn_fs_fs__id_part_t node_id_a, node_id_b;
1334
/* Root paths are a common special case. */
1335
svn_boolean_t a_is_root_dir
1336
= (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
1337
svn_boolean_t b_is_root_dir
1338
= (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
1340
/* Another useful thing to know: Both are txns but not the same txn. */
1341
svn_boolean_t different_txn
1342
= root_a->is_txn_root && root_b->is_txn_root
1343
&& strcmp(root_a->txn, root_b->txn);
1345
/* Path from different repository are always unrelated. */
1346
if (root_a->fs != root_b->fs)
1348
*relation = svn_fs_node_unrelated;
1349
return SVN_NO_ERROR;
1352
/* Are both (!) root paths? Then, they are related and we only test how
1353
* direct the relation is. */
1354
if (a_is_root_dir && b_is_root_dir)
1356
/* For txn roots, root->REV is the base revision of that TXN. */
1357
*relation = ( (root_a->rev == root_b->rev)
1358
&& (root_a->is_txn_root == root_b->is_txn_root)
1360
? svn_fs_node_unchanged
1361
: svn_fs_node_common_ancestor;
1362
return SVN_NO_ERROR;
1365
/* We checked for all separations between ID spaces (repos, txn).
1366
* Now, we can simply test for the ID values themselves. */
1367
SVN_ERR(get_dag(&node, root_a, path_a, pool));
1368
id_a = svn_fs_fs__dag_get_id(node);
1369
node_id_a = *svn_fs_fs__id_node_id(id_a);
1371
SVN_ERR(get_dag(&node, root_b, path_b, pool));
1372
id_b = svn_fs_fs__dag_get_id(node);
1373
node_id_b = *svn_fs_fs__id_node_id(id_b);
1375
/* Noderevs from different nodes are unrelated. */
1376
if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
1378
*relation = svn_fs_node_unrelated;
1379
return SVN_NO_ERROR;
1382
/* Noderevs have the same node-ID now. So, they *seem* to be related.
1384
* Special case: Different txns may create the same (txn-local) node ID.
1385
* Only when they are committed can they actually be related to others. */
1386
if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
1388
*relation = svn_fs_node_unrelated;
1389
return SVN_NO_ERROR;
1392
/* The noderevs are actually related. Are they the same? */
1393
if (svn_fs_fs__id_eq(id_a, id_b))
1394
*relation = svn_fs_node_unchanged;
1396
*relation = svn_fs_node_common_ancestor;
1398
return SVN_NO_ERROR;
1169
1402
svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1311
1557
parent_path_t *parent_path;
1312
1558
apr_hash_t *proplist;
1559
const svn_fs_fs__id_part_t *txn_id;
1560
svn_boolean_t mergeinfo_mod = FALSE;
1315
1562
if (! root->is_txn_root)
1316
1563
return SVN_FS__NOT_TXN(root);
1564
txn_id = root_txn_id(root);
1319
1566
path = svn_fs__canonicalize_abspath(path, pool);
1320
SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1567
SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
1322
1569
/* Check (non-recursively) to see if path is locked; if so, check
1323
1570
that we can use it. */
1366
1615
/* Make a record of this modification in the changes table. */
1367
1616
return add_change(root->fs, txn_id, path,
1368
1617
svn_fs_fs__dag_get_id(parent_path->node),
1369
svn_fs_path_change_modify, FALSE, TRUE,
1618
svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
1370
1619
svn_fs_fs__dag_node_kind(parent_path->node),
1371
1620
SVN_INVALID_REVNUM, NULL, pool);
1423
1673
_("Conflict at '%s'"), path);
1676
/* Compare the directory representations at nodes LHS and RHS and set
1677
* *CHANGED to TRUE, if at least one entry has been added or removed them.
1678
* Use POOL for temporary allocations.
1680
static svn_error_t *
1681
compare_dir_structure(svn_boolean_t *changed,
1686
apr_array_header_t *lhs_entries;
1687
apr_array_header_t *rhs_entries;
1690
SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
1691
SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
1693
/* different number of entries -> some addition / removal */
1694
if (lhs_entries->nelts != rhs_entries->nelts)
1697
return SVN_NO_ERROR;
1700
/* Since directories are sorted by name, we can simply compare their
1701
entries one-by-one without binary lookup etc. */
1702
for (i = 0; i < lhs_entries->nelts; ++i)
1704
svn_fs_dirent_t *lhs_entry
1705
= APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
1706
svn_fs_dirent_t *rhs_entry
1707
= APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
1709
if (strcmp(lhs_entry->name, rhs_entry->name)
1710
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
1711
svn_fs_fs__id_node_id(rhs_entry->id))
1712
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
1713
svn_fs_fs__id_copy_id(rhs_entry->id)))
1716
return SVN_NO_ERROR;
1721
return SVN_NO_ERROR;
1427
1724
/* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1428
1725
* and TARGET must be distinct node revisions. TARGET_PATH should
1452
1749
dag_node_t *target,
1453
1750
dag_node_t *source,
1454
1751
dag_node_t *ancestor,
1752
const svn_fs_fs__id_part_t *txn_id,
1456
1753
apr_int64_t *mergeinfo_increment_out,
1457
1754
apr_pool_t *pool)
1459
1756
const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1460
apr_hash_t *s_entries, *t_entries, *a_entries;
1461
apr_hash_index_t *hi;
1757
apr_array_header_t *s_entries, *t_entries, *a_entries;
1758
int i, s_idx = -1, t_idx = -1;
1463
1760
apr_pool_t *iterpool;
1464
1761
apr_int64_t mergeinfo_increment = 0;
1587
1884
node_revision_t *tgt_nr, *anc_nr, *src_nr;
1886
apr_pool_t *scratch_pool;
1589
1888
/* Get node revisions for our id's. */
1590
SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1591
SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1592
SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
1889
scratch_pool = svn_pool_create(pool);
1890
SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
1892
svn_pool_clear(scratch_pool);
1893
SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
1895
svn_pool_clear(scratch_pool);
1896
SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
1898
svn_pool_destroy(scratch_pool);
1594
1900
/* Now compare the prop-keys of the skels. Note that just because
1595
1901
the keys are different -doesn't- mean the proplists have
1596
different contents. But merge() isn't concerned with contents;
1597
it doesn't do a brute-force comparison on textual contents, so
1598
it won't do that here either. Checking to see if the propkey
1599
atoms are `equal' is enough. */
1600
if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1601
return conflict_err(conflict_p, target_path);
1602
if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1603
return conflict_err(conflict_p, target_path);
1902
different contents. */
1903
SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, TRUE, pool));
1905
return conflict_err(conflict_p, target_path);
1907
/* The directory entries got changed in the repository but the directory
1908
properties did not. */
1909
SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, TRUE, pool));
1912
/* There is an incoming prop change for this directory.
1913
We will accept it only if the directory changes were mere updates
1914
to its entries, i.e. there were no additions or removals.
1915
Those could cause update problems to the working copy. */
1916
svn_boolean_t changed;
1917
SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
1920
return conflict_err(conflict_p, target_path);
1606
1924
/* ### todo: it would be more efficient to simply check for a NULL
1615
1933
/* for each entry E in a_entries... */
1616
1934
iterpool = svn_pool_create(pool);
1617
for (hi = apr_hash_first(pool, a_entries);
1619
hi = apr_hash_next(hi))
1935
for (i = 0; i < a_entries->nelts; ++i)
1621
1937
svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1625
1938
svn_pool_clear(iterpool);
1627
name = svn__apr_hash_index_key(hi);
1628
klen = svn__apr_hash_index_klen(hi);
1629
a_entry = svn__apr_hash_index_val(hi);
1631
s_entry = apr_hash_get(s_entries, name, klen);
1632
t_entry = apr_hash_get(t_entries, name, klen);
1940
a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
1941
s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
1942
t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
1634
1944
/* No changes were made to this entry while the transaction was
1635
1945
in progress, so do nothing to the target. */
1636
1946
if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1639
1949
/* A change was made to this entry while the transaction was in
1640
1950
process, but the transaction did not touch this entry. */
1665
1975
mergeinfo_increment += mergeinfo_end;
1668
SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1978
SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
1676
SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1986
SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
1707
2018
/* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1708
2019
modification of ANCESTOR-ENTRY, declare a conflict. */
1709
if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1710
svn_fs_fs__id_node_id(a_entry->id)) != 0
1711
|| strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1712
svn_fs_fs__id_copy_id(a_entry->id)) != 0
1713
|| strcmp(svn_fs_fs__id_node_id(t_entry->id),
1714
svn_fs_fs__id_node_id(a_entry->id)) != 0
1715
|| strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1716
svn_fs_fs__id_copy_id(a_entry->id)) != 0)
2020
if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
2021
svn_fs_fs__id_node_id(a_entry->id))
2022
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
2023
svn_fs_fs__id_copy_id(a_entry->id))
2024
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
2025
svn_fs_fs__id_node_id(a_entry->id))
2026
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
2027
svn_fs_fs__id_copy_id(a_entry->id)))
1717
2028
return conflict_err(conflict_p,
1718
2029
svn_fspath__join(target_path,
1737
2048
if (fs_supports_mergeinfo)
1738
2049
mergeinfo_increment += sub_mergeinfo_increment;
1741
/* We've taken care of any possible implications E could have.
1742
Remove it from source_entries, so it's easy later to loop
1743
over all the source entries that didn't exist in
1744
ancestor_entries. */
1746
apr_hash_set(s_entries, name, klen, NULL);
1749
2053
/* For each entry E in source but not in ancestor */
1750
for (hi = apr_hash_first(pool, s_entries);
1752
hi = apr_hash_next(hi))
2054
for (i = 0; i < s_entries->nelts; ++i)
1754
svn_fs_dirent_t *s_entry, *t_entry;
1755
const char *name = svn__apr_hash_index_key(hi);
1756
apr_ssize_t klen = svn__apr_hash_index_klen(hi);
2056
svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
1757
2057
dag_node_t *s_ent_node;
1759
2059
svn_pool_clear(iterpool);
1761
s_entry = svn__apr_hash_index_val(hi);
1762
t_entry = apr_hash_get(t_entries, name, klen);
2061
s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
2062
a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
2063
t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
2065
/* Process only entries in source that are NOT in ancestor. */
1764
2069
/* If NAME exists in TARGET, declare a conflict. */
2068
2382
apr_pool_t *pool)
2070
2384
dag_node_t *node;
2385
apr_hash_t *hash = svn_hash__make(pool);
2386
apr_array_header_t *table;
2072
2389
/* Get the entries for this path in the caller's pool. */
2073
2390
SVN_ERR(get_dag(&node, root, path, pool));
2074
return svn_fs_fs__dag_dir_entries(table_p, node, pool);
2391
SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
2393
/* Convert directory array to hash. */
2394
for (i = 0; i < table->nelts; ++i)
2396
svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
2397
svn_hash_sets(hash, entry->name, entry);
2401
return SVN_NO_ERROR;
2404
static svn_error_t *
2405
fs_dir_optimal_order(apr_array_header_t **ordered_p,
2406
svn_fs_root_t *root,
2407
apr_hash_t *entries,
2408
apr_pool_t *result_pool,
2409
apr_pool_t *scratch_pool)
2411
*ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
2414
return SVN_NO_ERROR;
2077
2417
/* Raise an error if PATH contains a newline because FSFS cannot handle
2101
2441
parent_path_t *parent_path;
2102
2442
dag_node_t *sub_dir;
2103
const char *txn_id = root->txn;
2443
const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2105
2445
SVN_ERR(check_newline(path, pool));
2107
2447
path = svn_fs__canonicalize_abspath(path, pool);
2108
2448
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2111
2451
/* Check (recursively) to see if some lock is 'reserving' a path at
2112
2452
that location, or even some child-path; if so, check that we can
2137
2477
/* Make a record of this modification in the changes table. */
2138
2478
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2139
svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2140
SVN_INVALID_REVNUM, NULL, pool);
2479
svn_fs_path_change_add, FALSE, FALSE, FALSE,
2480
svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
2149
2489
apr_pool_t *pool)
2151
2491
parent_path_t *parent_path;
2152
const char *txn_id = root->txn;
2492
const svn_fs_fs__id_part_t *txn_id;
2153
2493
apr_int64_t mergeinfo_count = 0;
2154
2494
svn_node_kind_t kind;
2156
2496
if (! root->is_txn_root)
2157
2497
return SVN_FS__NOT_TXN(root);
2499
txn_id = root_txn_id(root);
2159
2500
path = svn_fs__canonicalize_abspath(path, pool);
2160
SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
2501
SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
2161
2502
kind = svn_fs_fs__dag_node_kind(parent_path->node);
2163
2504
/* We can't remove the root of the filesystem. */
2193
2534
/* Make a record of this modification in the changes table. */
2194
2535
return add_change(root->fs, txn_id, path,
2195
2536
svn_fs_fs__dag_get_id(parent_path->node),
2196
svn_fs_path_change_delete, FALSE, FALSE, kind,
2537
svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
2197
2538
SVN_INVALID_REVNUM, NULL, pool);
2236
2577
_("Cannot copy between two different filesystems ('%s' and '%s')"),
2237
2578
from_root->fs->path, to_root->fs->path);
2580
/* more things that we can't do ATM */
2239
2581
if (from_root->is_txn_root)
2240
2582
return svn_error_create
2241
2583
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2242
2584
_("Copy from mutable tree not currently supported"));
2586
if (! to_root->is_txn_root)
2587
return svn_error_create
2588
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2589
_("Copy immutable tree not supported"));
2244
2591
/* Get the NODE for FROM_PATH in FROM_ROOT.*/
2245
2592
SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2248
2595
component does not exist, it's not that big a deal. We'll just
2249
2596
make one there. */
2250
2597
SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2251
open_path_last_optional, txn_id, pool));
2598
open_path_last_optional, TRUE, pool));
2253
2600
/* Check to see if path (or any child thereof) is locked; if so,
2254
2601
check that we can use the existing lock(s). */
2321
2668
/* Make a record of this modification in the changes table. */
2322
2669
SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2323
2670
SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2324
svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2325
svn_fs_fs__dag_node_kind(from_node),
2671
svn_fs_fs__dag_get_id(new_node), kind, FALSE,
2672
FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
2326
2673
from_root->rev, from_canonpath, pool));
2397
2744
apr_pool_t *pool)
2399
2746
dag_node_t *node;
2400
const char *copyfrom_path, *copyfrom_str = NULL;
2401
svn_revnum_t copyfrom_rev;
2404
/* Check to see if there is a cached version of this copyfrom
2406
if (! root->is_txn_root) {
2407
fs_rev_root_data_t *frd = root->fsap_data;
2408
copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2413
if (*copyfrom_str == 0)
2415
/* We have a cached entry that says there is no copyfrom
2417
copyfrom_rev = SVN_INVALID_REVNUM;
2418
copyfrom_path = NULL;
2422
/* Parse the copyfrom string for our cached entry. */
2423
buf = apr_pstrdup(pool, copyfrom_str);
2424
str = svn_cstring_tokenize(" ", &buf);
2425
copyfrom_rev = SVN_STR_TO_REV(str);
2426
copyfrom_path = buf;
2431
/* There is no cached entry, look it up the old-fashioned
2433
SVN_ERR(get_dag(&node, root, path, pool));
2434
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev, node));
2435
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path, node));
2438
*rev_p = copyfrom_rev;
2439
*path_p = copyfrom_path;
2748
/* There is no cached entry, look it up the old-fashioned
2750
SVN_ERR(get_dag(&node, root, path, pool));
2751
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
2752
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
2441
2754
return SVN_NO_ERROR;
2455
2768
parent_path_t *parent_path;
2456
2769
dag_node_t *child;
2457
const char *txn_id = root->txn;
2770
const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2459
2772
SVN_ERR(check_newline(path, pool));
2461
2774
path = svn_fs__canonicalize_abspath(path, pool);
2462
2775
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2465
2778
/* If there's already a file by that name, complain.
2466
2779
This also catches the case of trying to make a file named `/'. */
2490
2803
/* Make a record of this modification in the changes table. */
2491
2804
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2492
svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2493
SVN_INVALID_REVNUM, NULL, pool);
2805
svn_fs_path_change_add, TRUE, FALSE, FALSE,
2806
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2612
2923
} txdelta_baton_t;
2615
/* ### see comment in window_consumer() regarding this function. */
2617
/* Helper function of generic type `svn_write_fn_t'. Implements a
2618
writable stream which appends to an svn_stringbuf_t. */
2619
static svn_error_t *
2620
write_to_string(void *baton, const char *data, apr_size_t *len)
2622
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2623
svn_stringbuf_appendbytes(tb->target_string, data, *len);
2624
return SVN_NO_ERROR;
2629
2926
/* The main window handler returned by svn_fs_apply_textdelta. */
2630
2927
static svn_error_t *
2631
2928
window_consumer(svn_txdelta_window_t *window, void *baton)
2637
2934
cb->target_string. */
2638
2935
SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2640
/* ### the write_to_string() callback for the txdelta's output stream
2641
### should be doing all the flush determination logic, not here.
2642
### in a drastic case, a window could generate a LOT more than the
2643
### maximum buffer size. we want to flush to the underlying target
2644
### stream much sooner (e.g. also in a streamy fashion). also, by
2645
### moving this logic inside the stream, the stream becomes nice
2646
### and encapsulated: it holds all the logic about buffering and
2649
### further: I believe the buffering should be removed from tree.c
2650
### the buffering should go into the target_stream itself, which
2651
### is defined by reps-string.c. Specifically, I think the
2652
### rep_write_contents() function will handle the buffering and
2653
### the spill to the underlying DB. by locating it there, then
2654
### anybody who gets a writable stream for FS content can take
2655
### advantage of the buffering capability. this will be important
2656
### when we export an FS API function for writing a fulltext into
2657
### the FS, rather than forcing that fulltext thru apply_textdelta.
2660
/* Check to see if we need to purge the portion of the contents that
2661
have been written thus far. */
2662
if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2664
apr_size_t len = tb->target_string->len;
2665
SVN_ERR(svn_stream_write(tb->target_stream,
2666
tb->target_string->data,
2668
svn_stringbuf_setempty(tb->target_string);
2671
/* Is the window NULL? If so, we're done. */
2937
/* Is the window NULL? If so, we're done. The stream has already been
2938
closed by the interpreter. */
2674
/* Close the internal-use stream. ### This used to be inside of
2675
txn_body_fulltext_finalize_edits(), but that invoked a nested
2676
Berkeley DB transaction -- scandalous! */
2677
SVN_ERR(svn_stream_close(tb->target_stream));
2679
SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2940
SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2683
2943
return SVN_NO_ERROR;
2691
2951
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2692
2952
parent_path_t *parent_path;
2693
const char *txn_id = tb->root->txn;
2953
const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
2695
2955
/* Call open_path with no flags, as we want this to return an error
2696
2956
if the node for which we are searching doesn't exist. */
2697
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2957
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
2699
2959
/* Check (non-recursively) to see if path is locked; if so, check
2700
2960
that we can use it. */
2730
2990
SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2733
/* Make a writable "string" stream which writes data to
2734
tb->target_string. */
2735
tb->target_string = svn_stringbuf_create_empty(tb->pool);
2736
tb->string_stream = svn_stream_create(tb, tb->pool);
2737
svn_stream_set_write(tb->string_stream, write_to_string);
2739
2993
/* Now, create a custom window handler that uses our two streams. */
2740
2994
svn_txdelta_apply(tb->source_stream,
2748
3002
/* Make a record of this modification in the changes table. */
2749
3003
return add_change(tb->root->fs, txn_id, tb->path,
2750
3004
svn_fs_fs__dag_get_id(tb->node),
2751
svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2752
SVN_INVALID_REVNUM, NULL, pool);
3005
svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3006
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2856
3110
struct text_baton_t *tb = baton;
2857
3111
parent_path_t *parent_path;
2858
const char *txn_id = tb->root->txn;
3112
const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
2860
3114
/* Call open_path with no flags, as we want this to return an error
2861
3115
if the node for which we are searching doesn't exist. */
2862
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
3116
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
2864
3118
/* Check (non-recursively) to see if path is locked; if so, check
2865
3119
that we can use it. */
2883
3137
/* Make a record of this modification in the changes table. */
2884
3138
return add_change(tb->root->fs, txn_id, tb->path,
2885
3139
svn_fs_fs__dag_get_id(tb->node),
2886
svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2887
SVN_INVALID_REVNUM, NULL, pool);
3140
svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3141
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2993
3248
apr_pool_t *pool)
2995
3250
if (root->is_txn_root)
2996
return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
3251
return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
3252
root_txn_id(root), pool);
3000
fs_rev_root_data_t *frd = root->fsap_data;
3001
return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3002
frd->copyfrom_cache, pool);
3254
return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3049
3302
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3051
3304
/* And we require that the path exist in the root. */
3052
SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
3305
SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
3053
3306
if (kind == svn_node_none)
3054
3307
return SVN_FS__NOT_FOUND(root, path);
3056
3309
/* Okay, all seems well. Build our history object and return it. */
3057
*history_p = assemble_history(root->fs,
3058
svn_fs__canonicalize_abspath(path, pool),
3059
root->rev, FALSE, NULL,
3060
SVN_INVALID_REVNUM, pool);
3310
*history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
3311
SVN_INVALID_REVNUM, result_pool);
3061
3312
return SVN_NO_ERROR;
3115
3366
const char *copy_dst_path;
3116
3367
svn_fs_root_t *copy_dst_root;
3117
3368
dag_node_t *copy_dst_node;
3118
svn_node_kind_t kind;
3120
3370
/* Initialize return values. */
3121
3371
*root_p = NULL;
3122
3372
*path_p = NULL;
3124
3374
path = svn_fs__canonicalize_abspath(path, pool);
3125
SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3375
SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
3127
3377
/* Find the youngest copyroot in the path of this node-rev, which
3128
3378
will indicate the target of the innermost copy affecting the
3136
3386
revision between COPY_DST_REV and REV. Make sure that PATH
3137
3387
exists as of COPY_DST_REV and is related to this node-rev. */
3138
3388
SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3139
SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3140
if (kind == svn_node_none)
3141
return SVN_NO_ERROR;
3142
3389
SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3143
open_path_node_only, NULL, pool));
3390
open_path_node_only | open_path_allow_null, FALSE, pool));
3391
if (copy_dst_parent_path == NULL)
3392
return SVN_NO_ERROR;
3144
3394
copy_dst_node = copy_dst_parent_path->node;
3145
3395
if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3146
3396
svn_fs_fs__dag_get_id(parent_path->node)))
3244
3494
SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3245
3495
node_id = svn_fs_fs__id_node_id(given_noderev_id);
3247
/* Is it a brand new uncommitted node? */
3248
if (node_id[0] == '_')
3250
*revision = SVN_INVALID_REVNUM;
3251
return SVN_NO_ERROR;
3254
/* Maybe this is a new-style node ID that just has the revision
3255
sitting right in it. */
3256
dash = strchr(node_id, '-');
3257
if (dash && *(dash+1))
3259
*revision = SVN_STR_TO_REV(dash + 1);
3260
return SVN_NO_ERROR;
3263
/* The root node always has ID 0, created in revision 0 and will never
3264
use the new-style ID format. */
3265
if (strcmp(node_id, "0") == 0)
3497
/* Is it a brand new uncommitted node or a new-style node ID?
3498
* (committed old-style nodes will have a 0 revision value;
3499
* rev 0, number 0 is rev 0 root node). Note that != 0 includes
3500
* SVN_INVALID_REVNUM for uncommitted nodes. */
3501
if (node_id->revision != 0 || node_id->number == 0)
3503
*revision = node_id->revision;
3268
3504
return SVN_NO_ERROR;
3358
struct history_prev_args
3360
svn_fs_history_t **prev_history_p;
3361
svn_fs_history_t *history;
3362
svn_boolean_t cross_copies;
3367
3594
static svn_error_t *
3368
history_prev(void *baton, apr_pool_t *pool)
3595
history_prev(svn_fs_history_t **prev_history,
3596
svn_fs_history_t *history,
3597
svn_boolean_t cross_copies,
3598
apr_pool_t *result_pool,
3599
apr_pool_t *scratch_pool)
3370
struct history_prev_args *args = baton;
3371
svn_fs_history_t **prev_history = args->prev_history_p;
3372
svn_fs_history_t *history = args->history;
3373
3601
fs_history_data_t *fhd = history->fsap_data;
3374
3602
const char *commit_path, *src_path, *path = fhd->path;
3375
3603
svn_revnum_t commit_rev, src_rev, dst_rev;
3376
3604
svn_revnum_t revision = fhd->revision;
3377
apr_pool_t *retpool = args->pool;
3378
3605
svn_fs_t *fs = fhd->fs;
3379
3606
parent_path_t *parent_path;
3380
3607
dag_node_t *node;
3393
3620
if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3395
3622
reported = FALSE;
3396
if (! args->cross_copies)
3397
3624
return SVN_NO_ERROR;
3398
3625
path = fhd->path_hint;
3399
3626
revision = fhd->rev_hint;
3402
3629
/* Construct a ROOT for the current revision. */
3403
SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3630
SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
3405
3632
/* Open PATH/REVISION, and get its node and a bunch of other
3407
SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3634
SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
3408
3635
node = parent_path->node;
3409
3636
commit_path = svn_fs_fs__dag_get_created_path(node);
3410
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3637
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3412
3639
/* The Subversion filesystem is written in such a way that a given
3413
3640
line of history may have at most one interesting history point
3423
3650
/* ... we either have not yet reported on this revision (and
3424
3651
need now to do so) ... */
3425
*prev_history = assemble_history(fs,
3426
apr_pstrdup(retpool, commit_path),
3652
*prev_history = assemble_history(fs, commit_path,
3427
3653
commit_rev, TRUE, NULL,
3428
SVN_INVALID_REVNUM, retpool);
3654
SVN_INVALID_REVNUM, result_pool);
3429
3655
return SVN_NO_ERROR;
3442
3668
/* Replace NODE and friends with the information from its
3443
3669
predecessor. */
3444
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
3670
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
3445
3671
commit_path = svn_fs_fs__dag_get_created_path(node);
3446
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3672
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3450
3676
/* Find the youngest copyroot in the path of this node, including
3452
3678
SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3453
parent_path, pool));
3679
parent_path, scratch_pool));
3455
3681
/* Initialize some state variables. */
3456
3682
src_path = NULL;
3464
3690
svn_fs_root_t *copyroot_root;
3466
3692
SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3468
SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
3694
SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
3469
3695
copy_dst = svn_fs_fs__dag_get_created_path(node);
3471
3697
/* If our current path was the very destination of the copy,
3504
3730
if ((dst_rev == revision) && reported)
3507
*prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3509
src_path, src_rev, retpool);
3733
*prev_history = assemble_history(fs, path, dst_rev, ! retry,
3734
src_path, src_rev, result_pool);
3513
*prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3514
commit_rev, TRUE, NULL,
3515
SVN_INVALID_REVNUM, retpool);
3738
*prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
3739
NULL, SVN_INVALID_REVNUM, result_pool);
3518
3742
return SVN_NO_ERROR;
3542
3767
if (! fhd->is_interesting)
3543
3768
prev_history = assemble_history(fs, "/", fhd->revision,
3544
1, NULL, SVN_INVALID_REVNUM, pool);
3769
1, NULL, SVN_INVALID_REVNUM,
3545
3771
else if (fhd->revision > 0)
3546
3772
prev_history = assemble_history(fs, "/", fhd->revision - 1,
3547
1, NULL, SVN_INVALID_REVNUM, pool);
3773
1, NULL, SVN_INVALID_REVNUM,
3551
struct history_prev_args args;
3778
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3552
3779
prev_history = history;
3556
args.prev_history_p = &prev_history;
3557
args.history = prev_history;
3558
args.cross_copies = cross_copies;
3560
SVN_ERR(history_prev(&args, pool));
3783
svn_pool_clear(iterpool);
3784
SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
3785
result_pool, iterpool));
3562
3787
if (! prev_history)
3595
3822
/* Return a new history object (marked as "interesting") for PATH and
3596
3823
REVISION, allocated in POOL, and with its members set to the values
3597
of the parameters provided. Note that PATH and PATH_HINT are not
3598
duped into POOL -- it is the responsibility of the caller to ensure
3599
that this happens. */
3824
of the parameters provided. Note that PATH and PATH_HINT get
3825
normalized and duplicated in POOL. */
3600
3826
static svn_fs_history_t *
3601
3827
assemble_history(svn_fs_t *fs,
3602
3828
const char *path,
3643
3870
apr_pool_t *result_pool,
3644
3871
apr_pool_t *scratch_pool)
3646
apr_hash_t *entries;
3647
apr_hash_index_t *hi;
3873
apr_array_header_t *entries;
3648
3875
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3650
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3653
for (hi = apr_hash_first(scratch_pool, entries);
3655
hi = apr_hash_next(hi))
3877
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
3878
for (i = 0; i < entries->nelts; ++i)
3657
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3880
svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
3658
3881
const char *kid_path;
3659
3882
dag_node_t *kid_dag;
3660
3883
svn_boolean_t has_mergeinfo, go_down;
3759
3982
path = svn_fs__canonicalize_abspath(path, scratch_pool);
3761
SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3984
SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
3763
3986
if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3764
3987
return SVN_NO_ERROR;
4088
4309
static svn_error_t *
4089
4310
make_txn_root(svn_fs_root_t **root_p,
4312
const svn_fs_fs__id_part_t *txn,
4092
4313
svn_revnum_t base_rev,
4093
4314
apr_uint32_t flags,
4094
4315
apr_pool_t *pool)
4096
4317
svn_fs_root_t *root = make_root(fs, pool);
4097
4318
fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4099
4321
root->is_txn_root = TRUE;
4100
root->txn = apr_pstrdup(root->pool, txn);
4322
root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
4101
4323
root->txn_flags = flags;
4102
4324
root->rev = base_rev;
4106
4326
/* Because this cache actually tries to invalidate elements, keep
4107
4327
the number of elements per page down.
4114
4334
APR_HASH_KEY_STRING,
4116
4336
apr_pstrcat(pool, txn, ":TXN",
4120
4340
/* Initialize transaction-local caches in FS.
4122
4342
Note that we cannot put those caches in frd because that content
4123
4343
fs root object is not available where we would need it. */
4124
SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
4344
SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
4126
4346
root->fsap_data = frd;
4143
4363
/* Check metadata sanity on NODE, and on its children. Manually verify
4144
4364
information for DAG nodes in revision REV, and trust the metadata
4145
accuracy for nodes belonging to older revisions. */
4365
accuracy for nodes belonging to older revisions. To detect cycles,
4366
provide all parent dag_node_t * in PARENT_NODES. */
4146
4367
static svn_error_t *
4147
4368
verify_node(dag_node_t *node,
4148
4369
svn_revnum_t rev,
4370
apr_array_header_t *parent_nodes,
4149
4371
apr_pool_t *pool)
4151
4373
svn_boolean_t has_mergeinfo;
4155
4377
int pred_count;
4156
4378
svn_node_kind_t kind;
4157
4379
apr_pool_t *iterpool = svn_pool_create(pool);
4382
/* Detect (non-)DAG cycles. */
4383
for (i = 0; i < parent_nodes->nelts; ++i)
4385
dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
4386
if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
4387
svn_fs_fs__dag_get_id(node)))
4388
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4389
"Node is its own direct or indirect parent '%s'",
4390
stringify_node(node, iterpool));
4159
4393
/* Fetch some data. */
4160
4394
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4206
4440
if (kind == svn_node_dir)
4208
apr_hash_t *entries;
4209
apr_hash_index_t *hi;
4442
apr_array_header_t *entries;
4210
4443
apr_int64_t children_mergeinfo = 0;
4444
APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
4212
4446
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4214
4448
/* Compute CHILDREN_MERGEINFO. */
4215
for (hi = apr_hash_first(pool, entries);
4217
hi = apr_hash_next(hi))
4449
for (i = 0; i < entries->nelts; ++i)
4219
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4451
svn_fs_dirent_t *dirent
4452
= APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4220
4453
dag_node_t *child;
4221
svn_revnum_t child_rev;
4222
4454
apr_int64_t child_mergeinfo;
4224
4456
svn_pool_clear(iterpool);
4226
4458
/* Compute CHILD_REV. */
4227
SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4228
SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4230
if (child_rev == rev)
4231
SVN_ERR(verify_node(child, rev, iterpool));
4233
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4459
if (svn_fs_fs__id_rev(dirent->id) == rev)
4461
SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
4463
SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
4464
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
4469
/* access mergeinfo counter with minimal overhead */
4470
node_revision_t *noderev;
4471
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
4472
iterpool, iterpool));
4473
child_mergeinfo = noderev->mergeinfo_count;
4234
4476
children_mergeinfo += child_mergeinfo;
4270
4517
if (root->is_txn_root)
4272
4519
fs_txn_root_data_t *frd = root->fsap_data;
4273
SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
4520
SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
4277
fs_rev_root_data_t *frd = root->fsap_data;
4278
root_dir = frd->root_dir;
4524
root_dir = root->fsap_data;
4281
4527
/* Recursively verify ROOT_DIR. */
4282
SVN_ERR(verify_node(root_dir, root->rev, pool));
4528
parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
4529
SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
4284
4531
/* Verify explicitly the predecessor of the root. */