2 * node_tree.c: an editor for tracking repository deltas changes
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
27 #define APR_WANT_STRFUNC
29 #include <apr_pools.h>
31 #include "svn_types.h"
32 #include "svn_error.h"
34 #include "svn_delta.h"
36 #include "svn_repos.h"
38 #include "svn_private_config.h"
40 /*** NOTE: This editor is unique in that it currently is hard-coded to
41 be anchored at the root directory of the filesystem. This
42 affords us the ability to use the same paths for filesystem
43 locations and editor paths. ***/
47 /*** Node creation and assembly structures and routines. ***/
48 static svn_repos_node_t
*
49 create_node(const char *name
,
50 svn_repos_node_t
*parent
,
53 svn_repos_node_t
*node
= apr_pcalloc(pool
, sizeof(*node
));
55 node
->kind
= svn_node_unknown
;
56 node
->name
= apr_pstrdup(pool
, name
);
57 node
->parent
= parent
;
62 static svn_repos_node_t
*
63 create_sibling_node(svn_repos_node_t
*elder
,
67 svn_repos_node_t
*tmp_node
;
69 /* No ELDER sibling? That's just not gonna work out. */
73 /* Run to the end of the list of siblings of ELDER. */
75 while (tmp_node
->sibling
)
76 tmp_node
= tmp_node
->sibling
;
78 /* Create a new youngest sibling and return that. */
79 return (tmp_node
->sibling
= create_node(name
, elder
->parent
, pool
));
83 static svn_repos_node_t
*
84 create_child_node(svn_repos_node_t
*parent
,
88 /* No PARENT node? That's just not gonna work out. */
92 /* If PARENT has no children, create its first one and return that. */
94 return (parent
->child
= create_node(name
, parent
, pool
));
96 /* If PARENT already has a child, create a new sibling for its first
97 child and return that. */
98 return create_sibling_node(parent
->child
, name
, pool
);
102 static svn_repos_node_t
*
103 find_child_by_name(svn_repos_node_t
*parent
,
106 svn_repos_node_t
*tmp_node
;
108 /* No PARENT node, or a barren PARENT? Nothing to find. */
109 if ((! parent
) || (! parent
->child
))
112 /* Look through the children for a node with a matching name. */
113 tmp_node
= parent
->child
;
116 if (! strcmp(tmp_node
->name
, name
))
122 if (tmp_node
->sibling
)
123 tmp_node
= tmp_node
->sibling
;
134 find_real_base_location(const char **path_p
,
136 svn_repos_node_t
*node
,
139 /* If NODE is an add-with-history, then its real base location is
141 if ((node
->action
== 'A')
142 && node
->copyfrom_path
143 && SVN_IS_VALID_REVNUM(node
->copyfrom_rev
))
145 *path_p
= node
->copyfrom_path
;
146 *rev_p
= node
->copyfrom_rev
;
150 /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's
151 name to whatever the parent's real base path turns out to be (and
152 pass the base revision on through). */
158 find_real_base_location(&path
, &rev
, node
->parent
, pool
);
159 *path_p
= svn_path_join(path
, node
->name
, pool
);
164 /* Finally, if the node has no parent, then its name is "/", and it
165 has no interesting base revision. */
167 *rev_p
= SVN_INVALID_REVNUM
;
174 /*** Editor functions and batons. ***/
180 svn_fs_root_t
*base_root
;
181 apr_pool_t
*node_pool
;
182 svn_repos_node_t
*node
;
188 struct edit_baton
*edit_baton
;
189 struct node_baton
*parent_baton
;
190 svn_repos_node_t
*node
;
195 delete_entry(const char *path
,
196 svn_revnum_t revision
,
200 struct node_baton
*d
= parent_baton
;
201 struct edit_baton
*eb
= d
->edit_baton
;
202 svn_repos_node_t
*node
;
204 const char *base_path
;
205 svn_revnum_t base_rev
;
206 svn_fs_root_t
*base_root
;
207 svn_node_kind_t kind
;
209 /* Get (or create) the change node and update it. */
210 name
= svn_path_basename(path
, pool
);
211 node
= find_child_by_name(d
->node
, name
);
213 node
= create_child_node(d
->node
, name
, eb
->node_pool
);
216 /* We need to look up this node's parents to see what its original
217 path in the filesystem was. Why? Because if this deletion
218 occurred underneath a copied path, the thing that was deleted
219 probably lived at a different location (relative to the copy
221 find_real_base_location(&base_path
, &base_rev
, node
, pool
);
222 if (! SVN_IS_VALID_REVNUM(base_rev
))
224 /* No interesting base revision? We'll just look for the path
226 base_root
= eb
->base_root
;
230 /* Oh. Perhaps some copy goodness happened somewhere? */
231 SVN_ERR(svn_fs_revision_root(&base_root
, eb
->fs
, base_rev
, pool
));
234 /* Now figure out if this thing was a file or a dir. */
235 SVN_ERR(svn_fs_check_path(&kind
, base_root
, base_path
, pool
));
236 if (kind
== svn_node_none
)
237 return svn_error_createf(SVN_ERR_FS_NOT_FOUND
, NULL
,
238 _("'%s' not found in filesystem"), path
);
247 add_open_helper(const char *path
,
249 svn_node_kind_t kind
,
251 const char *copyfrom_path
,
252 svn_revnum_t copyfrom_rev
,
256 struct node_baton
*pb
= parent_baton
;
257 struct edit_baton
*eb
= pb
->edit_baton
;
258 struct node_baton
*nb
= apr_pcalloc(pool
, sizeof(*nb
));
260 assert(parent_baton
&& path
);
263 nb
->parent_baton
= pb
;
265 /* Create and populate the node. */
266 nb
->node
= create_child_node(pb
->node
, svn_path_basename(path
, pool
),
268 nb
->node
->kind
= kind
;
269 nb
->node
->action
= action
;
270 nb
->node
->copyfrom_rev
= copyfrom_rev
;
271 nb
->node
->copyfrom_path
=
272 copyfrom_path
? apr_pstrdup(eb
->node_pool
, copyfrom_path
) : NULL
;
280 open_root(void *edit_baton
,
281 svn_revnum_t base_revision
,
285 struct edit_baton
*eb
= edit_baton
;
286 struct node_baton
*d
= apr_pcalloc(pool
, sizeof(*d
));
289 d
->parent_baton
= NULL
;
290 d
->node
= (eb
->node
= create_node("", NULL
, eb
->node_pool
));
291 d
->node
->kind
= svn_node_dir
;
292 d
->node
->action
= 'R';
300 open_directory(const char *path
,
302 svn_revnum_t base_revision
,
306 SVN_ERR(add_open_helper(path
, 'R', svn_node_dir
, parent_baton
,
307 NULL
, SVN_INVALID_REVNUM
,
314 add_directory(const char *path
,
316 const char *copyfrom_path
,
317 svn_revnum_t copyfrom_revision
,
321 SVN_ERR(add_open_helper(path
, 'A', svn_node_dir
, parent_baton
,
322 copyfrom_path
, copyfrom_revision
,
329 open_file(const char *path
,
331 svn_revnum_t base_revision
,
335 SVN_ERR(add_open_helper(path
, 'R', svn_node_file
, parent_baton
,
336 NULL
, SVN_INVALID_REVNUM
,
343 add_file(const char *path
,
345 const char *copyfrom_path
,
346 svn_revnum_t copyfrom_revision
,
350 SVN_ERR(add_open_helper(path
, 'A', svn_node_file
, parent_baton
,
351 copyfrom_path
, copyfrom_revision
,
358 apply_textdelta(void *file_baton
,
359 const char *base_checksum
,
361 svn_txdelta_window_handler_t
*handler
,
362 void **handler_baton
)
364 struct node_baton
*fb
= file_baton
;
365 fb
->node
->text_mod
= TRUE
;
366 *handler
= svn_delta_noop_window_handler
;
367 *handler_baton
= NULL
;
374 change_node_prop(void *node_baton
,
376 const svn_string_t
*value
,
379 struct node_baton
*nb
= node_baton
;
380 nb
->node
->prop_mod
= TRUE
;
386 svn_repos_node_editor(const svn_delta_editor_t
**editor
,
389 svn_fs_root_t
*base_root
,
391 apr_pool_t
*node_pool
,
394 svn_delta_editor_t
*my_editor
;
395 struct edit_baton
*my_edit_baton
;
397 /* Set up the editor. */
398 my_editor
= svn_delta_default_editor(pool
);
399 my_editor
->open_root
= open_root
;
400 my_editor
->delete_entry
= delete_entry
;
401 my_editor
->add_directory
= add_directory
;
402 my_editor
->open_directory
= open_directory
;
403 my_editor
->add_file
= add_file
;
404 my_editor
->open_file
= open_file
;
405 my_editor
->apply_textdelta
= apply_textdelta
;
406 my_editor
->change_file_prop
= change_node_prop
;
407 my_editor
->change_dir_prop
= change_node_prop
;
409 /* Set up the edit baton. */
410 my_edit_baton
= apr_pcalloc(pool
, sizeof(*my_edit_baton
));
411 my_edit_baton
->node_pool
= node_pool
;
412 my_edit_baton
->fs
= repos
->fs
;
413 my_edit_baton
->root
= root
;
414 my_edit_baton
->base_root
= base_root
;
417 *edit_baton
= my_edit_baton
;
425 svn_repos_node_from_baton(void *edit_baton
)
427 struct edit_baton
*eb
= edit_baton
;