]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_fs_fs/dag.c
MFV: r363292
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_fs_fs / dag.c
1 /* dag.c : DAG-like interface filesystem, private to libsvn_fs
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include <string.h>
24
25 #include "svn_path.h"
26 #include "svn_error.h"
27 #include "svn_fs.h"
28 #include "svn_props.h"
29 #include "svn_pools.h"
30
31 #include "cached_data.h"
32 #include "dag.h"
33 #include "fs.h"
34 #include "fs_fs.h"
35 #include "id.h"
36 #include "transaction.h"
37
38 #include "../libsvn_fs/fs-loader.h"
39
40 #include "private/svn_fspath.h"
41 #include "svn_private_config.h"
42 #include "private/svn_temp_serializer.h"
43 #include "temp_serializer.h"
44
45 \f
46 /* Initializing a filesystem.  */
47
48 struct dag_node_t
49 {
50   /* The filesystem this dag node came from. */
51   svn_fs_t *fs;
52
53   /* The node revision ID for this dag node, allocated in POOL.  */
54   svn_fs_id_t *id;
55
56   /* In the special case that this node is the root of a transaction
57      that has not yet been modified, the node revision ID for this dag
58      node's predecessor; otherwise NULL. (Used in
59      svn_fs_node_created_rev.) */
60   const svn_fs_id_t *fresh_root_predecessor_id;
61
62   /* The node's type (file, dir, etc.) */
63   svn_node_kind_t kind;
64
65   /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
66      This is allocated in this node's POOL.
67
68      If you're willing to respect all the rules above, you can munge
69      this yourself, but you're probably better off just calling
70      `get_node_revision' and `set_node_revision', which take care of
71      things for you.  */
72   node_revision_t *node_revision;
73
74   /* The pool to allocate NODE_REVISION in. */
75   apr_pool_t *node_pool;
76
77   /* the path at which this node was created. */
78   const char *created_path;
79 };
80
81
82 \f
83 /* Trivial helper/accessor functions. */
84 svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
85 {
86   return node->kind;
87 }
88
89
90 const svn_fs_id_t *
91 svn_fs_fs__dag_get_id(const dag_node_t *node)
92 {
93   return node->id;
94 }
95
96
97 const char *
98 svn_fs_fs__dag_get_created_path(dag_node_t *node)
99 {
100   return node->created_path;
101 }
102
103
104 svn_fs_t *
105 svn_fs_fs__dag_get_fs(dag_node_t *node)
106 {
107   return node->fs;
108 }
109
110 void
111 svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
112 {
113   node->fs = fs;
114 }
115
116
117 /* Dup NODEREV and all associated data into POOL.
118    Leaves the id and is_fresh_txn_root fields as zero bytes. */
119 static node_revision_t *
120 copy_node_revision(node_revision_t *noderev,
121                    apr_pool_t *pool)
122 {
123   node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr));
124   nr->kind = noderev->kind;
125   if (noderev->predecessor_id)
126     nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
127   nr->predecessor_count = noderev->predecessor_count;
128   if (noderev->copyfrom_path)
129     nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path);
130   nr->copyfrom_rev = noderev->copyfrom_rev;
131   nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path);
132   nr->copyroot_rev = noderev->copyroot_rev;
133   nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool);
134   nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool);
135   nr->mergeinfo_count = noderev->mergeinfo_count;
136   nr->has_mergeinfo = noderev->has_mergeinfo;
137
138   if (noderev->created_path)
139     nr->created_path = apr_pstrdup(pool, noderev->created_path);
140   return nr;
141 }
142
143
144 /* Set *NODEREV_P to the cached node-revision for NODE.
145    If the node-revision was not already cached in NODE, read it in,
146    allocating the cache in NODE->NODE_POOL.
147
148    If you plan to change the contents of NODE, be careful!  We're
149    handing you a pointer directly to our cached node-revision, not
150    your own copy.  If you change it as part of some operation, but
151    then some Berkeley DB function deadlocks or gets an error, you'll
152    need to back out your changes, or else the cache will reflect
153    changes that never got committed.  It's probably best not to change
154    the structure at all.  */
155 static svn_error_t *
156 get_node_revision(node_revision_t **noderev_p,
157                   dag_node_t *node)
158 {
159   /* If we've already got a copy, there's no need to read it in.  */
160   if (! node->node_revision)
161     {
162       node_revision_t *noderev;
163       apr_pool_t *scratch_pool = svn_pool_create(node->node_pool);
164
165       SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs,
166                                            node->id, node->node_pool,
167                                            scratch_pool));
168       node->node_revision = noderev;
169       svn_pool_destroy(scratch_pool);
170     }
171
172   /* Now NODE->node_revision is set.  */
173   *noderev_p = node->node_revision;
174   return SVN_NO_ERROR;
175 }
176
177
178 svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node)
179 {
180   return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node));
181 }
182
183
184 svn_error_t *
185 svn_fs_fs__dag_get_node(dag_node_t **node,
186                         svn_fs_t *fs,
187                         const svn_fs_id_t *id,
188                         apr_pool_t *pool)
189 {
190   dag_node_t *new_node;
191   node_revision_t *noderev;
192
193   /* Construct the node. */
194   new_node = apr_pcalloc(pool, sizeof(*new_node));
195   new_node->fs = fs;
196   new_node->id = svn_fs_fs__id_copy(id, pool);
197
198   /* Grab the contents so we can inspect the node's kind and created path. */
199   new_node->node_pool = pool;
200   SVN_ERR(get_node_revision(&noderev, new_node));
201
202   /* Initialize the KIND and CREATED_PATH attributes */
203   new_node->kind = noderev->kind;
204   new_node->created_path = apr_pstrdup(pool, noderev->created_path);
205
206   if (noderev->is_fresh_txn_root)
207     new_node->fresh_root_predecessor_id = noderev->predecessor_id;
208   else
209     new_node->fresh_root_predecessor_id = NULL;
210
211   /* Return a fresh new node */
212   *node = new_node;
213   return SVN_NO_ERROR;
214 }
215
216
217 svn_error_t *
218 svn_fs_fs__dag_get_revision(svn_revnum_t *rev,
219                             dag_node_t *node,
220                             apr_pool_t *pool)
221 {
222   /* In the special case that this is an unmodified transaction root,
223      we need to actually get the revision of the noderev's predecessor
224      (the revision root); see Issue #2608. */
225   const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
226     ? node->fresh_root_predecessor_id : node->id;
227
228   /* Look up the committed revision from the Node-ID. */
229   *rev = svn_fs_fs__id_rev(correct_id);
230
231   return SVN_NO_ERROR;
232 }
233
234
235 svn_error_t *
236 svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p,
237                                   dag_node_t *node)
238 {
239   node_revision_t *noderev;
240
241   SVN_ERR(get_node_revision(&noderev, node));
242   *id_p = noderev->predecessor_id;
243   return SVN_NO_ERROR;
244 }
245
246
247 svn_error_t *
248 svn_fs_fs__dag_get_predecessor_count(int *count,
249                                      dag_node_t *node)
250 {
251   node_revision_t *noderev;
252
253   SVN_ERR(get_node_revision(&noderev, node));
254   *count = noderev->predecessor_count;
255   return SVN_NO_ERROR;
256 }
257
258 svn_error_t *
259 svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count,
260                                    dag_node_t *node)
261 {
262   node_revision_t *noderev;
263
264   SVN_ERR(get_node_revision(&noderev, node));
265   *count = noderev->mergeinfo_count;
266   return SVN_NO_ERROR;
267 }
268
269 svn_error_t *
270 svn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
271                              dag_node_t *node)
272 {
273   node_revision_t *noderev;
274
275   SVN_ERR(get_node_revision(&noderev, node));
276   *has_mergeinfo = noderev->has_mergeinfo;
277   return SVN_NO_ERROR;
278 }
279
280 svn_error_t *
281 svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
282                                               dag_node_t *node)
283 {
284   node_revision_t *noderev;
285
286   if (node->kind != svn_node_dir)
287     {
288       *do_they = FALSE;
289       return SVN_NO_ERROR;
290     }
291
292   SVN_ERR(get_node_revision(&noderev, node));
293   if (noderev->mergeinfo_count > 1)
294     *do_they = TRUE;
295   else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
296     *do_they = TRUE;
297   else
298     *do_they = FALSE;
299   return SVN_NO_ERROR;
300 }
301
302 \f
303 /*** Directory node functions ***/
304
305 /* Some of these are helpers for functions outside this section. */
306
307 /* Set *ID_P to the node-id for entry NAME in PARENT.  If no such
308    entry, set *ID_P to NULL but do not error.  The node-id is
309    allocated in POOL. */
310 static svn_error_t *
311 dir_entry_id_from_node(const svn_fs_id_t **id_p,
312                        dag_node_t *parent,
313                        const char *name,
314                        apr_pool_t *result_pool,
315                        apr_pool_t *scratch_pool)
316 {
317   svn_fs_dirent_t *dirent;
318
319   SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool,
320                                    scratch_pool));
321   *id_p = dirent ? dirent->id : NULL;
322
323   return SVN_NO_ERROR;
324 }
325
326
327 /* Add or set in PARENT a directory entry NAME pointing to ID.
328    Allocations are done in POOL.
329
330    Assumptions:
331    - PARENT is a mutable directory.
332    - ID does not refer to an ancestor of parent
333    - NAME is a single path component
334 */
335 static svn_error_t *
336 set_entry(dag_node_t *parent,
337           const char *name,
338           const svn_fs_id_t *id,
339           svn_node_kind_t kind,
340           const svn_fs_fs__id_part_t *txn_id,
341           apr_pool_t *pool)
342 {
343   node_revision_t *parent_noderev;
344
345   /* Get the parent's node-revision. */
346   SVN_ERR(get_node_revision(&parent_noderev, parent));
347
348   /* Set the new entry. */
349   return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id,
350                               kind, pool);
351 }
352
353
354 /* Make a new entry named NAME in PARENT.  If IS_DIR is true, then the
355    node revision the new entry points to will be a directory, else it
356    will be a file.  The new node will be allocated in POOL.  PARENT
357    must be mutable, and must not have an entry named NAME.
358
359    Use POOL for all allocations, except caching the node_revision in PARENT.
360  */
361 static svn_error_t *
362 make_entry(dag_node_t **child_p,
363            dag_node_t *parent,
364            const char *parent_path,
365            const char *name,
366            svn_boolean_t is_dir,
367            const svn_fs_fs__id_part_t *txn_id,
368            apr_pool_t *pool)
369 {
370   const svn_fs_id_t *new_node_id;
371   node_revision_t new_noderev, *parent_noderev;
372
373   /* Make sure that NAME is a single path component. */
374   if (! svn_path_is_single_path_component(name))
375     return svn_error_createf
376       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
377        _("Attempted to create a node with an illegal name '%s'"), name);
378
379   /* Make sure that parent is a directory */
380   if (parent->kind != svn_node_dir)
381     return svn_error_create
382       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
383        _("Attempted to create entry in non-directory parent"));
384
385   /* Check that the parent is mutable. */
386   if (! svn_fs_fs__dag_check_mutable(parent))
387     return svn_error_createf
388       (SVN_ERR_FS_NOT_MUTABLE, NULL,
389        _("Attempted to clone child of non-mutable node"));
390
391   /* Create the new node's NODE-REVISION */
392   memset(&new_noderev, 0, sizeof(new_noderev));
393   new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
394   new_noderev.created_path = svn_fspath__join(parent_path, name, pool);
395
396   SVN_ERR(get_node_revision(&parent_noderev, parent));
397   new_noderev.copyroot_path = apr_pstrdup(pool,
398                                           parent_noderev->copyroot_path);
399   new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
400   new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
401   new_noderev.copyfrom_path = NULL;
402
403   SVN_ERR(svn_fs_fs__create_node
404           (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev,
405            svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)),
406            txn_id, pool));
407
408   /* Create a new dag_node_t for our new node */
409   SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
410                                   new_node_id, pool));
411
412   /* We can safely call set_entry because we already know that
413      PARENT is mutable, and we just created CHILD, so we know it has
414      no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
415   return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p),
416                    new_noderev.kind, txn_id, pool);
417 }
418
419
420 svn_error_t *
421 svn_fs_fs__dag_dir_entries(apr_array_header_t **entries,
422                            dag_node_t *node,
423                            apr_pool_t *pool)
424 {
425   node_revision_t *noderev;
426
427   SVN_ERR(get_node_revision(&noderev, node));
428
429   if (noderev->kind != svn_node_dir)
430     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
431                             _("Can't get entries of non-directory"));
432
433   return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool);
434 }
435
436 svn_error_t *
437 svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
438                          dag_node_t *node,
439                          const char* name,
440                          apr_pool_t *result_pool,
441                          apr_pool_t *scratch_pool)
442 {
443   node_revision_t *noderev;
444   SVN_ERR(get_node_revision(&noderev, node));
445
446   if (noderev->kind != svn_node_dir)
447     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
448                             _("Can't get entries of non-directory"));
449
450   /* Get a dirent hash for this directory. */
451   return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name,
452                                            result_pool, scratch_pool);
453 }
454
455
456 svn_error_t *
457 svn_fs_fs__dag_set_entry(dag_node_t *node,
458                          const char *entry_name,
459                          const svn_fs_id_t *id,
460                          svn_node_kind_t kind,
461                          const svn_fs_fs__id_part_t *txn_id,
462                          apr_pool_t *pool)
463 {
464   /* Check it's a directory. */
465   if (node->kind != svn_node_dir)
466     return svn_error_create
467       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
468        _("Attempted to set entry in non-directory node"));
469
470   /* Check it's mutable. */
471   if (! svn_fs_fs__dag_check_mutable(node))
472     return svn_error_create
473       (SVN_ERR_FS_NOT_MUTABLE, NULL,
474        _("Attempted to set entry in immutable node"));
475
476   return set_entry(node, entry_name, id, kind, txn_id, pool);
477 }
478
479
480 \f
481 /*** Proplists. ***/
482
483 svn_error_t *
484 svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p,
485                             dag_node_t *node,
486                             apr_pool_t *pool)
487 {
488   node_revision_t *noderev;
489   apr_hash_t *proplist = NULL;
490
491   SVN_ERR(get_node_revision(&noderev, node));
492
493   SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
494                                   noderev, pool));
495
496   *proplist_p = proplist;
497
498   return SVN_NO_ERROR;
499 }
500
501 svn_error_t *
502 svn_fs_fs__dag_has_props(svn_boolean_t *has_props,
503                          dag_node_t *node,
504                          apr_pool_t *scratch_pool)
505 {
506   node_revision_t *noderev;
507
508   SVN_ERR(get_node_revision(&noderev, node));
509
510   if (! noderev->prop_rep)
511     {
512       *has_props = FALSE; /* Easy out */
513       return SVN_NO_ERROR;
514     }
515
516   if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id))
517     {
518       /* We are in a commit or something. Check actual properties */
519       apr_hash_t *proplist;
520
521       SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
522                                       noderev, scratch_pool));
523
524       *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE;
525     }
526   else
527     {
528       /* Properties are stored as a standard hash stream,
529          always ending with "END\n" (4 bytes) */
530       *has_props = noderev->prop_rep->expanded_size > 4;
531     }
532
533   return SVN_NO_ERROR;
534 }
535
536 svn_error_t *
537 svn_fs_fs__dag_set_proplist(dag_node_t *node,
538                             apr_hash_t *proplist,
539                             apr_pool_t *pool)
540 {
541   node_revision_t *noderev;
542
543   /* Sanity check: this node better be mutable! */
544   if (! svn_fs_fs__dag_check_mutable(node))
545     {
546       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
547       return svn_error_createf
548         (SVN_ERR_FS_NOT_MUTABLE, NULL,
549          "Can't set proplist on *immutable* node-revision %s",
550          idstr->data);
551     }
552
553   /* Go get a fresh NODE-REVISION for this node. */
554   SVN_ERR(get_node_revision(&noderev, node));
555
556   /* Set the new proplist. */
557   return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
558 }
559
560
561 svn_error_t *
562 svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
563                                          apr_int64_t increment,
564                                          apr_pool_t *pool)
565 {
566   node_revision_t *noderev;
567
568   /* Sanity check: this node better be mutable! */
569   if (! svn_fs_fs__dag_check_mutable(node))
570     {
571       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
572       return svn_error_createf
573         (SVN_ERR_FS_NOT_MUTABLE, NULL,
574          "Can't increment mergeinfo count on *immutable* node-revision %s",
575          idstr->data);
576     }
577
578   if (increment == 0)
579     return SVN_NO_ERROR;
580
581   /* Go get a fresh NODE-REVISION for this node. */
582   SVN_ERR(get_node_revision(&noderev, node));
583
584   noderev->mergeinfo_count += increment;
585   if (noderev->mergeinfo_count < 0)
586     {
587       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
588       return svn_error_createf
589         (SVN_ERR_FS_CORRUPT, NULL,
590          apr_psprintf(pool,
591                       _("Can't increment mergeinfo count on node-revision %%s "
592                         "to negative value %%%s"),
593                       APR_INT64_T_FMT),
594          idstr->data, noderev->mergeinfo_count);
595     }
596   if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
597     {
598       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
599       return svn_error_createf
600         (SVN_ERR_FS_CORRUPT, NULL,
601          apr_psprintf(pool,
602                       _("Can't increment mergeinfo count on *file* "
603                         "node-revision %%s to %%%s (> 1)"),
604                       APR_INT64_T_FMT),
605          idstr->data, noderev->mergeinfo_count);
606     }
607
608   /* Flush it out. */
609   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
610                                       noderev, FALSE, pool);
611 }
612
613 svn_error_t *
614 svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
615                                  svn_boolean_t has_mergeinfo,
616                                  apr_pool_t *pool)
617 {
618   node_revision_t *noderev;
619
620   /* Sanity check: this node better be mutable! */
621   if (! svn_fs_fs__dag_check_mutable(node))
622     {
623       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
624       return svn_error_createf
625         (SVN_ERR_FS_NOT_MUTABLE, NULL,
626          "Can't set mergeinfo flag on *immutable* node-revision %s",
627          idstr->data);
628     }
629
630   /* Go get a fresh NODE-REVISION for this node. */
631   SVN_ERR(get_node_revision(&noderev, node));
632
633   noderev->has_mergeinfo = has_mergeinfo;
634
635   /* Flush it out. */
636   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
637                                       noderev, FALSE, pool);
638 }
639
640 \f
641 /*** Roots. ***/
642
643 svn_error_t *
644 svn_fs_fs__dag_revision_root(dag_node_t **node_p,
645                              svn_fs_t *fs,
646                              svn_revnum_t rev,
647                              apr_pool_t *pool)
648 {
649   dag_node_t *new_node;
650
651   /* Construct the node. */
652   new_node = apr_pcalloc(pool, sizeof(*new_node));
653   new_node->fs = fs;
654   SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool));
655
656   /* Grab the contents so we can inspect the node's kind and created path. */
657   new_node->node_pool = pool;
658
659   /* Initialize the KIND and CREATED_PATH attributes */
660   new_node->kind = svn_node_dir;
661   new_node->created_path = "/";
662   new_node->fresh_root_predecessor_id = NULL;
663
664   /* Return a fresh new node */
665   *node_p = new_node;
666   return SVN_NO_ERROR;
667 }
668
669
670 svn_error_t *
671 svn_fs_fs__dag_txn_root(dag_node_t **node_p,
672                         svn_fs_t *fs,
673                         const svn_fs_fs__id_part_t *txn_id,
674                         apr_pool_t *pool)
675 {
676   const svn_fs_id_t *root_id, *ignored;
677
678   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
679   return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
680 }
681
682
683 svn_error_t *
684 svn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
685                              svn_fs_t *fs,
686                              const svn_fs_fs__id_part_t *txn_id,
687                              apr_pool_t *pool)
688 {
689   const svn_fs_id_t *base_root_id, *ignored;
690
691   SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
692   return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
693 }
694
695
696 svn_error_t *
697 svn_fs_fs__dag_clone_child(dag_node_t **child_p,
698                            dag_node_t *parent,
699                            const char *parent_path,
700                            const char *name,
701                            const svn_fs_fs__id_part_t *copy_id,
702                            const svn_fs_fs__id_part_t *txn_id,
703                            svn_boolean_t is_parent_copyroot,
704                            apr_pool_t *pool)
705 {
706   dag_node_t *cur_entry; /* parent's current entry named NAME */
707   const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
708   svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
709   apr_pool_t *subpool = svn_pool_create(pool);
710
711   /* First check that the parent is mutable. */
712   if (! svn_fs_fs__dag_check_mutable(parent))
713     return svn_error_createf
714       (SVN_ERR_FS_NOT_MUTABLE, NULL,
715        "Attempted to clone child of non-mutable node");
716
717   /* Make sure that NAME is a single path component. */
718   if (! svn_path_is_single_path_component(name))
719     return svn_error_createf
720       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
721        "Attempted to make a child clone with an illegal name '%s'", name);
722
723   /* Find the node named NAME in PARENT's entries list if it exists. */
724   SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool));
725   if (! cur_entry)
726     return svn_error_createf
727       (SVN_ERR_FS_NOT_FOUND, NULL,
728        "Attempted to open non-existent child node '%s'", name);
729
730   /* Check for mutability in the node we found.  If it's mutable, we
731      don't need to clone it. */
732   if (svn_fs_fs__dag_check_mutable(cur_entry))
733     {
734       /* This has already been cloned */
735       new_node_id = cur_entry->id;
736     }
737   else
738     {
739       node_revision_t *noderev, *parent_noderev;
740
741       /* Go get a fresh NODE-REVISION for current child node. */
742       SVN_ERR(get_node_revision(&noderev, cur_entry));
743
744       if (is_parent_copyroot)
745         {
746           SVN_ERR(get_node_revision(&parent_noderev, parent));
747           noderev->copyroot_rev = parent_noderev->copyroot_rev;
748           noderev->copyroot_path = apr_pstrdup(pool,
749                                                parent_noderev->copyroot_path);
750         }
751
752       noderev->copyfrom_path = NULL;
753       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
754
755       noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
756       noderev->predecessor_count++;
757       noderev->created_path = svn_fspath__join(parent_path, name, pool);
758
759       SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
760                                           noderev, copy_id, txn_id, pool));
761
762       /* Replace the ID in the parent's ENTRY list with the ID which
763          refers to the mutable clone of this child. */
764       SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
765                         pool));
766     }
767
768   /* Initialize the youngster. */
769   svn_pool_destroy(subpool);
770   return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
771 }
772
773
774
775 svn_error_t *
776 svn_fs_fs__dag_clone_root(dag_node_t **root_p,
777                           svn_fs_t *fs,
778                           const svn_fs_fs__id_part_t *txn_id,
779                           apr_pool_t *pool)
780 {
781   const svn_fs_id_t *base_root_id, *root_id;
782
783   /* Get the node ID's of the root directories of the transaction and
784      its base revision.  */
785   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
786
787   /* Oh, give me a clone...
788      (If they're the same, we haven't cloned the transaction's root
789      directory yet.)  */
790   SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
791
792   /*
793    * (Sung to the tune of "Home, Home on the Range", with thanks to
794    * Randall Garrett and Isaac Asimov.)
795    */
796
797   /* One way or another, root_id now identifies a cloned root node. */
798   return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
799 }
800
801
802 svn_error_t *
803 svn_fs_fs__dag_delete(dag_node_t *parent,
804                       const char *name,
805                       const svn_fs_fs__id_part_t *txn_id,
806                       apr_pool_t *pool)
807 {
808   node_revision_t *parent_noderev;
809   svn_fs_t *fs = parent->fs;
810   svn_fs_dirent_t *dirent;
811   svn_fs_id_t *id;
812   apr_pool_t *subpool;
813
814   /* Make sure parent is a directory. */
815   if (parent->kind != svn_node_dir)
816     return svn_error_createf
817       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
818        "Attempted to delete entry '%s' from *non*-directory node", name);
819
820   /* Make sure parent is mutable. */
821   if (! svn_fs_fs__dag_check_mutable(parent))
822     return svn_error_createf
823       (SVN_ERR_FS_NOT_MUTABLE, NULL,
824        "Attempted to delete entry '%s' from immutable directory node", name);
825
826   /* Make sure that NAME is a single path component. */
827   if (! svn_path_is_single_path_component(name))
828     return svn_error_createf
829       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
830        "Attempted to delete a node with an illegal name '%s'", name);
831
832   /* Get a fresh NODE-REVISION for the parent node. */
833   SVN_ERR(get_node_revision(&parent_noderev, parent));
834
835   subpool = svn_pool_create(pool);
836
837   /* Search this directory for a dirent with that NAME. */
838   SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev,
839                                             name, subpool, subpool));
840
841   /* If we never found ID in ENTRIES (perhaps because there are no
842      ENTRIES, perhaps because ID just isn't in the existing ENTRIES
843      ... it doesn't matter), return an error.  */
844   if (! dirent)
845     return svn_error_createf
846       (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
847        "Delete failed--directory has no entry '%s'", name);
848
849   /* Copy the ID out of the subpool and release the rest of the
850      directory listing. */
851   id = svn_fs_fs__id_copy(dirent->id, pool);
852   svn_pool_destroy(subpool);
853
854   /* If mutable, remove it and any mutable children from db. */
855   SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
856
857   /* Remove this entry from its parent's entries list. */
858   return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
859                               NULL, svn_node_unknown, pool);
860 }
861
862
863 svn_error_t *
864 svn_fs_fs__dag_remove_node(svn_fs_t *fs,
865                            const svn_fs_id_t *id,
866                            apr_pool_t *pool)
867 {
868   dag_node_t *node;
869
870   /* Fetch the node. */
871   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
872
873   /* If immutable, do nothing and return immediately. */
874   if (! svn_fs_fs__dag_check_mutable(node))
875     return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
876                              "Attempted removal of immutable node");
877
878   /* Delete the node revision. */
879   return svn_fs_fs__delete_node_revision(fs, id, pool);
880 }
881
882
883 svn_error_t *
884 svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
885                                  const svn_fs_id_t *id,
886                                  apr_pool_t *pool)
887 {
888   dag_node_t *node;
889
890   /* Get the node. */
891   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
892
893   /* If immutable, do nothing and return immediately. */
894   if (! svn_fs_fs__dag_check_mutable(node))
895     return SVN_NO_ERROR;
896
897   /* Else it's mutable.  Recurse on directories... */
898   if (node->kind == svn_node_dir)
899     {
900       apr_array_header_t *entries;
901       int i;
902       apr_pool_t *iterpool = svn_pool_create(pool);
903
904       /* Loop over directory entries */
905       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
906       if (entries)
907         for (i = 0; i < entries->nelts; ++i)
908           {
909             svn_pool_clear(iterpool);
910             SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs,
911                           APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id,
912                           iterpool));
913           }
914
915       svn_pool_destroy(iterpool);
916     }
917
918   /* ... then delete the node itself, after deleting any mutable
919      representations and strings it points to. */
920   return svn_fs_fs__dag_remove_node(fs, id, pool);
921 }
922
923 svn_error_t *
924 svn_fs_fs__dag_make_file(dag_node_t **child_p,
925                          dag_node_t *parent,
926                          const char *parent_path,
927                          const char *name,
928                          const svn_fs_fs__id_part_t *txn_id,
929                          apr_pool_t *pool)
930 {
931   /* Call our little helper function */
932   return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
933 }
934
935
936 svn_error_t *
937 svn_fs_fs__dag_make_dir(dag_node_t **child_p,
938                         dag_node_t *parent,
939                         const char *parent_path,
940                         const char *name,
941                         const svn_fs_fs__id_part_t *txn_id,
942                         apr_pool_t *pool)
943 {
944   /* Call our little helper function */
945   return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
946 }
947
948
949 svn_error_t *
950 svn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
951                             dag_node_t *file,
952                             apr_pool_t *pool)
953 {
954   node_revision_t *noderev;
955   svn_stream_t *contents;
956
957   /* Make sure our node is a file. */
958   if (file->kind != svn_node_file)
959     return svn_error_createf
960       (SVN_ERR_FS_NOT_FILE, NULL,
961        "Attempted to get textual contents of a *non*-file node");
962
963   /* Go get a fresh node-revision for FILE. */
964   SVN_ERR(get_node_revision(&noderev, file));
965
966   /* Get a stream to the contents. */
967   SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
968                                   noderev->data_rep, TRUE, pool));
969
970   *contents_p = contents;
971
972   return SVN_NO_ERROR;
973 }
974
975
976 svn_error_t *
977 svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
978                                      dag_node_t *source,
979                                      dag_node_t *target,
980                                      apr_pool_t *pool)
981 {
982   node_revision_t *src_noderev;
983   node_revision_t *tgt_noderev;
984
985   /* Make sure our nodes are files. */
986   if ((source && source->kind != svn_node_file)
987       || target->kind != svn_node_file)
988     return svn_error_createf
989       (SVN_ERR_FS_NOT_FILE, NULL,
990        "Attempted to get textual contents of a *non*-file node");
991
992   /* Go get fresh node-revisions for the nodes. */
993   if (source)
994     SVN_ERR(get_node_revision(&src_noderev, source));
995   else
996     src_noderev = NULL;
997   SVN_ERR(get_node_revision(&tgt_noderev, target));
998
999   /* Get the delta stream. */
1000   return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
1001                                           src_noderev, tgt_noderev, pool);
1002 }
1003
1004
1005 svn_error_t *
1006 svn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success,
1007                                          dag_node_t *node,
1008                                          svn_fs_process_contents_func_t processor,
1009                                          void* baton,
1010                                          apr_pool_t *pool)
1011 {
1012   node_revision_t *noderev;
1013
1014   /* Go get fresh node-revisions for the nodes. */
1015   SVN_ERR(get_node_revision(&noderev, node));
1016
1017   return svn_fs_fs__try_process_file_contents(success, node->fs,
1018                                               noderev,
1019                                               processor, baton, pool);
1020 }
1021
1022
1023 svn_error_t *
1024 svn_fs_fs__dag_file_length(svn_filesize_t *length,
1025                            dag_node_t *file,
1026                            apr_pool_t *pool)
1027 {
1028   node_revision_t *noderev;
1029
1030   /* Make sure our node is a file. */
1031   if (file->kind != svn_node_file)
1032     return svn_error_createf
1033       (SVN_ERR_FS_NOT_FILE, NULL,
1034        "Attempted to get length of a *non*-file node");
1035
1036   /* Go get a fresh node-revision for FILE, and . */
1037   SVN_ERR(get_node_revision(&noderev, file));
1038
1039   return svn_fs_fs__file_length(length, noderev, pool);
1040 }
1041
1042
1043 svn_error_t *
1044 svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
1045                              dag_node_t *file,
1046                              svn_checksum_kind_t kind,
1047                              apr_pool_t *pool)
1048 {
1049   node_revision_t *noderev;
1050
1051   if (file->kind != svn_node_file)
1052     return svn_error_createf
1053       (SVN_ERR_FS_NOT_FILE, NULL,
1054        "Attempted to get checksum of a *non*-file node");
1055
1056   SVN_ERR(get_node_revision(&noderev, file));
1057
1058   return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
1059 }
1060
1061
1062 svn_error_t *
1063 svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
1064                                dag_node_t *file,
1065                                apr_pool_t *pool)
1066 {
1067   node_revision_t *noderev;
1068   svn_stream_t *ws;
1069
1070   /* Make sure our node is a file. */
1071   if (file->kind != svn_node_file)
1072     return svn_error_createf
1073       (SVN_ERR_FS_NOT_FILE, NULL,
1074        "Attempted to set textual contents of a *non*-file node");
1075
1076   /* Make sure our node is mutable. */
1077   if (! svn_fs_fs__dag_check_mutable(file))
1078     return svn_error_createf
1079       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1080        "Attempted to set textual contents of an immutable node");
1081
1082   /* Get the node revision. */
1083   SVN_ERR(get_node_revision(&noderev, file));
1084
1085   SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
1086
1087   *contents = ws;
1088
1089   return SVN_NO_ERROR;
1090 }
1091
1092
1093
1094 svn_error_t *
1095 svn_fs_fs__dag_finalize_edits(dag_node_t *file,
1096                               const svn_checksum_t *checksum,
1097                               apr_pool_t *pool)
1098 {
1099   if (checksum)
1100     {
1101       svn_checksum_t *file_checksum;
1102
1103       SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
1104                                            checksum->kind, pool));
1105       if (!svn_checksum_match(checksum, file_checksum))
1106         return svn_checksum_mismatch_err(checksum, file_checksum, pool,
1107                                          _("Checksum mismatch for '%s'"),
1108                                          file->created_path);
1109     }
1110
1111   return SVN_NO_ERROR;
1112 }
1113
1114
1115 dag_node_t *
1116 svn_fs_fs__dag_dup(const dag_node_t *node,
1117                    apr_pool_t *pool)
1118 {
1119   /* Allocate our new node. */
1120   dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1121
1122   new_node->fs = node->fs;
1123   new_node->id = svn_fs_fs__id_copy(node->id, pool);
1124   new_node->kind = node->kind;
1125   new_node->created_path = apr_pstrdup(pool, node->created_path);
1126
1127   /* Only copy cached node_revision_t for immutable nodes. */
1128   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1129     {
1130       new_node->node_revision = copy_node_revision(node->node_revision, pool);
1131       new_node->node_revision->id =
1132           svn_fs_fs__id_copy(node->node_revision->id, pool);
1133       new_node->node_revision->is_fresh_txn_root =
1134           node->node_revision->is_fresh_txn_root;
1135     }
1136   new_node->node_pool = pool;
1137
1138   return new_node;
1139 }
1140
1141 svn_error_t *
1142 svn_fs_fs__dag_serialize(void **data,
1143                          apr_size_t *data_len,
1144                          void *in,
1145                          apr_pool_t *pool)
1146 {
1147   dag_node_t *node = in;
1148   svn_stringbuf_t *serialized;
1149
1150   /* create an serialization context and serialize the dag node as root */
1151   svn_temp_serializer__context_t *context =
1152       svn_temp_serializer__init(node,
1153                                 sizeof(*node),
1154                                 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1155                                 pool);
1156
1157   /* for mutable nodes, we will _never_ cache the noderev */
1158   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1159     svn_fs_fs__noderev_serialize(context, &node->node_revision);
1160   else
1161     svn_temp_serializer__set_null(context,
1162                                   (const void * const *)&node->node_revision);
1163
1164   /* The deserializer will use its own pool. */
1165   svn_temp_serializer__set_null(context,
1166                                 (const void * const *)&node->node_pool);
1167
1168   /* serialize other sub-structures */
1169   svn_fs_fs__id_serialize(context, (const svn_fs_id_t *const *)&node->id);
1170   svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
1171   svn_temp_serializer__add_string(context, &node->created_path);
1172
1173   /* return serialized data */
1174   serialized = svn_temp_serializer__get(context);
1175   *data = serialized->data;
1176   *data_len = serialized->len;
1177
1178   return SVN_NO_ERROR;
1179 }
1180
1181 svn_error_t *
1182 svn_fs_fs__dag_deserialize(void **out,
1183                            void *data,
1184                            apr_size_t data_len,
1185                            apr_pool_t *pool)
1186 {
1187   dag_node_t *node = (dag_node_t *)data;
1188   if (data_len == 0)
1189     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1190                             _("Empty noderev in cache"));
1191
1192   /* Copy the _full_ buffer as it also contains the sub-structures. */
1193   node->fs = NULL;
1194
1195   /* fixup all references to sub-structures */
1196   svn_fs_fs__id_deserialize(node, &node->id);
1197   svn_fs_fs__id_deserialize(node,
1198                             (svn_fs_id_t **)&node->fresh_root_predecessor_id);
1199   svn_fs_fs__noderev_deserialize(node, &node->node_revision);
1200   node->node_pool = pool;
1201
1202   svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1203
1204   /* return result */
1205   *out = node;
1206
1207   return SVN_NO_ERROR;
1208 }
1209
1210 svn_error_t *
1211 svn_fs_fs__dag_open(dag_node_t **child_p,
1212                     dag_node_t *parent,
1213                     const char *name,
1214                     apr_pool_t *result_pool,
1215                     apr_pool_t *scratch_pool)
1216 {
1217   const svn_fs_id_t *node_id;
1218
1219   /* Ensure that NAME exists in PARENT's entry list. */
1220   SVN_ERR(dir_entry_id_from_node(&node_id, parent, name,
1221                                  scratch_pool, scratch_pool));
1222   if (! node_id)
1223     {
1224       *child_p = NULL;
1225       return SVN_NO_ERROR;
1226     }
1227
1228   /* Make sure that NAME is a single path component. */
1229   if (! svn_path_is_single_path_component(name))
1230     return svn_error_createf
1231       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1232        "Attempted to open node with an illegal name '%s'", name);
1233
1234   /* Now get the node that was requested. */
1235   return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
1236                                  node_id, result_pool);
1237 }
1238
1239
1240 svn_error_t *
1241 svn_fs_fs__dag_copy(dag_node_t *to_node,
1242                     const char *entry,
1243                     dag_node_t *from_node,
1244                     svn_boolean_t preserve_history,
1245                     svn_revnum_t from_rev,
1246                     const char *from_path,
1247                     const svn_fs_fs__id_part_t *txn_id,
1248                     apr_pool_t *pool)
1249 {
1250   const svn_fs_id_t *id;
1251
1252   if (preserve_history)
1253     {
1254       node_revision_t *from_noderev, *to_noderev;
1255       svn_fs_fs__id_part_t copy_id;
1256       const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
1257       svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
1258
1259       /* Make a copy of the original node revision. */
1260       SVN_ERR(get_node_revision(&from_noderev, from_node));
1261       to_noderev = copy_node_revision(from_noderev, pool);
1262
1263       /* Reserve a copy ID for this new copy. */
1264       SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, fs, txn_id, pool));
1265
1266       /* Create a successor with its predecessor pointing at the copy
1267          source. */
1268       to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
1269       to_noderev->predecessor_count++;
1270       to_noderev->created_path =
1271         svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
1272                      pool);
1273       to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
1274       to_noderev->copyfrom_rev = from_rev;
1275
1276       /* Set the copyroot equal to our own id. */
1277       to_noderev->copyroot_path = NULL;
1278
1279       SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
1280                                           &copy_id, txn_id, pool));
1281
1282     }
1283   else  /* don't preserve history */
1284     {
1285       id = svn_fs_fs__dag_get_id(from_node);
1286     }
1287
1288   /* Set the entry in to_node to the new id. */
1289   return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
1290                                   txn_id, pool);
1291 }
1292
1293
1294 \f
1295 /*** Comparison. ***/
1296
1297 svn_error_t *
1298 svn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
1299                                 svn_boolean_t *contents_changed,
1300                                 dag_node_t *node1,
1301                                 dag_node_t *node2,
1302                                 svn_boolean_t strict,
1303                                 apr_pool_t *pool)
1304 {
1305   node_revision_t *noderev1, *noderev2;
1306
1307   /* If we have no place to store our results, don't bother doing
1308      anything. */
1309   if (! props_changed && ! contents_changed)
1310     return SVN_NO_ERROR;
1311
1312   /* The node revision skels for these two nodes. */
1313   SVN_ERR(get_node_revision(&noderev1, node1));
1314   SVN_ERR(get_node_revision(&noderev2, node2));
1315
1316   if (strict)
1317     {
1318       /* In strict mode, compare text and property representations in the
1319          svn_fs_contents_different() / svn_fs_props_different() manner.
1320
1321          See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9"
1322          discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and
1323          issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */
1324       svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1);
1325       svn_boolean_t same;
1326
1327       /* Compare property keys. */
1328       if (props_changed != NULL)
1329         {
1330           SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1,
1331                                             noderev2, pool));
1332           *props_changed = !same;
1333         }
1334
1335       /* Compare contents keys. */
1336       if (contents_changed != NULL)
1337         {
1338           SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1,
1339                                                  noderev2, pool));
1340           *contents_changed = !same;
1341         }
1342     }
1343   else
1344     {
1345       /* Otherwise, compare representation keys -- as in Subversion 1.8. */
1346
1347       /* Compare property keys. */
1348       if (props_changed != NULL)
1349         *props_changed =
1350           !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
1351                                            noderev2->prop_rep);
1352
1353       /* Compare contents keys. */
1354       if (contents_changed != NULL)
1355         *contents_changed =
1356           !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
1357                                            noderev2->data_rep);
1358     }
1359
1360   return SVN_NO_ERROR;
1361 }
1362
1363 svn_error_t *
1364 svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
1365                             const char **path,
1366                             dag_node_t *node)
1367 {
1368   node_revision_t *noderev;
1369
1370   /* Go get a fresh node-revision for NODE. */
1371   SVN_ERR(get_node_revision(&noderev, node));
1372
1373   *rev = noderev->copyroot_rev;
1374   *path = noderev->copyroot_path;
1375
1376   return SVN_NO_ERROR;
1377 }
1378
1379 svn_error_t *
1380 svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
1381                                 dag_node_t *node)
1382 {
1383   node_revision_t *noderev;
1384
1385   /* Go get a fresh node-revision for NODE. */
1386   SVN_ERR(get_node_revision(&noderev, node));
1387
1388   *rev = noderev->copyfrom_rev;
1389
1390   return SVN_NO_ERROR;
1391 }
1392
1393 svn_error_t *
1394 svn_fs_fs__dag_get_copyfrom_path(const char **path,
1395                                  dag_node_t *node)
1396 {
1397   node_revision_t *noderev;
1398
1399   /* Go get a fresh node-revision for NODE. */
1400   SVN_ERR(get_node_revision(&noderev, node));
1401
1402   *path = noderev->copyfrom_path;
1403
1404   return SVN_NO_ERROR;
1405 }
1406
1407 svn_error_t *
1408 svn_fs_fs__dag_update_ancestry(dag_node_t *target,
1409                                dag_node_t *source,
1410                                apr_pool_t *pool)
1411 {
1412   node_revision_t *source_noderev, *target_noderev;
1413
1414   if (! svn_fs_fs__dag_check_mutable(target))
1415     return svn_error_createf
1416       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1417        _("Attempted to update ancestry of non-mutable node"));
1418
1419   SVN_ERR(get_node_revision(&source_noderev, source));
1420   SVN_ERR(get_node_revision(&target_noderev, target));
1421
1422   target_noderev->predecessor_id = source->id;
1423   target_noderev->predecessor_count = source_noderev->predecessor_count;
1424   target_noderev->predecessor_count++;
1425
1426   return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,
1427                                       FALSE, pool);
1428 }