]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/subversion/libsvn_wc/update_editor.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / subversion / subversion / libsvn_wc / update_editor.c
1 /*
2  * update_editor.c :  main editor for checkouts and updates
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <apr_pools.h>
30 #include <apr_hash.h>
31 #include <apr_md5.h>
32 #include <apr_tables.h>
33 #include <apr_strings.h>
34
35 #include "svn_types.h"
36 #include "svn_pools.h"
37 #include "svn_hash.h"
38 #include "svn_string.h"
39 #include "svn_dirent_uri.h"
40 #include "svn_path.h"
41 #include "svn_error.h"
42 #include "svn_io.h"
43 #include "svn_private_config.h"
44 #include "svn_time.h"
45
46 #include "wc.h"
47 #include "adm_files.h"
48 #include "conflicts.h"
49 #include "translate.h"
50 #include "workqueue.h"
51
52 #include "private/svn_subr_private.h"
53 #include "private/svn_wc_private.h"
54 #include "private/svn_editor.h"
55
56 /* Checks whether a svn_wc__db_status_t indicates whether a node is
57    present in a working copy. Used by the editor implementation */
58 #define IS_NODE_PRESENT(status)                             \
59            ((status) != svn_wc__db_status_server_excluded &&\
60             (status) != svn_wc__db_status_excluded &&       \
61             (status) != svn_wc__db_status_not_present)
62
63 static svn_error_t *
64 path_join_under_root(const char **result_path,
65                      const char *base_path,
66                      const char *add_path,
67                      apr_pool_t *result_pool);
68
69 \f
70 /*
71  * This code handles "checkout" and "update" and "switch".
72  * A checkout is similar to an update that is only adding new items.
73  *
74  * The intended behaviour of "update" and "switch", focusing on the checks
75  * to be made before applying a change, is:
76  *
77  *   For each incoming change:
78  *     if target is already in conflict or obstructed:
79  *       skip this change
80  *     else
81  *     if this action will cause a tree conflict:
82  *       record the tree conflict
83  *       skip this change
84  *     else:
85  *       make this change
86  *
87  * In more detail:
88  *
89  *   For each incoming change:
90  *
91  *   1.   if  # Incoming change is inside an item already in conflict:
92  *    a.    tree/text/prop change to node beneath tree-conflicted dir
93  *        then  # Skip all changes in this conflicted subtree [*1]:
94  *          do not update the Base nor the Working
95  *          notify "skipped because already in conflict" just once
96  *            for the whole conflicted subtree
97  *
98  *        if  # Incoming change affects an item already in conflict:
99  *    b.    tree/text/prop change to tree-conflicted dir/file, or
100  *    c.    tree change to a text/prop-conflicted file/dir, or
101  *    d.    text/prop change to a text/prop-conflicted file/dir [*2], or
102  *    e.    tree change to a dir tree containing any conflicts,
103  *        then  # Skip this change [*1]:
104  *          do not update the Base nor the Working
105  *          notify "skipped because already in conflict"
106  *
107  *   2.   if  # Incoming change affects an item that's "obstructed":
108  *    a.    on-disk node kind doesn't match recorded Working node kind
109  *            (including an absence/presence mis-match),
110  *        then  # Skip this change [*1]:
111  *          do not update the Base nor the Working
112  *          notify "skipped because obstructed"
113  *
114  *   3.   if  # Incoming change raises a tree conflict:
115  *    a.    tree/text/prop change to node beneath sched-delete dir, or
116  *    b.    tree/text/prop change to sched-delete dir/file, or
117  *    c.    text/prop change to tree-scheduled dir/file,
118  *        then  # Skip this change:
119  *          do not update the Base nor the Working [*3]
120  *          notify "tree conflict"
121  *
122  *   4.   Apply the change:
123  *          update the Base
124  *          update the Working, possibly raising text/prop conflicts
125  *          notify
126  *
127  * Notes:
128  *
129  *      "Tree change" here refers to an add or delete of the target node,
130  *      including the add or delete part of a copy or move or rename.
131  *
132  * [*1] We should skip changes to an entire node, as the base revision number
133  *      applies to the entire node. Not sure how this affects attempts to
134  *      handle text and prop changes separately.
135  *
136  * [*2] Details of which combinations of property and text changes conflict
137  *      are not specified here.
138  *
139  * [*3] For now, we skip the update, and require the user to:
140  *        - Modify the WC to be compatible with the incoming change;
141  *        - Mark the conflict as resolved;
142  *        - Repeat the update.
143  *      Ideally, it would be possible to resolve any conflict without
144  *      repeating the update. To achieve this, we would have to store the
145  *      necessary data at conflict detection time, and delay the update of
146  *      the Base until the time of resolving.
147  */
148
149 \f
150 /*** batons ***/
151
152 struct edit_baton
153 {
154   /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
155      directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
156      target, the values are identical.
157
158      TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
159      ANCHOR_ABSPATH is the target */
160   const char *target_basename;
161
162   /* Absolute variants of ANCHOR and TARGET */
163   const char *anchor_abspath;
164   const char *target_abspath;
165
166   /* The DB handle for managing the working copy state.  */
167   svn_wc__db_t *db;
168
169   /* Array of file extension patterns to preserve as extensions in
170      generated conflict files. */
171   const apr_array_header_t *ext_patterns;
172
173   /* Hash mapping const char * absolute working copy paths to depth-first
174      ordered arrays of svn_prop_inherited_item_t * structures representing
175      the properties inherited by the base node at that working copy path.
176      May be NULL. */
177   apr_hash_t *wcroot_iprops;
178
179   /* The revision we're targeting...or something like that.  This
180      starts off as a pointer to the revision to which we are updating,
181      or SVN_INVALID_REVNUM, but by the end of the edit, should be
182      pointing to the final revision. */
183   svn_revnum_t *target_revision;
184
185   /* The requested depth of this edit. */
186   svn_depth_t requested_depth;
187
188   /* Is the requested depth merely an operational limitation, or is
189      also the new sticky ambient depth of the update target? */
190   svn_boolean_t depth_is_sticky;
191
192   /* Need to know if the user wants us to overwrite the 'now' times on
193      edited/added files with the last-commit-time. */
194   svn_boolean_t use_commit_times;
195
196   /* Was the root actually opened (was this a non-empty edit)? */
197   svn_boolean_t root_opened;
198
199   /* Was the update-target deleted?  This is a special situation. */
200   svn_boolean_t target_deleted;
201
202   /* Allow unversioned obstructions when adding a path. */
203   svn_boolean_t allow_unver_obstructions;
204
205   /* Handle local additions as modifications of new nodes */
206   svn_boolean_t adds_as_modification;
207
208   /* If set, we check out into an empty directory. This allows for a number
209      of conflict checks to be omitted. */
210   svn_boolean_t clean_checkout;
211
212   /* If this is a 'switch' operation, the new relpath of target_abspath,
213      else NULL. */
214   const char *switch_relpath;
215
216   /* The URL to the root of the repository. */
217   const char *repos_root;
218
219   /* The UUID of the repos, or NULL. */
220   const char *repos_uuid;
221
222   /* External diff3 to use for merges (can be null, in which case
223      internal merge code is used). */
224   const char *diff3_cmd;
225
226   /* Externals handler */
227   svn_wc_external_update_t external_func;
228   void *external_baton;
229
230   /* This editor sends back notifications as it edits. */
231   svn_wc_notify_func2_t notify_func;
232   void *notify_baton;
233
234   /* This editor is normally wrapped in a cancellation editor anyway,
235      so it doesn't bother to check for cancellation itself.  However,
236      it needs a cancel_func and cancel_baton available to pass to
237      long-running functions. */
238   svn_cancel_func_t cancel_func;
239   void *cancel_baton;
240
241   /* This editor will invoke a interactive conflict-resolution
242      callback, if available. */
243   svn_wc_conflict_resolver_func2_t conflict_func;
244   void *conflict_baton;
245
246   /* Subtrees that were skipped during the edit, and therefore shouldn't
247      have their revision/url info updated at the end.  If a path is a
248      directory, its descendants will also be skipped.  The keys are paths
249      relative to the working copy root and the values unspecified. */
250   apr_hash_t *skipped_trees;
251
252   /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
253      returned from fetch_dirents_func for that repos_relpath. These
254      are used to avoid issue #3569 in specific update scenarios where a
255      restricted depth is used. */
256   apr_hash_t *dir_dirents;
257
258   /* Absolute path of the working copy root or NULL if not initialized yet */
259   const char *wcroot_abspath;
260
261   apr_pool_t *pool;
262 };
263
264
265 /* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
266  * updated.
267  *
268  * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
269  * LOCAL_ABSPATH.
270  */
271 static svn_error_t *
272 remember_skipped_tree(struct edit_baton *eb,
273                       const char *local_abspath,
274                       apr_pool_t *scratch_pool)
275 {
276   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
277
278   svn_hash_sets(eb->skipped_trees,
279                 apr_pstrdup(eb->pool,
280                             svn_dirent_skip_ancestor(eb->wcroot_abspath,
281                                                      local_abspath)),
282                 (void *)1);
283
284   return SVN_NO_ERROR;
285 }
286
287 /* Per directory baton. Lives in its own subpool of the parent directory
288    or of the edit baton if there is no parent directory */
289 struct dir_baton
290 {
291   /* Basename of this directory. */
292   const char *name;
293
294   /* Absolute path of this directory */
295   const char *local_abspath;
296
297   /* The repository relative path this directory will correspond to. */
298   const char *new_relpath;
299
300   /* The revision of the directory before updating */
301   svn_revnum_t old_revision;
302
303   /* The repos_relpath before updating/switching */
304   const char *old_repos_relpath;
305
306   /* The global edit baton. */
307   struct edit_baton *edit_baton;
308
309   /* Baton for this directory's parent, or NULL if this is the root
310      directory. */
311   struct dir_baton *parent_baton;
312
313   /* Set if updates to this directory are skipped */
314   svn_boolean_t skip_this;
315
316   /* Set if there was a previous notification for this directory */
317   svn_boolean_t already_notified;
318
319   /* Set if this directory is being added during this editor drive. */
320   svn_boolean_t adding_dir;
321
322   /* Set on a node and its descendants are not present in the working copy
323      but should still be updated (not skipped). These nodes should all be
324      marked as deleted. */
325   svn_boolean_t shadowed;
326
327   /* Set on a node when the existing node is obstructed, and the edit operation
328      continues as semi-shadowed update */
329   svn_boolean_t edit_obstructed;
330
331   /* The (new) changed_* information, cached to avoid retrieving it later */
332   svn_revnum_t changed_rev;
333   apr_time_t changed_date;
334   const char *changed_author;
335
336   /* If not NULL, contains a mapping of const char* basenames of children that
337      have been deleted to their svn_skel_t* tree conflicts.
338      We store this hash to allow replacements to continue under a just
339      installed tree conflict.
340
341      The add after the delete will then update the tree conflicts information
342      and reinstall it. */
343   apr_hash_t *deletion_conflicts;
344
345   /* A hash of file names (only the hash key matters) seen by add_file
346      and not yet added to the database by close_file. */
347   apr_hash_t *not_present_files;
348
349   /* Set if an unversioned dir of the same name already existed in
350      this directory. */
351   svn_boolean_t obstruction_found;
352
353   /* Set if a dir of the same name already exists and is
354      scheduled for addition without history. */
355   svn_boolean_t add_existed;
356
357   /* An array of svn_prop_t structures, representing all the property
358      changes to be applied to this directory. */
359   apr_array_header_t *propchanges;
360
361   /* A boolean indicating whether this node or one of its children has
362      received any 'real' changes. Used to avoid tree conflicts for simple
363      entryprop changes, like lock management */
364   svn_boolean_t edited;
365
366   /* The tree conflict to install once the node is really edited */
367   svn_skel_t *edit_conflict;
368
369   /* The bump information for this directory. */
370   struct bump_dir_info *bump_info;
371
372   /* The depth of the directory in the wc (or inferred if added).  Not
373      used for filtering; we have a separate wrapping editor for that. */
374   svn_depth_t ambient_depth;
375
376   /* Was the directory marked as incomplete before the update?
377      (In other words, are we resuming an interrupted update?)
378
379      If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
380      and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
381      we only receive the changes in/for children and properties.*/
382   svn_boolean_t was_incomplete;
383
384   /* The pool in which this baton itself is allocated. */
385   apr_pool_t *pool;
386
387   /* how many nodes are referring to baton? */
388   int ref_count;
389
390 };
391
392
393 struct handler_baton
394 {
395   svn_txdelta_window_handler_t apply_handler;
396   void *apply_baton;
397   apr_pool_t *pool;
398   struct file_baton *fb;
399
400   /* Where we are assembling the new file. */
401   const char *new_text_base_tmp_abspath;
402
403     /* The expected source checksum of the text source or NULL if no base
404      checksum is available (MD5 if the server provides a checksum, SHA1 if
405      the server doesn't) */
406   svn_checksum_t *expected_source_checksum;
407
408   /* Why two checksums?
409      The editor currently provides an md5 which we use to detect corruption
410      during transmission.  We use the sha1 inside libsvn_wc both for pristine
411      handling and corruption detection.  In the future, the editor will also
412      provide a sha1, so we may not have to calculate both, but for the time
413      being, that's the way it is. */
414
415   /* The calculated checksum of the text source or NULL if the acual
416      checksum is not being calculated. The checksum kind is identical to the
417      kind of expected_source_checksum. */
418   svn_checksum_t *actual_source_checksum;
419
420   /* The stream used to calculate the source checksums */
421   svn_stream_t *source_checksum_stream;
422
423   /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
424      This is initialized to all zeroes when the baton is created, then
425      populated with the MD5 digest of the resultant fulltext after the
426      last window is handled by the handler returned from
427      apply_textdelta(). */
428   unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
429
430   /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
431      eventually writing the pristine. */
432   svn_checksum_t * new_text_base_sha1_checksum;
433 };
434
435
436 /* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
437    not be set for automatic deletion, and the name will be returned in
438    TMP_FILENAME.
439
440    This implementation creates a new empty file with a unique name.
441
442    ### This is inefficient for callers that just want an empty file to read
443    ### from.  There could be (and there used to be) a permanent, shared
444    ### empty file for this purpose.
445
446    ### This is inefficient for callers that just want to reserve a unique
447    ### file name to create later.  A better way may not be readily available.
448  */
449 static svn_error_t *
450 get_empty_tmp_file(const char **tmp_filename,
451                    svn_wc__db_t *db,
452                    const char *wri_abspath,
453                    apr_pool_t *result_pool,
454                    apr_pool_t *scratch_pool)
455 {
456   const char *temp_dir_abspath;
457
458   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
459                                          scratch_pool, scratch_pool));
460   SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
461                                    svn_io_file_del_none,
462                                    scratch_pool, scratch_pool));
463
464   return SVN_NO_ERROR;
465 }
466
467 /* An APR pool cleanup handler.  This runs the working queue for an
468    editor baton. */
469 static apr_status_t
470 cleanup_edit_baton(void *edit_baton)
471 {
472   struct edit_baton *eb = edit_baton;
473   svn_error_t *err;
474   apr_pool_t *pool = apr_pool_parent_get(eb->pool);
475
476   err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
477                        NULL /* cancel_func */, NULL /* cancel_baton */,
478                        pool);
479
480   if (err)
481     {
482       apr_status_t apr_err = err->apr_err;
483       svn_error_clear(err);
484       return apr_err;
485     }
486   return APR_SUCCESS;
487 }
488
489 /* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
490    If PATH and PB are NULL, this is the root directory of the edit; in this
491    case, make the new dir baton in a subpool of EB->pool.
492    ADDING should be TRUE if we are adding this directory.  */
493 static svn_error_t *
494 make_dir_baton(struct dir_baton **d_p,
495                const char *path,
496                struct edit_baton *eb,
497                struct dir_baton *pb,
498                svn_boolean_t adding,
499                apr_pool_t *scratch_pool)
500 {
501   apr_pool_t *dir_pool;
502   struct dir_baton *d;
503
504   if (pb != NULL)
505     dir_pool = svn_pool_create(pb->pool);
506   else
507     dir_pool = svn_pool_create(eb->pool);
508
509   SVN_ERR_ASSERT(path || (! pb));
510
511   /* Okay, no easy out, so allocate and initialize a dir baton. */
512   d = apr_pcalloc(dir_pool, sizeof(*d));
513
514   /* Construct the PATH and baseNAME of this directory. */
515   if (path)
516     {
517       d->name = svn_dirent_basename(path, dir_pool);
518       SVN_ERR(path_join_under_root(&d->local_abspath,
519                                    pb->local_abspath, d->name, dir_pool));
520     }
521   else
522     {
523       /* This is the root baton. */
524       d->name = NULL;
525       d->local_abspath = eb->anchor_abspath;
526     }
527
528   /* Figure out the new_relpath for this directory. */
529   if (eb->switch_relpath)
530     {
531       /* Handle switches... */
532
533       if (pb == NULL)
534         {
535           if (*eb->target_basename == '\0')
536             {
537               /* No parent baton and target_basename=="" means that we are
538                  the target of the switch. Thus, our NEW_RELPATH will be
539                  the SWITCH_RELPATH.  */
540               d->new_relpath = eb->switch_relpath;
541             }
542           else
543             {
544               /* This node is NOT the target of the switch (one of our
545                  children is the target); therefore, it must already exist.
546                  Get its old REPOS_RELPATH, as it won't be changing.  */
547               SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
548                                                  eb->db, d->local_abspath,
549                                                  dir_pool, scratch_pool));
550             }
551         }
552       else
553         {
554           /* This directory is *not* the root (has a parent). If there is
555              no grandparent, then we may have anchored at the parent,
556              and self is the target. If we match the target, then set
557              NEW_RELPATH to the SWITCH_RELPATH.
558
559              Otherwise, we simply extend NEW_RELPATH from the parent.  */
560           if (pb->parent_baton == NULL
561               && strcmp(eb->target_basename, d->name) == 0)
562             d->new_relpath = eb->switch_relpath;
563           else
564             d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
565                                               dir_pool);
566         }
567     }
568   else  /* must be an update */
569     {
570       /* If we are adding the node, then simply extend the parent's
571          relpath for our own.  */
572       if (adding)
573         {
574           SVN_ERR_ASSERT(pb != NULL);
575           d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
576                                             dir_pool);
577         }
578       else
579         {
580           SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
581                                              eb->db, d->local_abspath,
582                                              dir_pool, scratch_pool));
583           SVN_ERR_ASSERT(d->new_relpath);
584         }
585     }
586
587   d->edit_baton   = eb;
588   d->parent_baton = pb;
589   d->pool         = dir_pool;
590   d->propchanges  = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
591   d->obstruction_found = FALSE;
592   d->add_existed  = FALSE;
593   d->ref_count = 1;
594   d->old_revision = SVN_INVALID_REVNUM;
595   d->adding_dir   = adding;
596   d->changed_rev  = SVN_INVALID_REVNUM;
597   d->not_present_files = apr_hash_make(dir_pool);
598
599   /* Copy some flags from the parent baton */
600   if (pb)
601     {
602       d->skip_this = pb->skip_this;
603       d->shadowed = pb->shadowed || pb->edit_obstructed;
604
605       /* the parent's bump info has one more referer */
606       pb->ref_count++;
607     }
608
609   /* The caller of this function needs to fill these in. */
610   d->ambient_depth = svn_depth_unknown;
611   d->was_incomplete = FALSE;
612
613   *d_p = d;
614   return SVN_NO_ERROR;
615 }
616
617
618 /* Forward declarations. */
619 static svn_error_t *
620 already_in_a_tree_conflict(svn_boolean_t *conflicted,
621                            svn_boolean_t *ignored,
622                            svn_wc__db_t *db,
623                            const char *local_abspath,
624                            apr_pool_t *scratch_pool);
625
626
627 static void
628 do_notification(const struct edit_baton *eb,
629                 const char *local_abspath,
630                 svn_node_kind_t kind,
631                 svn_wc_notify_action_t action,
632                 apr_pool_t *scratch_pool)
633 {
634   svn_wc_notify_t *notify;
635
636   if (eb->notify_func == NULL)
637     return;
638
639   notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
640   notify->kind = kind;
641
642   (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
643 }
644
645 /* Decrement the directory's reference count. If it hits zero,
646    then this directory is "done". This means it is safe to clear its pool.
647
648    In addition, when the directory is "done", we recurse to possible cleanup
649    the parent directory.
650 */
651 static svn_error_t *
652 maybe_release_dir_info(struct dir_baton *db)
653 {
654   db->ref_count--;
655
656   if (!db->ref_count)
657     {
658       struct dir_baton *pb = db->parent_baton;
659
660       svn_pool_destroy(db->pool);
661
662       if (pb)
663         SVN_ERR(maybe_release_dir_info(pb));
664     }
665
666   return SVN_NO_ERROR;
667 }
668
669 /* Per file baton. Lives in its own subpool below the pool of the parent
670    directory */
671 struct file_baton
672 {
673   /* Pool specific to this file_baton. */
674   apr_pool_t *pool;
675
676   /* Name of this file (its entry in the directory). */
677   const char *name;
678
679   /* Absolute path to this file */
680   const char *local_abspath;
681
682   /* The repository relative path this file will correspond to. */
683   const char *new_relpath;
684
685   /* The revision of the file before updating */
686   svn_revnum_t old_revision;
687
688   /* The repos_relpath before updating/switching */
689   const char *old_repos_relpath;
690
691   /* The global edit baton. */
692   struct edit_baton *edit_baton;
693
694   /* The parent directory of this file. */
695   struct dir_baton *dir_baton;
696
697   /* Set if updates to this directory are skipped */
698   svn_boolean_t skip_this;
699
700   /* Set if there was a previous notification  */
701   svn_boolean_t already_notified;
702
703   /* Set if this file is new. */
704   svn_boolean_t adding_file;
705
706   /* Set if an unversioned file of the same name already existed in
707      this directory. */
708   svn_boolean_t obstruction_found;
709
710   /* Set if a file of the same name already exists and is
711      scheduled for addition without history. */
712   svn_boolean_t add_existed;
713
714   /* Set if this file is being added in the BASE layer, but is not-present
715      in the working copy (replaced, deleted, etc.). */
716   svn_boolean_t shadowed;
717
718   /* Set on a node when the existing node is obstructed, and the edit operation
719      continues as semi-shadowed update */
720   svn_boolean_t edit_obstructed;
721
722   /* The (new) changed_* information, cached to avoid retrieving it later */
723   svn_revnum_t changed_rev;
724   apr_time_t changed_date;
725   const char *changed_author;
726
727   /* If there are file content changes, these are the checksums of the
728      resulting new text base, which is in the pristine store, else NULL. */
729   const svn_checksum_t *new_text_base_md5_checksum;
730   const svn_checksum_t *new_text_base_sha1_checksum;
731
732   /* The checksum of the file before the update */
733   const svn_checksum_t *original_checksum;
734
735   /* An array of svn_prop_t structures, representing all the property
736      changes to be applied to this file.  Once a file baton is
737      initialized, this is never NULL, but it may have zero elements.  */
738   apr_array_header_t *propchanges;
739
740   /* For existing files, whether there are local modifications. FALSE for added
741      files */
742   svn_boolean_t local_prop_mods;
743
744   /* Bump information for the directory this file lives in */
745   struct bump_dir_info *bump_info;
746
747   /* A boolean indicating whether this node or one of its children has
748      received any 'real' changes. Used to avoid tree conflicts for simple
749      entryprop changes, like lock management */
750   svn_boolean_t edited;
751
752   /* The tree conflict to install once the node is really edited */
753   svn_skel_t *edit_conflict;
754 };
755
756
757 /* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
758  * PATH is relative to the root of the edit. ADDING tells whether this file
759  * is being added. */
760 static svn_error_t *
761 make_file_baton(struct file_baton **f_p,
762                 struct dir_baton *pb,
763                 const char *path,
764                 svn_boolean_t adding,
765                 apr_pool_t *scratch_pool)
766 {
767   struct edit_baton *eb = pb->edit_baton;
768   apr_pool_t *file_pool = svn_pool_create(pb->pool);
769   struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
770
771   SVN_ERR_ASSERT(path);
772
773   /* Make the file's on-disk name. */
774   f->name = svn_dirent_basename(path, file_pool);
775   f->old_revision = SVN_INVALID_REVNUM;
776   SVN_ERR(path_join_under_root(&f->local_abspath,
777                                pb->local_abspath, f->name, file_pool));
778
779   /* Figure out the new URL for this file. */
780   if (eb->switch_relpath)
781     {
782       /* Handle switches... */
783
784       /* This file has a parent directory. If there is
785          no grandparent, then we may have anchored at the parent,
786          and self is the target. If we match the target, then set
787          NEW_RELPATH to the SWITCH_RELPATH.
788
789          Otherwise, we simply extend NEW_RELPATH from the parent.  */
790       if (pb->parent_baton == NULL
791           && strcmp(eb->target_basename, f->name) == 0)
792         f->new_relpath = eb->switch_relpath;
793       else
794         f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
795                                           file_pool);
796     }
797   else  /* must be an update */
798     {
799       if (adding)
800         f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
801       else
802         {
803           SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
804                                              eb->db, f->local_abspath,
805                                              file_pool, scratch_pool));
806           SVN_ERR_ASSERT(f->new_relpath);
807         }
808     }
809
810   f->pool              = file_pool;
811   f->edit_baton        = pb->edit_baton;
812   f->propchanges       = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
813   f->bump_info         = pb->bump_info;
814   f->adding_file       = adding;
815   f->obstruction_found = FALSE;
816   f->add_existed       = FALSE;
817   f->skip_this         = pb->skip_this;
818   f->shadowed          = pb->shadowed || pb->edit_obstructed;
819   f->dir_baton         = pb;
820   f->changed_rev       = SVN_INVALID_REVNUM;
821
822   /* the directory has one more referer now */
823   pb->ref_count++;
824
825   *f_p = f;
826   return SVN_NO_ERROR;
827 }
828
829 /* Complete a conflict skel by describing the update.
830  *
831  * LOCAL_KIND is the node kind of the tree conflict victim in the
832  * working copy.
833  *
834  * All temporary allocations are be made in SCRATCH_POOL, while allocations
835  * needed for the returned conflict struct are made in RESULT_POOL.
836  */
837 static svn_error_t *
838 complete_conflict(svn_skel_t *conflict,
839                   const struct edit_baton *eb,
840                   const char *local_abspath,
841                   const char *old_repos_relpath,
842                   svn_revnum_t old_revision,
843                   const char *new_repos_relpath,
844                   svn_node_kind_t local_kind,
845                   svn_node_kind_t target_kind,
846                   apr_pool_t *result_pool,
847                   apr_pool_t *scratch_pool)
848 {
849   svn_wc_conflict_version_t *original_version;
850   svn_wc_conflict_version_t *target_version;
851   svn_boolean_t is_complete;
852
853   if (!conflict)
854     return SVN_NO_ERROR; /* Not conflicted */
855
856   SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
857
858   if (is_complete)
859     return SVN_NO_ERROR; /* Already completed */
860
861   if (old_repos_relpath)
862     original_version = svn_wc_conflict_version_create2(eb->repos_root,
863                                                        eb->repos_uuid,
864                                                        old_repos_relpath,
865                                                        old_revision,
866                                                        local_kind,
867                                                        result_pool);
868   else
869     original_version = NULL;
870
871   if (new_repos_relpath)
872     target_version = svn_wc_conflict_version_create2(eb->repos_root,
873                                                         eb->repos_uuid,
874                                                         new_repos_relpath,
875                                                         *eb->target_revision,
876                                                         target_kind,
877                                                         result_pool);
878   else
879     target_version = NULL;
880
881   if (eb->switch_relpath)
882     SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
883                                                 original_version,
884                                                 target_version,
885                                                 result_pool, scratch_pool));
886   else
887     SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
888                                                 original_version,
889                                                 target_version,
890                                                 result_pool, scratch_pool));
891
892   return SVN_NO_ERROR;
893 }
894
895
896 /* Called when a directory is really edited, to avoid marking a
897    tree conflict on a node for a no-change edit */
898 static svn_error_t *
899 mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
900 {
901   if (db->edited)
902     return SVN_NO_ERROR;
903
904   if (db->parent_baton)
905     SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
906
907   db->edited = TRUE;
908
909   if (db->edit_conflict)
910     {
911       /* We have a (delayed) tree conflict to install */
912
913       SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
914                                 db->local_abspath,
915                                 db->old_repos_relpath, db->old_revision,
916                                 db->new_relpath,
917                                 svn_node_dir, svn_node_dir,
918                                 db->pool, scratch_pool));
919       SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
920                                           db->local_abspath,
921                                           db->edit_conflict, NULL,
922                                           scratch_pool));
923
924       do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
925                       svn_wc_notify_tree_conflict, scratch_pool);
926       db->already_notified = TRUE;
927
928     }
929
930   return SVN_NO_ERROR;
931 }
932
933 /* Called when a file is really edited, to avoid marking a
934    tree conflict on a node for a no-change edit */
935 static svn_error_t *
936 mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
937 {
938   if (fb->edited)
939     return SVN_NO_ERROR;
940
941   SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
942
943   fb->edited = TRUE;
944
945   if (fb->edit_conflict)
946     {
947       /* We have a (delayed) tree conflict to install */
948
949       SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
950                                 fb->local_abspath, fb->old_repos_relpath,
951                                 fb->old_revision, fb->new_relpath,
952                                 svn_node_file, svn_node_file,
953                                 fb->pool, scratch_pool));
954
955       SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
956                                           fb->local_abspath,
957                                           fb->edit_conflict, NULL,
958                                           scratch_pool));
959
960       do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
961                       svn_wc_notify_tree_conflict, scratch_pool);
962       fb->already_notified = TRUE;
963     }
964
965   return SVN_NO_ERROR;
966 }
967
968
969 /* Handle the next delta window of the file described by BATON.  If it is
970  * the end (WINDOW == NULL), then check the checksum, store the text in the
971  * pristine store and write its details into BATON->fb->new_text_base_*. */
972 static svn_error_t *
973 window_handler(svn_txdelta_window_t *window, void *baton)
974 {
975   struct handler_baton *hb = baton;
976   struct file_baton *fb = hb->fb;
977   svn_wc__db_t *db = fb->edit_baton->db;
978   svn_error_t *err;
979
980   /* Apply this window.  We may be done at that point.  */
981   err = hb->apply_handler(window, hb->apply_baton);
982   if (window != NULL && !err)
983     return SVN_NO_ERROR;
984
985   if (hb->expected_source_checksum)
986     {
987       /* Close the stream to calculate HB->actual_source_md5_checksum. */
988       svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
989
990       if (!err2)
991         {
992           SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
993                         hb->actual_source_checksum->kind);
994
995           if (!svn_checksum_match(hb->expected_source_checksum,
996                                   hb->actual_source_checksum))
997             {
998               err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
999                         _("Checksum mismatch while updating '%s':\n"
1000                           "   expected:  %s\n"
1001                           "     actual:  %s\n"),
1002                         svn_dirent_local_style(fb->local_abspath, hb->pool),
1003                         svn_checksum_to_cstring(hb->expected_source_checksum,
1004                                                 hb->pool),
1005                         svn_checksum_to_cstring(hb->actual_source_checksum,
1006                                                 hb->pool));
1007             }
1008         }
1009
1010       err = svn_error_compose_create(err, err2);
1011     }
1012
1013   if (err)
1014     {
1015       /* We failed to apply the delta; clean up the temporary file if it
1016          already created by lazy_open_target(). */
1017       if (hb->new_text_base_tmp_abspath)
1018         {
1019           svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath,
1020                                               TRUE, hb->pool));
1021         }
1022     }
1023   else
1024     {
1025       /* Tell the file baton about the new text base's checksums. */
1026       fb->new_text_base_md5_checksum =
1027         svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1028       fb->new_text_base_sha1_checksum =
1029         svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1030
1031       /* Store the new pristine text in the pristine store now.  Later, in a
1032          single transaction we will update the BASE_NODE to include a
1033          reference to this pristine text's checksum. */
1034       SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
1035                                           fb->new_text_base_sha1_checksum,
1036                                           fb->new_text_base_md5_checksum,
1037                                           hb->pool));
1038     }
1039
1040   svn_pool_destroy(hb->pool);
1041
1042   return err;
1043 }
1044
1045
1046 /* Find the last-change info within ENTRY_PROPS, and return then in the
1047    CHANGED_* parameters. Each parameter will be initialized to its "none"
1048    value, and will contain the relavent info if found.
1049
1050    CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1051    used for some temporary allocations.
1052 */
1053 static svn_error_t *
1054 accumulate_last_change(svn_revnum_t *changed_rev,
1055                        apr_time_t *changed_date,
1056                        const char **changed_author,
1057                        const apr_array_header_t *entry_props,
1058                        apr_pool_t *result_pool,
1059                        apr_pool_t *scratch_pool)
1060 {
1061   int i;
1062
1063   *changed_rev = SVN_INVALID_REVNUM;
1064   *changed_date = 0;
1065   *changed_author = NULL;
1066
1067   for (i = 0; i < entry_props->nelts; ++i)
1068     {
1069       const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1070
1071       /* A prop value of NULL means the information was not
1072          available.  We don't remove this field from the entries
1073          file; we have convention just leave it empty.  So let's
1074          just skip those entry props that have no values. */
1075       if (! prop->value)
1076         continue;
1077
1078       if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1079         *changed_author = apr_pstrdup(result_pool, prop->value->data);
1080       else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1081         {
1082           apr_int64_t rev;
1083           SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1084           *changed_rev = (svn_revnum_t)rev;
1085         }
1086       else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1087         SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1088                                       scratch_pool));
1089
1090       /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1091          property here. */
1092     }
1093
1094   return SVN_NO_ERROR;
1095 }
1096
1097
1098 /* Join ADD_PATH to BASE_PATH.  If ADD_PATH is absolute, or if any ".."
1099  * component of it resolves to a path above BASE_PATH, then return
1100  * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1101  *
1102  * This is to prevent the situation where the repository contains,
1103  * say, "..\nastyfile".  Although that's perfectly legal on some
1104  * systems, when checked out onto Win32 it would cause "nastyfile" to
1105  * be created in the parent of the current edit directory.
1106  *
1107  * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1108  */
1109 static svn_error_t *
1110 path_join_under_root(const char **result_path,
1111                      const char *base_path,
1112                      const char *add_path,
1113                      apr_pool_t *pool)
1114 {
1115   svn_boolean_t under_root;
1116
1117   SVN_ERR(svn_dirent_is_under_root(&under_root,
1118                                    result_path, base_path, add_path, pool));
1119
1120   if (! under_root)
1121     {
1122       return svn_error_createf(
1123           SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1124           _("Path '%s' is not in the working copy"),
1125           svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1126                                  pool));
1127     }
1128
1129   /* This catches issue #3288 */
1130   if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1131     {
1132       return svn_error_createf(
1133           SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1134           _("'%s' is not valid as filename in directory '%s'"),
1135           svn_dirent_local_style(add_path, pool),
1136           svn_dirent_local_style(base_path, pool));
1137     }
1138
1139   return SVN_NO_ERROR;
1140 }
1141
1142 \f
1143 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1144
1145 /* An svn_delta_editor_t function. */
1146 static svn_error_t *
1147 set_target_revision(void *edit_baton,
1148                     svn_revnum_t target_revision,
1149                     apr_pool_t *pool)
1150 {
1151   struct edit_baton *eb = edit_baton;
1152
1153   *(eb->target_revision) = target_revision;
1154   return SVN_NO_ERROR;
1155 }
1156
1157 static svn_error_t *
1158 check_tree_conflict(svn_skel_t **pconflict,
1159                     struct edit_baton *eb,
1160                     const char *local_abspath,
1161                     svn_wc__db_status_t working_status,
1162                     svn_boolean_t exists_in_repos,
1163                     svn_node_kind_t expected_kind,
1164                     svn_wc_conflict_action_t action,
1165                     apr_pool_t *result_pool,
1166                     apr_pool_t *scratch_pool);
1167
1168 /* An svn_delta_editor_t function. */
1169 static svn_error_t *
1170 open_root(void *edit_baton,
1171           svn_revnum_t base_revision, /* This is ignored in co */
1172           apr_pool_t *pool,
1173           void **dir_baton)
1174 {
1175   struct edit_baton *eb = edit_baton;
1176   struct dir_baton *db;
1177   svn_boolean_t already_conflicted, conflict_ignored;
1178   svn_error_t *err;
1179   svn_wc__db_status_t status;
1180   svn_wc__db_status_t base_status;
1181   svn_node_kind_t kind;
1182   svn_boolean_t have_work;
1183
1184   /* Note that something interesting is actually happening in this
1185      edit run. */
1186   eb->root_opened = TRUE;
1187
1188   SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1189   *dir_baton = db;
1190
1191   err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1192                                    eb->db, db->local_abspath, pool);
1193
1194   if (err)
1195     {
1196       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1197         return svn_error_trace(err);
1198
1199       svn_error_clear(err);
1200       already_conflicted = conflict_ignored = FALSE;
1201     }
1202   else if (already_conflicted)
1203     {
1204       /* Record a skip of both the anchor and target in the skipped tree
1205          as the anchor itself might not be updated */
1206       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1207       SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1208
1209       db->skip_this = TRUE;
1210       db->already_notified = TRUE;
1211
1212       /* Notify that we skipped the target, while we actually skipped
1213          the anchor */
1214       do_notification(eb, eb->target_abspath, svn_node_unknown,
1215                       svn_wc_notify_skip_conflicted, pool);
1216
1217       return SVN_NO_ERROR;
1218     }
1219
1220
1221   SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1222                                &db->old_repos_relpath, NULL, NULL,
1223                                &db->changed_rev, &db->changed_date,
1224                                &db->changed_author, &db->ambient_depth,
1225                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1226                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1227                                NULL, NULL, &have_work,
1228                                eb->db, db->local_abspath,
1229                                db->pool, pool));
1230
1231   if (conflict_ignored)
1232     {
1233       db->shadowed = TRUE;
1234     }
1235   else if (have_work)
1236     {
1237       const char *move_src_root_abspath;
1238
1239       SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
1240                                        NULL, eb->db, db->local_abspath,
1241                                        pool, pool));
1242       if (move_src_root_abspath || *eb->target_basename == '\0')
1243         SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1244                                          &db->old_revision,
1245                                          &db->old_repos_relpath, NULL, NULL,
1246                                          &db->changed_rev, &db->changed_date,
1247                                          &db->changed_author,
1248                                          &db->ambient_depth,
1249                                          NULL, NULL, NULL, NULL, NULL, NULL,
1250                                          eb->db, db->local_abspath,
1251                                          db->pool, pool));
1252
1253       if (move_src_root_abspath)
1254         {
1255           /* This is an update anchored inside a move. We need to
1256              raise a move-edit tree-conflict on the move root to
1257              update the move destination. */
1258           svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1259
1260           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1261                     tree_conflict, eb->db, move_src_root_abspath,
1262                     svn_wc_conflict_reason_moved_away,
1263                     svn_wc_conflict_action_edit,
1264                     move_src_root_abspath, pool, pool));
1265
1266           if (strcmp(db->local_abspath, move_src_root_abspath))
1267             {
1268               /* We are raising the tree-conflict on some parent of
1269                  the edit root, we won't be handling that path again
1270                  so raise the conflict now. */
1271               SVN_ERR(complete_conflict(tree_conflict, eb,
1272                                         move_src_root_abspath,
1273                                         db->old_repos_relpath,
1274                                         db->old_revision, db->new_relpath,
1275                                         svn_node_dir, svn_node_dir,
1276                                         pool, pool));
1277               SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1278                                                   move_src_root_abspath,
1279                                                   tree_conflict,
1280                                                   NULL, pool));
1281               do_notification(eb, move_src_root_abspath, svn_node_dir,
1282                               svn_wc_notify_tree_conflict, pool);
1283             }
1284           else
1285             db->edit_conflict = tree_conflict;
1286         }
1287
1288       db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1289                               make sure it doesn't use the ACTUAL tree */
1290     }
1291   else
1292     base_status = status;
1293
1294   if (*eb->target_basename == '\0')
1295     {
1296       /* For an update with a NULL target, this is equivalent to open_dir(): */
1297
1298       db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1299
1300       /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1301                    open_directory() does.
1302                    (or find a way to reuse that code here)
1303
1304          ### BH 2013: I don't think we need all of the detection here, as the
1305                       user explicitly asked to update this node. So we don't
1306                       have to tell that it is a local replacement/delete.
1307        */
1308
1309       SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1310                                                         db->local_abspath,
1311                                                         db->new_relpath,
1312                                                         *eb->target_revision,
1313                                                         pool));
1314     }
1315
1316   return SVN_NO_ERROR;
1317 }
1318
1319
1320 /* ===================================================================== */
1321 /* Checking for local modifications. */
1322
1323 /* A baton for use with modcheck_found_entry(). */
1324 typedef struct modcheck_baton_t {
1325   svn_wc__db_t *db;         /* wc_db to access nodes */
1326   svn_boolean_t found_mod;  /* whether a modification has been found */
1327   svn_boolean_t found_not_delete;  /* Found a not-delete modification */
1328 } modcheck_baton_t;
1329
1330 /* An implementation of svn_wc_status_func4_t. */
1331 static svn_error_t *
1332 modcheck_callback(void *baton,
1333                   const char *local_abspath,
1334                   const svn_wc_status3_t *status,
1335                   apr_pool_t *scratch_pool)
1336 {
1337   modcheck_baton_t *mb = baton;
1338
1339   switch (status->node_status)
1340     {
1341       case svn_wc_status_normal:
1342       case svn_wc_status_incomplete:
1343       case svn_wc_status_ignored:
1344       case svn_wc_status_none:
1345       case svn_wc_status_unversioned:
1346       case svn_wc_status_external:
1347         break;
1348
1349       case svn_wc_status_deleted:
1350         mb->found_mod = TRUE;
1351         break;
1352
1353       case svn_wc_status_missing:
1354       case svn_wc_status_obstructed:
1355         mb->found_mod = TRUE;
1356         mb->found_not_delete = TRUE;
1357         /* Exit from the status walker: We know what we want to know */
1358         return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1359
1360       default:
1361       case svn_wc_status_added:
1362       case svn_wc_status_replaced:
1363       case svn_wc_status_modified:
1364         mb->found_mod = TRUE;
1365         mb->found_not_delete = TRUE;
1366         /* Exit from the status walker: We know what we want to know */
1367         return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1368     }
1369
1370   return SVN_NO_ERROR;
1371 }
1372
1373
1374 /* Set *MODIFIED to true iff there are any local modifications within the
1375  * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
1376  * is set to true and all the local modifications were deletes then set
1377  * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise.  LOCAL_ABSPATH
1378  * may be a file or a directory. */
1379 svn_error_t *
1380 svn_wc__node_has_local_mods(svn_boolean_t *modified,
1381                             svn_boolean_t *all_edits_are_deletes,
1382                             svn_wc__db_t *db,
1383                             const char *local_abspath,
1384                             svn_cancel_func_t cancel_func,
1385                             void *cancel_baton,
1386                             apr_pool_t *scratch_pool)
1387 {
1388   modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
1389   svn_error_t *err;
1390
1391   modcheck_baton.db = db;
1392
1393   /* Walk the WC tree for status with depth infinity, looking for any local
1394    * modifications. If it's a "sparse" directory, that's OK: there can be
1395    * no local mods in the pieces that aren't present in the WC. */
1396
1397   err = svn_wc__internal_walk_status(db, local_abspath,
1398                                      svn_depth_infinity,
1399                                      FALSE, FALSE, FALSE, NULL,
1400                                      modcheck_callback, &modcheck_baton,
1401                                      cancel_func, cancel_baton,
1402                                      scratch_pool);
1403
1404   if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
1405     svn_error_clear(err);
1406   else
1407     SVN_ERR(err);
1408
1409   *modified = modcheck_baton.found_mod;
1410   *all_edits_are_deletes = (modcheck_baton.found_mod
1411                             && !modcheck_baton.found_not_delete);
1412
1413   return SVN_NO_ERROR;
1414 }
1415
1416 /* Indicates an unset svn_wc_conflict_reason_t. */
1417 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1418
1419 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
1420  * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1421  * LOCAL_ABSPATH as the victim.
1422  *
1423  * The edit baton EB gives information including whether the operation is
1424  * an update or a switch.
1425  *
1426  * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1427  * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1428  * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1429  *
1430  * If a tree conflict reason was found for the incoming action, the resulting
1431  * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1432  * while *PCONFLICT is always overwritten.
1433  *
1434  * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1435  * SCRATCH_POOL.
1436  */
1437 static svn_error_t *
1438 check_tree_conflict(svn_skel_t **pconflict,
1439                     struct edit_baton *eb,
1440                     const char *local_abspath,
1441                     svn_wc__db_status_t working_status,
1442                     svn_boolean_t exists_in_repos,
1443                     svn_node_kind_t expected_kind,
1444                     svn_wc_conflict_action_t action,
1445                     apr_pool_t *result_pool,
1446                     apr_pool_t *scratch_pool)
1447 {
1448   svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1449   svn_boolean_t modified = FALSE;
1450   svn_boolean_t all_mods_are_deletes = FALSE;
1451   const char *move_src_op_root_abspath = NULL;
1452
1453   *pconflict = NULL;
1454
1455   /* Find out if there are any local changes to this node that may
1456    * be the "reason" of a tree-conflict with the incoming "action". */
1457   switch (working_status)
1458     {
1459       case svn_wc__db_status_added:
1460       case svn_wc__db_status_moved_here:
1461       case svn_wc__db_status_copied:
1462         if (!exists_in_repos)
1463           {
1464             /* The node is locally added, and it did not exist before.  This
1465              * is an 'update', so the local add can only conflict with an
1466              * incoming 'add'.  In fact, if we receive anything else than an
1467              * svn_wc_conflict_action_add (which includes 'added',
1468              * 'copied-here' and 'moved-here') during update on a node that
1469              * did not exist before, then something is very wrong.
1470              * Note that if there was no action on the node, this code
1471              * would not have been called in the first place. */
1472             SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1473
1474             /* Scan the addition in case our caller didn't. */
1475             if (working_status == svn_wc__db_status_added)
1476               SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1477                                                NULL, NULL, NULL, NULL,
1478                                                NULL, NULL,
1479                                                eb->db, local_abspath,
1480                                                scratch_pool, scratch_pool));
1481
1482             if (working_status == svn_wc__db_status_moved_here)
1483               reason = svn_wc_conflict_reason_moved_here;
1484             else
1485               reason = svn_wc_conflict_reason_added;
1486           }
1487         else
1488           {
1489             /* The node is locally replaced but could also be moved-away. */
1490             SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1491                                              &move_src_op_root_abspath,
1492                                              eb->db, local_abspath,
1493                                              scratch_pool, scratch_pool));
1494             if (move_src_op_root_abspath)
1495               reason = svn_wc_conflict_reason_moved_away;
1496             else
1497               reason = svn_wc_conflict_reason_replaced;
1498           }
1499         break;
1500
1501
1502       case svn_wc__db_status_deleted:
1503         {
1504           SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1505                                            &move_src_op_root_abspath,
1506                                            eb->db, local_abspath,
1507                                            scratch_pool, scratch_pool));
1508           if (move_src_op_root_abspath)
1509             reason = svn_wc_conflict_reason_moved_away;
1510           else
1511             reason = svn_wc_conflict_reason_deleted;
1512         }
1513         break;
1514
1515       case svn_wc__db_status_incomplete:
1516         /* We used svn_wc__db_read_info(), so 'incomplete' means
1517          * - there is no node in the WORKING tree
1518          * - a BASE node is known to exist
1519          * So the node exists and is essentially 'normal'. We still need to
1520          * check prop and text mods, and those checks will retrieve the
1521          * missing information (hopefully). */
1522       case svn_wc__db_status_normal:
1523         if (action == svn_wc_conflict_action_edit)
1524           {
1525             /* An edit onto a local edit or onto *no* local changes is no
1526              * tree-conflict. (It's possibly a text- or prop-conflict,
1527              * but we don't handle those here.)
1528              *
1529              * Except when there is a local obstruction
1530              */
1531             if (exists_in_repos)
1532               {
1533                 svn_node_kind_t disk_kind;
1534
1535                 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1536                                           scratch_pool));
1537
1538                 if (disk_kind != expected_kind && disk_kind != svn_node_none)
1539                   {
1540                     reason = svn_wc_conflict_reason_obstructed;
1541                     break;
1542                   }
1543
1544               }
1545             return SVN_NO_ERROR;
1546           }
1547
1548         /* Replace is handled as delete and then specifically in
1549            add_directory() and add_file(), so we only expect deletes here */
1550         SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1551
1552         /* Check if the update wants to delete or replace a locally
1553          * modified node. */
1554
1555
1556         /* Do a deep tree detection of local changes. The update editor will
1557          * not visit the subdirectories of a directory that it wants to delete.
1558          * Therefore, we need to start a separate crawl here. */
1559
1560         SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
1561                                             eb->db, local_abspath,
1562                                             eb->cancel_func, eb->cancel_baton,
1563                                             scratch_pool));
1564
1565         if (modified)
1566           {
1567             if (all_mods_are_deletes)
1568               reason = svn_wc_conflict_reason_deleted;
1569             else
1570               reason = svn_wc_conflict_reason_edited;
1571           }
1572         break;
1573
1574       case svn_wc__db_status_server_excluded:
1575         /* Not allowed to view the node. Not allowed to report tree
1576          * conflicts. */
1577       case svn_wc__db_status_excluded:
1578         /* Locally marked as excluded. No conflicts wanted. */
1579       case svn_wc__db_status_not_present:
1580         /* A committed delete (but parent not updated). The delete is
1581            committed, so no conflict possible during update. */
1582         return SVN_NO_ERROR;
1583
1584       case svn_wc__db_status_base_deleted:
1585         /* An internal status. Should never show up here. */
1586         SVN_ERR_MALFUNCTION();
1587         break;
1588
1589     }
1590
1591   if (reason == SVN_WC_CONFLICT_REASON_NONE)
1592     /* No conflict with the current action. */
1593     return SVN_NO_ERROR;
1594
1595
1596   /* Sanity checks. Note that if there was no action on the node, this function
1597    * would not have been called in the first place.*/
1598   if (reason == svn_wc_conflict_reason_edited
1599       || reason == svn_wc_conflict_reason_obstructed
1600       || reason == svn_wc_conflict_reason_deleted
1601       || reason == svn_wc_conflict_reason_moved_away
1602       || reason == svn_wc_conflict_reason_replaced)
1603     {
1604       /* When the node existed before (it was locally deleted, replaced or
1605        * edited), then 'update' cannot add it "again". So it can only send
1606        * _action_edit, _delete or _replace. */
1607     if (action != svn_wc_conflict_action_edit
1608         && action != svn_wc_conflict_action_delete
1609         && action != svn_wc_conflict_action_replace)
1610       return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1611                _("Unexpected attempt to add a node at path '%s'"),
1612                svn_dirent_local_style(local_abspath, scratch_pool));
1613     }
1614   else if (reason == svn_wc_conflict_reason_added ||
1615            reason == svn_wc_conflict_reason_moved_here)
1616     {
1617       /* When the node did not exist before (it was locally added),
1618        * then 'update' cannot want to modify it in any way.
1619        * It can only send _action_add. */
1620       if (action != svn_wc_conflict_action_add)
1621         return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1622                  _("Unexpected attempt to edit, delete, or replace "
1623                    "a node at path '%s'"),
1624                  svn_dirent_local_style(local_abspath, scratch_pool));
1625
1626     }
1627
1628
1629   /* A conflict was detected. Create a conflict skel to record it. */
1630   *pconflict = svn_wc__conflict_skel_create(result_pool);
1631
1632   SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1633                                                   eb->db, local_abspath,
1634                                                   reason,
1635                                                   action,
1636                                                   move_src_op_root_abspath,
1637                                                   result_pool, scratch_pool));
1638
1639   return SVN_NO_ERROR;
1640 }
1641
1642
1643 /* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1644  * not a moved-away-edit conflict, set *CONFLICTED to TRUE.  Otherwise
1645  * set *CONFLICTED to FALSE.
1646  */
1647 static svn_error_t *
1648 already_in_a_tree_conflict(svn_boolean_t *conflicted,
1649                            svn_boolean_t *ignored,
1650                            svn_wc__db_t *db,
1651                            const char *local_abspath,
1652                            apr_pool_t *scratch_pool)
1653 {
1654   const char *ancestor_abspath = local_abspath;
1655   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1656
1657   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1658
1659   *conflicted = *ignored = FALSE;
1660
1661   while (TRUE)
1662     {
1663       svn_boolean_t is_wc_root;
1664
1665       svn_pool_clear(iterpool);
1666
1667       SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1668                                               ancestor_abspath, TRUE,
1669                                               scratch_pool));
1670       if (*conflicted || *ignored)
1671         break;
1672
1673       SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1674                                    iterpool));
1675       if (is_wc_root)
1676         break;
1677
1678       ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1679     }
1680
1681   svn_pool_destroy(iterpool);
1682
1683   return SVN_NO_ERROR;
1684 }
1685
1686 /* Temporary helper until the new conflict handling is in place */
1687 static svn_error_t *
1688 node_already_conflicted(svn_boolean_t *conflicted,
1689                         svn_boolean_t *conflict_ignored,
1690                         svn_wc__db_t *db,
1691                         const char *local_abspath,
1692                         apr_pool_t *scratch_pool)
1693 {
1694   SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1695                                           local_abspath, FALSE,
1696                                           scratch_pool));
1697
1698   return SVN_NO_ERROR;
1699 }
1700
1701
1702 /* An svn_delta_editor_t function. */
1703 static svn_error_t *
1704 delete_entry(const char *path,
1705              svn_revnum_t revision,
1706              void *parent_baton,
1707              apr_pool_t *pool)
1708 {
1709   struct dir_baton *pb = parent_baton;
1710   struct edit_baton *eb = pb->edit_baton;
1711   const char *base = svn_relpath_basename(path, NULL);
1712   const char *local_abspath;
1713   const char *repos_relpath;
1714   svn_node_kind_t kind, base_kind;
1715   svn_revnum_t old_revision;
1716   svn_boolean_t conflicted;
1717   svn_boolean_t have_work;
1718   svn_skel_t *tree_conflict = NULL;
1719   svn_wc__db_status_t status;
1720   svn_wc__db_status_t base_status;
1721   apr_pool_t *scratch_pool;
1722   svn_boolean_t deleting_target;
1723   svn_boolean_t deleting_switched;
1724   svn_boolean_t keep_as_working = FALSE;
1725   svn_boolean_t queue_deletes = TRUE;
1726
1727   if (pb->skip_this)
1728     return SVN_NO_ERROR;
1729
1730   scratch_pool = svn_pool_create(pb->pool);
1731
1732   SVN_ERR(mark_directory_edited(pb, scratch_pool));
1733
1734   SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1735                                scratch_pool));
1736
1737   deleting_target =  (strcmp(local_abspath, eb->target_abspath) == 0);
1738
1739   /* Detect obstructing working copies */
1740   {
1741     svn_boolean_t is_root;
1742
1743     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1744                                  scratch_pool));
1745
1746     if (is_root)
1747       {
1748         /* Just skip this node; a future update will handle it */
1749         SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1750         do_notification(eb, local_abspath, svn_node_unknown,
1751                         svn_wc_notify_update_skip_obstruction, scratch_pool);
1752
1753         svn_pool_destroy(scratch_pool);
1754
1755         return SVN_NO_ERROR;
1756       }
1757   }
1758
1759   SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1760                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1761                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1762                                &conflicted, NULL, NULL, NULL,
1763                                NULL, NULL, &have_work,
1764                                eb->db, local_abspath,
1765                                scratch_pool, scratch_pool));
1766
1767   if (!have_work)
1768     {
1769       base_status = status;
1770       base_kind = kind;
1771     }
1772   else
1773     SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
1774                                      &repos_relpath,
1775                                      NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1776                                      NULL, NULL, NULL, NULL, NULL,
1777                                      eb->db, local_abspath,
1778                                      scratch_pool, scratch_pool));
1779
1780   if (pb->old_repos_relpath && repos_relpath)
1781     {
1782       const char *expected_name;
1783
1784       expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1785                                                 repos_relpath);
1786
1787       deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1788     }
1789   else
1790     deleting_switched = FALSE;
1791
1792   /* Is this path a conflict victim? */
1793   if (pb->shadowed)
1794     conflicted = FALSE; /* Conflict applies to WORKING */
1795   else if (conflicted)
1796     SVN_ERR(node_already_conflicted(&conflicted, NULL,
1797                                     eb->db, local_abspath, scratch_pool));
1798   if (conflicted)
1799     {
1800       SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1801
1802       do_notification(eb, local_abspath, svn_node_unknown,
1803                       svn_wc_notify_skip_conflicted,
1804                       scratch_pool);
1805
1806       svn_pool_destroy(scratch_pool);
1807
1808       return SVN_NO_ERROR;
1809     }
1810
1811
1812   /* Receive the remote removal of excluded/server-excluded/not present node.
1813      Do not notify, but perform the change even when the node is shadowed */
1814   if (base_status == svn_wc__db_status_not_present
1815       || base_status == svn_wc__db_status_excluded
1816       || base_status == svn_wc__db_status_server_excluded)
1817     {
1818       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1819                                      FALSE /* keep_as_working */,
1820                                      FALSE /* queue_deletes */,
1821                                      FALSE /* remove_locks */,
1822                                      SVN_INVALID_REVNUM /* not_present_rev */,
1823                                      NULL, NULL,
1824                                      scratch_pool));
1825
1826       if (deleting_target)
1827         eb->target_deleted = TRUE;
1828
1829       svn_pool_destroy(scratch_pool);
1830
1831       return SVN_NO_ERROR;
1832     }
1833
1834   /* Is this path the victim of a newly-discovered tree conflict?  If so,
1835    * remember it and notify the client. Then (if it was existing and
1836    * modified), re-schedule the node to be added back again, as a (modified)
1837    * copy of the previous base version.  */
1838
1839   /* Check for conflicts only when we haven't already recorded
1840    * a tree-conflict on a parent node. */
1841   if (!pb->shadowed && !pb->edit_obstructed)
1842     {
1843       SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1844                                   status, TRUE,
1845                                   (kind == svn_node_dir)
1846                                         ? svn_node_dir
1847                                         : svn_node_file,
1848                                   svn_wc_conflict_action_delete,
1849                                   pb->pool, scratch_pool));
1850     }
1851   else
1852     queue_deletes = FALSE; /* There is no in-wc representation */
1853
1854   if (tree_conflict != NULL)
1855     {
1856       svn_wc_conflict_reason_t reason;
1857       /* When we raise a tree conflict on a node, we don't want to mark the
1858        * node as skipped, to allow a replacement to continue doing at least
1859        * a bit of its work (possibly adding a not present node, for the
1860        * next update) */
1861       if (!pb->deletion_conflicts)
1862         pb->deletion_conflicts = apr_hash_make(pb->pool);
1863
1864       svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1865                     tree_conflict);
1866
1867       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
1868                                                   eb->db, local_abspath,
1869                                                   tree_conflict,
1870                                                   scratch_pool, scratch_pool));
1871
1872       if (reason == svn_wc_conflict_reason_edited
1873           || reason == svn_wc_conflict_reason_obstructed)
1874         {
1875           /* The item exists locally and has some sort of local mod.
1876            * It no longer exists in the repository at its target URL@REV.
1877            *
1878            * To prepare the "accept mine" resolution for the tree conflict,
1879            * we must schedule the existing content for re-addition as a copy
1880            * of what it was, but with its local modifications preserved. */
1881           keep_as_working = TRUE;
1882
1883           /* Fall through to remove the BASE_NODEs properly, with potentially
1884              keeping a not-present marker */
1885         }
1886       else if (reason == svn_wc_conflict_reason_deleted
1887                || reason == svn_wc_conflict_reason_moved_away
1888                || reason == svn_wc_conflict_reason_replaced)
1889         {
1890           /* The item does not exist locally because it was already shadowed.
1891            * We must complete the deletion, leaving the tree conflict info
1892            * as the only difference from a normal deletion. */
1893
1894           /* Fall through to the normal "delete" code path. */
1895         }
1896       else
1897         SVN_ERR_MALFUNCTION();  /* other reasons are not expected here */
1898     }
1899
1900   SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1901                             old_revision, NULL,
1902                             (kind == svn_node_dir)
1903                                 ? svn_node_dir
1904                                 : svn_node_file,
1905                             svn_node_none,
1906                             pb->pool, scratch_pool));
1907
1908   /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1909      nodes based on that from disk, but leave any WORKING_NODEs on disk.
1910
1911      Local modifications are already turned into copies at this point.
1912
1913      If the thing being deleted is the *target* of this update, then
1914      we need to recreate a 'deleted' entry, so that the parent can give
1915      accurate reports about itself in the future. */
1916   if (! deleting_target && ! deleting_switched)
1917     {
1918       /* Delete, and do not leave a not-present node.  */
1919       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1920                                      keep_as_working, queue_deletes, FALSE,
1921                                      SVN_INVALID_REVNUM /* not_present_rev */,
1922                                      tree_conflict, NULL,
1923                                      scratch_pool));
1924     }
1925   else
1926     {
1927       /* Delete, leaving a not-present node.  */
1928       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1929                                      keep_as_working, queue_deletes, FALSE,
1930                                      *eb->target_revision,
1931                                      tree_conflict, NULL,
1932                                      scratch_pool));
1933       if (deleting_target)
1934         eb->target_deleted = TRUE;
1935       else
1936         {
1937           /* Don't remove the not-present marker at the final bump */
1938           SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1939         }
1940     }
1941
1942   SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1943                          eb->cancel_func, eb->cancel_baton,
1944                          scratch_pool));
1945
1946   /* Notify. */
1947   if (tree_conflict)
1948     {
1949       if (eb->conflict_func)
1950         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1951                                                  tree_conflict,
1952                                                  NULL /* merge_options */,
1953                                                  eb->conflict_func,
1954                                                  eb->conflict_baton,
1955                                                  eb->cancel_func,
1956                                                  eb->cancel_baton,
1957                                                  scratch_pool));
1958       do_notification(eb, local_abspath, svn_node_unknown,
1959                       svn_wc_notify_tree_conflict, scratch_pool);
1960     }
1961   else
1962     {
1963       svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1964       svn_node_kind_t node_kind;
1965
1966       if (pb->shadowed || pb->edit_obstructed)
1967         action = svn_wc_notify_update_shadowed_delete;
1968
1969       if (kind == svn_node_dir)
1970         node_kind = svn_node_dir;
1971       else
1972         node_kind = svn_node_file;
1973
1974       do_notification(eb, local_abspath, node_kind, action, scratch_pool);
1975     }
1976
1977   svn_pool_destroy(scratch_pool);
1978
1979   return SVN_NO_ERROR;
1980 }
1981
1982 /* An svn_delta_editor_t function. */
1983 static svn_error_t *
1984 add_directory(const char *path,
1985               void *parent_baton,
1986               const char *copyfrom_path,
1987               svn_revnum_t copyfrom_rev,
1988               apr_pool_t *pool,
1989               void **child_baton)
1990 {
1991   struct dir_baton *pb = parent_baton;
1992   struct edit_baton *eb = pb->edit_baton;
1993   struct dir_baton *db;
1994   svn_node_kind_t kind;
1995   svn_wc__db_status_t status;
1996   svn_node_kind_t wc_kind;
1997   svn_boolean_t conflicted;
1998   svn_boolean_t conflict_ignored = FALSE;
1999   svn_boolean_t versioned_locally_and_present;
2000   svn_skel_t *tree_conflict = NULL;
2001   svn_error_t *err;
2002
2003   SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
2004
2005   SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
2006   *child_baton = db;
2007
2008   if (db->skip_this)
2009     return SVN_NO_ERROR;
2010
2011   SVN_ERR(mark_directory_edited(db, pool));
2012
2013   if (strcmp(eb->target_abspath, db->local_abspath) == 0)
2014     {
2015       /* The target of the edit is being added, give it the requested
2016          depth of the edit (but convert svn_depth_unknown to
2017          svn_depth_infinity). */
2018       db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
2019         ? svn_depth_infinity : eb->requested_depth;
2020     }
2021   else if (eb->requested_depth == svn_depth_immediates
2022            || (eb->requested_depth == svn_depth_unknown
2023                && pb->ambient_depth == svn_depth_immediates))
2024     {
2025       db->ambient_depth = svn_depth_empty;
2026     }
2027   else
2028     {
2029       db->ambient_depth = svn_depth_infinity;
2030     }
2031
2032   /* It may not be named the same as the administrative directory. */
2033   if (svn_wc_is_adm_dir(db->name, pool))
2034     return svn_error_createf(
2035        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
2036        _("Failed to add directory '%s': object of the same name as the "
2037          "administrative directory"),
2038        svn_dirent_local_style(db->local_abspath, pool));
2039
2040   SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
2041
2042   err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
2043                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2044                              NULL, NULL, NULL, NULL, NULL,
2045                              &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
2046                              eb->db, db->local_abspath, db->pool, db->pool);
2047   if (err)
2048     {
2049       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2050         return svn_error_trace(err);
2051
2052       svn_error_clear(err);
2053       wc_kind = svn_node_unknown;
2054       status = svn_wc__db_status_normal;
2055       conflicted = FALSE;
2056
2057       versioned_locally_and_present = FALSE;
2058     }
2059   else if (wc_kind == svn_node_dir
2060            && status == svn_wc__db_status_normal)
2061     {
2062       /* !! We found the root of a separate working copy obstructing the wc !!
2063
2064          If the directory would be part of our own working copy then
2065          we wouldn't have been called as an add_directory().
2066
2067          The only thing we can do is add a not-present node, to allow
2068          a future update to bring in the new files when the problem is
2069          resolved.  Note that svn_wc__db_base_add_not_present_node()
2070          explicitly adds the node into the parent's node database. */
2071
2072       SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2073                                                    db->new_relpath,
2074                                                    eb->repos_root,
2075                                                    eb->repos_uuid,
2076                                                    *eb->target_revision,
2077                                                    svn_node_file,
2078                                                    NULL, NULL,
2079                                                    pool));
2080
2081       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2082       db->skip_this = TRUE;
2083       db->already_notified = TRUE;
2084
2085       do_notification(eb, db->local_abspath, svn_node_dir,
2086                       svn_wc_notify_update_skip_obstruction, pool);
2087
2088       return SVN_NO_ERROR;
2089     }
2090   else if (status == svn_wc__db_status_normal
2091            && (wc_kind == svn_node_file
2092                || wc_kind == svn_node_symlink))
2093     {
2094       /* We found a file external occupating the place we need in BASE.
2095
2096          We can't add a not-present node in this case as that would overwrite
2097          the file external. Luckily the file external itself stops us from
2098          forgetting a child of this parent directory like an obstructing
2099          working copy would.
2100
2101          The reason we get here is that the adm crawler doesn't report
2102          file externals.
2103       */
2104
2105       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2106       db->skip_this = TRUE;
2107       db->already_notified = TRUE;
2108
2109       do_notification(eb, db->local_abspath, svn_node_file,
2110                       svn_wc_notify_update_skip_obstruction, pool);
2111
2112       return SVN_NO_ERROR;
2113     }
2114   else if (wc_kind == svn_node_unknown)
2115     versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
2116   else
2117     versioned_locally_and_present = IS_NODE_PRESENT(status);
2118
2119   /* Is this path a conflict victim? */
2120   if (conflicted)
2121     {
2122       if (pb->deletion_conflicts)
2123         tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2124
2125       if (tree_conflict)
2126         {
2127           svn_wc_conflict_reason_t reason;
2128           const char *move_src_op_root_abspath;
2129           /* So this deletion wasn't just a deletion, it is actually a
2130              replacement. Let's install a better tree conflict. */
2131
2132           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2133                                                       &move_src_op_root_abspath,
2134                                                       eb->db,
2135                                                       db->local_abspath,
2136                                                       tree_conflict,
2137                                                       db->pool, db->pool));
2138
2139           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2140
2141           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2142                                         tree_conflict,
2143                                         eb->db, db->local_abspath,
2144                                         reason, svn_wc_conflict_action_replace,
2145                                         move_src_op_root_abspath,
2146                                         db->pool, db->pool));
2147
2148           /* And now stop checking for conflicts here and just perform
2149              a shadowed update */
2150           db->edit_conflict = tree_conflict; /* Cache for close_directory */
2151           tree_conflict = NULL; /* No direct notification */
2152           db->shadowed = TRUE; /* Just continue */
2153           conflicted = FALSE; /* No skip */
2154         }
2155       else
2156         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2157                                         eb->db, db->local_abspath, pool));
2158     }
2159
2160   /* Now the "usual" behaviour if already conflicted. Skip it. */
2161   if (conflicted)
2162     {
2163       /* Record this conflict so that its descendants are skipped silently. */
2164       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2165
2166       db->skip_this = TRUE;
2167       db->already_notified = TRUE;
2168
2169       /* We skip this node, but once the update completes the parent node will
2170          be updated to the new revision. So a future recursive update of the
2171          parent will not bring in this new node as the revision of the parent
2172          describes to the repository that all children are available.
2173
2174          To resolve this problem, we add a not-present node to allow bringing
2175          the node in once this conflict is resolved.
2176
2177          Note that we can safely assume that no present base node exists,
2178          because then we would not have received an add_directory.
2179        */
2180       SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2181                                                    db->new_relpath,
2182                                                    eb->repos_root,
2183                                                    eb->repos_uuid,
2184                                                    *eb->target_revision,
2185                                                    svn_node_dir,
2186                                                    NULL, NULL,
2187                                                    pool));
2188
2189       /* ### TODO: Also print victim_path in the skip msg. */
2190       do_notification(eb, db->local_abspath, svn_node_dir,
2191                       svn_wc_notify_skip_conflicted, pool);
2192       return SVN_NO_ERROR;
2193     }
2194   else if (conflict_ignored)
2195     {
2196       db->shadowed = TRUE;
2197     }
2198
2199   if (db->shadowed)
2200     {
2201       /* Nothing to check; does not and will not exist in working copy */
2202     }
2203   else if (versioned_locally_and_present)
2204     {
2205       /* What to do with a versioned or schedule-add dir:
2206
2207          A dir already added without history is OK.  Set add_existed
2208          so that user notification is delayed until after any prop
2209          conflicts have been found.
2210
2211          An existing versioned dir is an error.  In the future we may
2212          relax this restriction and simply update such dirs.
2213
2214          A dir added with history is a tree conflict. */
2215
2216       svn_boolean_t local_is_non_dir;
2217       svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2218
2219       /* Is the local add a copy? */
2220       if (status == svn_wc__db_status_added)
2221         SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2222                                          NULL, NULL, NULL, NULL,
2223                                          eb->db, db->local_abspath,
2224                                          pool, pool));
2225
2226
2227       /* Is there *something* that is not a dir? */
2228       local_is_non_dir = (wc_kind != svn_node_dir
2229                           && status != svn_wc__db_status_deleted);
2230
2231       /* Do tree conflict checking if
2232        *  - if there is a local copy.
2233        *  - if this is a switch operation
2234        *  - the node kinds mismatch
2235        *
2236        * During switch, local adds at the same path as incoming adds get
2237        * "lost" in that switching back to the original will no longer have the
2238        * local add. So switch always alerts the user with a tree conflict. */
2239       if (!eb->adds_as_modification
2240           || local_is_non_dir
2241           || add_status != svn_wc__db_status_added)
2242         {
2243           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2244                                       db->local_abspath,
2245                                       status, FALSE, svn_node_none,
2246                                       svn_wc_conflict_action_add,
2247                                       pool, pool));
2248         }
2249
2250       if (tree_conflict == NULL)
2251         db->add_existed = TRUE; /* Take over WORKING */
2252       else
2253         db->shadowed = TRUE; /* Only update BASE */
2254     }
2255   else if (kind != svn_node_none)
2256     {
2257       /* There's an unversioned node at this path. */
2258       db->obstruction_found = TRUE;
2259
2260       /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2261        * if unversioned obstructions are allowed. */
2262       if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2263         {
2264           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2265           db->shadowed = TRUE;
2266
2267           /* Mark a conflict */
2268           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2269
2270           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2271                                         tree_conflict,
2272                                         eb->db, db->local_abspath,
2273                                         svn_wc_conflict_reason_unversioned,
2274                                         svn_wc_conflict_action_add, NULL,
2275                                         db->pool, pool));
2276           db->edit_conflict = tree_conflict;
2277         }
2278     }
2279
2280   if (tree_conflict)
2281     SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2282                               db->old_repos_relpath, db->old_revision,
2283                               db->new_relpath,
2284                               wc_kind,
2285                               svn_node_dir,
2286                               db->pool, pool));
2287
2288   SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2289                                      eb->db, db->local_abspath,
2290                                      db->new_relpath,
2291                                      eb->repos_root,
2292                                      eb->repos_uuid,
2293                                      *eb->target_revision,
2294                                      db->ambient_depth,
2295                                      (db->shadowed && db->obstruction_found),
2296                                      (! db->shadowed
2297                                       && status == svn_wc__db_status_added),
2298                                      tree_conflict, NULL,
2299                                      pool));
2300
2301   /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2302      updating the DB */
2303   if (!db->shadowed)
2304     SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2305
2306   if (tree_conflict != NULL)
2307     {
2308       if (eb->conflict_func)
2309         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2310                                                  tree_conflict,
2311                                                  NULL /* merge_options */,
2312                                                  eb->conflict_func,
2313                                                  eb->conflict_baton,
2314                                                  eb->cancel_func,
2315                                                  eb->cancel_baton,
2316                                                  pool));
2317
2318       db->already_notified = TRUE;
2319       do_notification(eb, db->local_abspath, svn_node_dir,
2320                       svn_wc_notify_tree_conflict, pool);
2321     }
2322
2323
2324   /* If this add was obstructed by dir scheduled for addition without
2325      history let close_directory() handle the notification because there
2326      might be properties to deal with.  If PATH was added inside a locally
2327      deleted tree, then suppress notification, a tree conflict was already
2328      issued. */
2329   if (eb->notify_func && !db->already_notified && !db->add_existed)
2330     {
2331       svn_wc_notify_action_t action;
2332
2333       if (db->shadowed)
2334         action = svn_wc_notify_update_shadowed_add;
2335       else if (db->obstruction_found || db->add_existed)
2336         action = svn_wc_notify_exists;
2337       else
2338         action = svn_wc_notify_update_add;
2339
2340       db->already_notified = TRUE;
2341
2342       do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2343     }
2344
2345   return SVN_NO_ERROR;
2346 }
2347
2348 /* An svn_delta_editor_t function. */
2349 static svn_error_t *
2350 open_directory(const char *path,
2351                void *parent_baton,
2352                svn_revnum_t base_revision,
2353                apr_pool_t *pool,
2354                void **child_baton)
2355 {
2356   struct dir_baton *db, *pb = parent_baton;
2357   struct edit_baton *eb = pb->edit_baton;
2358   svn_boolean_t have_work;
2359   svn_boolean_t conflicted;
2360   svn_boolean_t conflict_ignored = FALSE;
2361   svn_skel_t *tree_conflict = NULL;
2362   svn_wc__db_status_t status, base_status;
2363   svn_node_kind_t wc_kind;
2364
2365   SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2366   *child_baton = db;
2367
2368   if (db->skip_this)
2369     return SVN_NO_ERROR;
2370
2371   /* Detect obstructing working copies */
2372   {
2373     svn_boolean_t is_root;
2374
2375     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2376                                  pool));
2377
2378     if (is_root)
2379       {
2380         /* Just skip this node; a future update will handle it */
2381         SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2382         db->skip_this = TRUE;
2383         db->already_notified = TRUE;
2384
2385         do_notification(eb, db->local_abspath, svn_node_dir,
2386                         svn_wc_notify_update_skip_obstruction, pool);
2387
2388         return SVN_NO_ERROR;
2389       }
2390   }
2391
2392   /* We should have a write lock on every directory touched.  */
2393   SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2394
2395   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2396                                &db->old_repos_relpath, NULL, NULL,
2397                                &db->changed_rev, &db->changed_date,
2398                                &db->changed_author, &db->ambient_depth,
2399                                NULL, NULL, NULL, NULL,
2400                                NULL, NULL, NULL, NULL, NULL, NULL,
2401                                &conflicted, NULL, NULL, NULL,
2402                                NULL, NULL, &have_work,
2403                                eb->db, db->local_abspath,
2404                                db->pool, pool));
2405
2406   if (!have_work)
2407     base_status = status;
2408   else
2409     SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2410                                      &db->old_repos_relpath, NULL, NULL,
2411                                      &db->changed_rev, &db->changed_date,
2412                                      &db->changed_author, &db->ambient_depth,
2413                                      NULL, NULL, NULL, NULL, NULL, NULL,
2414                                      eb->db, db->local_abspath,
2415                                      db->pool, pool));
2416
2417   db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2418
2419   /* Is this path a conflict victim? */
2420   if (db->shadowed)
2421     conflicted = FALSE; /* Conflict applies to WORKING */
2422   else if (conflicted)
2423     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2424                                     eb->db, db->local_abspath, pool));
2425   if (conflicted)
2426     {
2427       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2428
2429       db->skip_this = TRUE;
2430       db->already_notified = TRUE;
2431
2432       do_notification(eb, db->local_abspath, svn_node_unknown,
2433                       svn_wc_notify_skip_conflicted, pool);
2434
2435       return SVN_NO_ERROR;
2436     }
2437   else if (conflict_ignored)
2438     {
2439       db->shadowed = TRUE;
2440     }
2441
2442   /* Is this path a fresh tree conflict victim?  If so, skip the tree
2443      with one notification. */
2444
2445   /* Check for conflicts only when we haven't already recorded
2446    * a tree-conflict on a parent node. */
2447   if (!db->shadowed)
2448     SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2449                                 status, TRUE, svn_node_dir,
2450                                 svn_wc_conflict_action_edit,
2451                                 db->pool, pool));
2452
2453   /* Remember the roots of any locally deleted trees. */
2454   if (tree_conflict != NULL)
2455     {
2456       svn_wc_conflict_reason_t reason;
2457       db->edit_conflict = tree_conflict;
2458       /* Other modifications wouldn't be a tree conflict */
2459
2460       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2461                                                   eb->db, db->local_abspath,
2462                                                   tree_conflict,
2463                                                   db->pool, db->pool));
2464       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2465                      || reason == svn_wc_conflict_reason_moved_away
2466                      || reason == svn_wc_conflict_reason_replaced
2467                      || reason == svn_wc_conflict_reason_obstructed);
2468
2469       /* Continue updating BASE */
2470       if (reason == svn_wc_conflict_reason_obstructed)
2471         db->edit_obstructed = TRUE;
2472       else
2473         db->shadowed = TRUE;
2474     }
2475
2476   /* Mark directory as being at target_revision and URL, but incomplete. */
2477   SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2478                                                     db->new_relpath,
2479                                                     *eb->target_revision,
2480                                                     pool));
2481
2482   return SVN_NO_ERROR;
2483 }
2484
2485
2486 /* An svn_delta_editor_t function. */
2487 static svn_error_t *
2488 change_dir_prop(void *dir_baton,
2489                 const char *name,
2490                 const svn_string_t *value,
2491                 apr_pool_t *pool)
2492 {
2493   svn_prop_t *propchange;
2494   struct dir_baton *db = dir_baton;
2495
2496   if (db->skip_this)
2497     return SVN_NO_ERROR;
2498
2499   propchange = apr_array_push(db->propchanges);
2500   propchange->name = apr_pstrdup(db->pool, name);
2501   propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2502
2503   if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2504     SVN_ERR(mark_directory_edited(db, pool));
2505
2506   return SVN_NO_ERROR;
2507 }
2508
2509 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2510    to the SVN_PROP_EXTERNALS property, return that change, else return
2511    null.  If PROPCHANGES contains more than one such change, return
2512    the first. */
2513 static const svn_prop_t *
2514 externals_prop_changed(const apr_array_header_t *propchanges)
2515 {
2516   int i;
2517
2518   for (i = 0; i < propchanges->nelts; i++)
2519     {
2520       const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2521       if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2522         return p;
2523     }
2524
2525   return NULL;
2526 }
2527
2528
2529
2530 /* An svn_delta_editor_t function. */
2531 static svn_error_t *
2532 close_directory(void *dir_baton,
2533                 apr_pool_t *pool)
2534 {
2535   struct dir_baton *db = dir_baton;
2536   struct edit_baton *eb = db->edit_baton;
2537   svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2538   apr_array_header_t *entry_prop_changes;
2539   apr_array_header_t *dav_prop_changes;
2540   apr_array_header_t *regular_prop_changes;
2541   apr_hash_t *base_props;
2542   apr_hash_t *actual_props;
2543   apr_hash_t *new_base_props = NULL;
2544   apr_hash_t *new_actual_props = NULL;
2545   svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2546   apr_time_t new_changed_date = 0;
2547   const char *new_changed_author = NULL;
2548   apr_pool_t *scratch_pool = db->pool;
2549   svn_skel_t *all_work_items = NULL;
2550   svn_skel_t *conflict_skel = NULL;
2551
2552   /* Skip if we're in a conflicted tree. */
2553   if (db->skip_this)
2554     {
2555       /* Allow the parent to complete its update. */
2556       SVN_ERR(maybe_release_dir_info(db));
2557
2558       return SVN_NO_ERROR;
2559     }
2560
2561   if (db->edited)
2562     conflict_skel = db->edit_conflict;
2563
2564   SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2565                                &dav_prop_changes, &regular_prop_changes, pool));
2566
2567   /* Fetch the existing properties.  */
2568   if ((!db->adding_dir || db->add_existed)
2569       && !db->shadowed)
2570     {
2571       SVN_ERR(svn_wc__get_actual_props(&actual_props,
2572                                        eb->db, db->local_abspath,
2573                                        scratch_pool, scratch_pool));
2574     }
2575   else
2576     actual_props = apr_hash_make(pool);
2577
2578   if (db->add_existed)
2579     {
2580       /* This node already exists. Grab the current pristine properties. */
2581       SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2582                                              eb->db, db->local_abspath,
2583                                              scratch_pool, scratch_pool));
2584     }
2585   else if (!db->adding_dir)
2586     {
2587       /* Get the BASE properties for proper merging. */
2588       SVN_ERR(svn_wc__db_base_get_props(&base_props,
2589                                         eb->db, db->local_abspath,
2590                                         scratch_pool, scratch_pool));
2591     }
2592   else
2593     base_props = apr_hash_make(pool);
2594
2595   /* An incomplete directory might have props which were supposed to be
2596      deleted but weren't.  Because the server sent us all the props we're
2597      supposed to have, any previous base props not in this list must be
2598      deleted (issue #1672). */
2599   if (db->was_incomplete)
2600     {
2601       int i;
2602       apr_hash_t *props_to_delete;
2603       apr_hash_index_t *hi;
2604
2605       /* In a copy of the BASE props, remove every property that we see an
2606          incoming change for. The remaining unmentioned properties are those
2607          which need to be deleted.  */
2608       props_to_delete = apr_hash_copy(pool, base_props);
2609       for (i = 0; i < regular_prop_changes->nelts; i++)
2610         {
2611           const svn_prop_t *prop;
2612           prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2613           svn_hash_sets(props_to_delete, prop->name, NULL);
2614         }
2615
2616       /* Add these props to the incoming propchanges (in
2617        * regular_prop_changes).  */
2618       for (hi = apr_hash_first(pool, props_to_delete);
2619            hi != NULL;
2620            hi = apr_hash_next(hi))
2621         {
2622           const char *propname = svn__apr_hash_index_key(hi);
2623           svn_prop_t *prop = apr_array_push(regular_prop_changes);
2624
2625           /* Record a deletion for PROPNAME.  */
2626           prop->name = propname;
2627           prop->value = NULL;
2628         }
2629     }
2630
2631   /* If this directory has property changes stored up, now is the time
2632      to deal with them. */
2633   if (regular_prop_changes->nelts)
2634     {
2635       /* If recording traversal info, then see if the
2636          SVN_PROP_EXTERNALS property on this directory changed,
2637          and record before and after for the change. */
2638       if (eb->external_func)
2639         {
2640           const svn_prop_t *change
2641             = externals_prop_changed(regular_prop_changes);
2642
2643           if (change)
2644             {
2645               const svn_string_t *new_val_s = change->value;
2646               const svn_string_t *old_val_s;
2647
2648               old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2649
2650               if ((new_val_s == NULL) && (old_val_s == NULL))
2651                 ; /* No value before, no value after... so do nothing. */
2652               else if (new_val_s && old_val_s
2653                        && (svn_string_compare(old_val_s, new_val_s)))
2654                 ; /* Value did not change... so do nothing. */
2655               else if (old_val_s || new_val_s)
2656                 /* something changed, record the change */
2657                 {
2658                   SVN_ERR((eb->external_func)(
2659                                        eb->external_baton,
2660                                        db->local_abspath,
2661                                        old_val_s,
2662                                        new_val_s,
2663                                        db->ambient_depth,
2664                                        db->pool));
2665                 }
2666             }
2667         }
2668
2669       if (db->shadowed)
2670         {
2671           /* We don't have a relevant actual row, but we need actual properties
2672              to allow property merging without conflicts. */
2673           if (db->adding_dir)
2674             actual_props = apr_hash_make(scratch_pool);
2675           else
2676             actual_props = base_props;
2677         }
2678
2679       /* Merge pending properties. */
2680       new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2681                                        db->pool);
2682       SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2683                                     &prop_state,
2684                                     &new_actual_props,
2685                                     eb->db,
2686                                     db->local_abspath,
2687                                     NULL /* use baseprops */,
2688                                     base_props,
2689                                     actual_props,
2690                                     regular_prop_changes,
2691                                     db->pool,
2692                                     scratch_pool),
2693                 _("Couldn't do property merge"));
2694       /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2695       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2696     }
2697
2698   SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2699                                  &new_changed_author, entry_prop_changes,
2700                                  scratch_pool, scratch_pool));
2701
2702   /* Check if we should add some not-present markers before marking the
2703      directory complete (Issue #3569) */
2704   {
2705     apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2706
2707     if (new_children != NULL)
2708       {
2709         apr_hash_index_t *hi;
2710         apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2711
2712         for (hi = apr_hash_first(scratch_pool, new_children);
2713              hi;
2714              hi = apr_hash_next(hi))
2715           {
2716             const char *child_name;
2717             const char *child_abspath;
2718             const char *child_relpath;
2719             const svn_dirent_t *dirent;
2720             svn_wc__db_status_t status;
2721             svn_node_kind_t child_kind;
2722             svn_error_t *err;
2723
2724             svn_pool_clear(iterpool);
2725
2726             child_name = svn__apr_hash_index_key(hi);
2727             child_abspath = svn_dirent_join(db->local_abspath, child_name,
2728                                             iterpool);
2729
2730             dirent = svn__apr_hash_index_val(hi);
2731             child_kind = (dirent->kind == svn_node_dir)
2732                                         ? svn_node_dir
2733                                         : svn_node_file;
2734
2735             if (db->ambient_depth < svn_depth_immediates
2736                 && child_kind == svn_node_dir)
2737               continue; /* We don't need the subdirs */
2738
2739             /* ### We just check if there is some node in BASE at this path */
2740             err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2741                                            NULL, NULL, NULL, NULL, NULL, NULL,
2742                                            NULL, NULL, NULL, NULL, NULL,
2743                                            eb->db, child_abspath,
2744                                            iterpool, iterpool);
2745
2746             if (!err)
2747               {
2748                 svn_boolean_t is_wcroot;
2749                 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2750                                              iterpool));
2751
2752                 if (!is_wcroot)
2753                   continue; /* Everything ok... Nothing to do here */
2754                 /* Fall through to allow recovering later */
2755               }
2756             else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2757               return svn_error_trace(err);
2758
2759             svn_error_clear(err);
2760
2761             child_relpath = svn_relpath_join(db->new_relpath, child_name,
2762                                              iterpool);
2763
2764             SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2765                                                          child_abspath,
2766                                                          child_relpath,
2767                                                          eb->repos_root,
2768                                                          eb->repos_uuid,
2769                                                          *eb->target_revision,
2770                                                          child_kind,
2771                                                          NULL, NULL,
2772                                                          iterpool));
2773           }
2774
2775         svn_pool_destroy(iterpool);
2776       }
2777   }
2778
2779   if (apr_hash_count(db->not_present_files))
2780     {
2781       apr_hash_index_t *hi;
2782       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2783
2784       /* This should call some new function (which could also be used
2785          for new_children above) to add all the names in single
2786          transaction, but I can't even trigger it.  I've tried
2787          ra_local, ra_svn, ra_neon, ra_serf and they all call
2788          close_file before close_dir. */
2789       for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2790            hi;
2791            hi = apr_hash_next(hi))
2792         {
2793           const char *child = svn__apr_hash_index_key(hi);
2794           const char *child_abspath, *child_relpath;
2795
2796           svn_pool_clear(iterpool);
2797
2798           child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2799           child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2800
2801           SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2802                                                        child_abspath,
2803                                                        child_relpath,
2804                                                        eb->repos_root,
2805                                                        eb->repos_uuid,
2806                                                        *eb->target_revision,
2807                                                        svn_node_file,
2808                                                        NULL, NULL,
2809                                                        iterpool));
2810         }
2811       svn_pool_destroy(iterpool);
2812     }
2813
2814   /* If this directory is merely an anchor for a targeted child, then we
2815      should not be updating the node at all.  */
2816   if (db->parent_baton == NULL
2817       && *eb->target_basename != '\0')
2818     {
2819       /* And we should not have received any changes!  */
2820       SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2821       /* ... which also implies NEW_CHANGED_* are not set,
2822          and NEW_BASE_PROPS == NULL.  */
2823     }
2824   else
2825     {
2826       apr_hash_t *props;
2827       apr_array_header_t *iprops = NULL;
2828
2829       /* ### we know a base node already exists. it was created in
2830          ### open_directory or add_directory.  let's just preserve the
2831          ### existing DEPTH value, and possibly CHANGED_*.  */
2832       /* If we received any changed_* values, then use them.  */
2833       if (SVN_IS_VALID_REVNUM(new_changed_rev))
2834         db->changed_rev = new_changed_rev;
2835       if (new_changed_date != 0)
2836         db->changed_date = new_changed_date;
2837       if (new_changed_author != NULL)
2838         db->changed_author = new_changed_author;
2839
2840       /* If no depth is set yet, set to infinity. */
2841       if (db->ambient_depth == svn_depth_unknown)
2842         db->ambient_depth = svn_depth_infinity;
2843
2844       if (eb->depth_is_sticky
2845           && db->ambient_depth != eb->requested_depth)
2846         {
2847           /* After a depth upgrade the entry must reflect the new depth.
2848              Upgrading to infinity changes the depth of *all* directories,
2849              upgrading to something else only changes the target. */
2850
2851           if (eb->requested_depth == svn_depth_infinity
2852               || (strcmp(db->local_abspath, eb->target_abspath) == 0
2853                   && eb->requested_depth > db->ambient_depth))
2854             {
2855               db->ambient_depth = eb->requested_depth;
2856             }
2857         }
2858
2859       /* Do we have new properties to install? Or shall we simply retain
2860          the prior set of properties? If we're installing new properties,
2861          then we also want to write them to an old-style props file.  */
2862       props = new_base_props;
2863       if (props == NULL)
2864         props = base_props;
2865
2866       if (conflict_skel)
2867         {
2868           svn_skel_t *work_item;
2869
2870           SVN_ERR(complete_conflict(conflict_skel,
2871                                     db->edit_baton,
2872                                     db->local_abspath,
2873                                     db->old_repos_relpath,
2874                                     db->old_revision,
2875                                     db->new_relpath,
2876                                     svn_node_dir, svn_node_dir,
2877                                     db->pool, scratch_pool));
2878
2879           SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2880                                                   eb->db, db->local_abspath,
2881                                                   conflict_skel,
2882                                                   scratch_pool, scratch_pool));
2883
2884           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2885                                             scratch_pool);
2886         }
2887
2888       /* Any inherited props to be set set for this base node? */
2889       if (eb->wcroot_iprops)
2890         {
2891           iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2892
2893           /* close_edit may also update iprops for switched nodes, catching
2894              those for which close_directory is never called (e.g. a switch
2895              with no changes).  So as a minor optimization we remove any
2896              iprops from the hash so as not to set them again in
2897              close_edit. */
2898           if (iprops)
2899             svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2900         }
2901
2902       /* Update the BASE data for the directory and mark the directory
2903          complete */
2904       SVN_ERR(svn_wc__db_base_add_directory(
2905                 eb->db, db->local_abspath,
2906                 eb->wcroot_abspath,
2907                 db->new_relpath,
2908                 eb->repos_root, eb->repos_uuid,
2909                 *eb->target_revision,
2910                 props,
2911                 db->changed_rev, db->changed_date, db->changed_author,
2912                 NULL /* children */,
2913                 db->ambient_depth,
2914                 (dav_prop_changes->nelts > 0)
2915                     ? svn_prop_array_to_hash(dav_prop_changes, pool)
2916                     : NULL,
2917                 conflict_skel,
2918                 (! db->shadowed) && new_base_props != NULL,
2919                 new_actual_props,
2920                 iprops, all_work_items,
2921                 scratch_pool));
2922     }
2923
2924   /* Process all of the queued work items for this directory.  */
2925   SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2926                          eb->cancel_func, eb->cancel_baton,
2927                          scratch_pool));
2928
2929   if (conflict_skel && eb->conflict_func)
2930     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2931                                              conflict_skel,
2932                                              NULL /* merge_options */,
2933                                              eb->conflict_func,
2934                                              eb->conflict_baton,
2935                                              eb->cancel_func,
2936                                              eb->cancel_baton,
2937                                              scratch_pool));
2938
2939   /* Notify of any prop changes on this directory -- but do nothing if
2940      it's an added or skipped directory, because notification has already
2941      happened in that case - unless the add was obstructed by a dir
2942      scheduled for addition without history, in which case we handle
2943      notification here). */
2944   if (!db->already_notified && eb->notify_func && db->edited)
2945     {
2946       svn_wc_notify_t *notify;
2947       svn_wc_notify_action_t action;
2948
2949       if (db->shadowed || db->edit_obstructed)
2950         action = svn_wc_notify_update_shadowed_update;
2951       else if (db->obstruction_found || db->add_existed)
2952         action = svn_wc_notify_exists;
2953       else
2954         action = svn_wc_notify_update_update;
2955
2956       notify = svn_wc_create_notify(db->local_abspath, action, pool);
2957       notify->kind = svn_node_dir;
2958       notify->prop_state = prop_state;
2959       notify->revision = *eb->target_revision;
2960       notify->old_revision = db->old_revision;
2961
2962       eb->notify_func(eb->notify_baton, notify, scratch_pool);
2963     }
2964
2965   /* We're done with this directory, so remove one reference from the
2966      bump information. */
2967   SVN_ERR(maybe_release_dir_info(db));
2968
2969   return SVN_NO_ERROR;
2970 }
2971
2972
2973 /* Common code for 'absent_file' and 'absent_directory'. */
2974 static svn_error_t *
2975 absent_node(const char *path,
2976             svn_node_kind_t absent_kind,
2977             void *parent_baton,
2978             apr_pool_t *pool)
2979 {
2980   struct dir_baton *pb = parent_baton;
2981   struct edit_baton *eb = pb->edit_baton;
2982   apr_pool_t *scratch_pool = svn_pool_create(pool);
2983   const char *name = svn_dirent_basename(path, NULL);
2984   const char *local_abspath;
2985   svn_error_t *err;
2986   svn_wc__db_status_t status;
2987   svn_node_kind_t kind;
2988
2989   if (pb->skip_this)
2990     return SVN_NO_ERROR;
2991
2992   SVN_ERR(mark_directory_edited(pb, scratch_pool));
2993
2994   local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2995
2996   /* If an item by this name is scheduled for addition that's a
2997      genuine tree-conflict.  */
2998   err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2999                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3000                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3001                              NULL, NULL, NULL, NULL,
3002                              eb->db, local_abspath,
3003                              scratch_pool, scratch_pool);
3004
3005   if (err)
3006     {
3007       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3008         return svn_error_trace(err);
3009
3010       svn_error_clear(err);
3011       status = svn_wc__db_status_not_present;
3012       kind = svn_node_unknown;
3013     }
3014
3015   if (status == svn_wc__db_status_normal)
3016     {
3017       svn_boolean_t wcroot;
3018       /* We found an obstructing working copy or a file external! */
3019
3020       SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
3021                                    scratch_pool));
3022
3023       if (wcroot)
3024         {
3025           /*
3026              We have an obstructing working copy; possibly a directory external
3027
3028              We can do two things now:
3029              1) notify the user, record a skip, etc.
3030              2) Just record the absent node in BASE in the parent
3031                 working copy.
3032
3033              As option 2 happens to be exactly what we do anyway, fall through.
3034            */
3035         }
3036       else
3037         {
3038           /* The server asks us to replace a file external
3039              (Existing BASE node; not reported by the working copy crawler or
3040               there would have been a delete_entry() call.
3041
3042              There is no way we can store this state in the working copy as
3043              the BASE layer is already filled.
3044
3045              We could error out, but that is not helping anybody; the user is not
3046              even seeing with what the file external would be replaced, so let's
3047              report a skip and continue the update.
3048            */
3049
3050           if (eb->notify_func)
3051             {
3052               svn_wc_notify_t *notify;
3053               notify = svn_wc_create_notify(
3054                                     local_abspath,
3055                                     svn_wc_notify_update_skip_obstruction,
3056                                     scratch_pool);
3057
3058               eb->notify_func(eb->notify_baton, notify, scratch_pool);
3059             }
3060
3061           svn_pool_destroy(scratch_pool);
3062           return SVN_NO_ERROR;
3063         }
3064     }
3065   else if (status == svn_wc__db_status_not_present
3066            || status == svn_wc__db_status_server_excluded
3067            || status == svn_wc__db_status_excluded)
3068     {
3069       /* The BASE node is not actually there, so we can safely turn it into
3070          an absent node */
3071     }
3072   else
3073     {
3074       /* We have a local addition. If this would be a BASE node it would have
3075          been deleted before we get here. (Which might have turned it into
3076          a copy).
3077
3078          ### This should be recorded as a tree conflict and the update
3079          ### can just continue, as we can just record the absent status
3080          ### in BASE.
3081        */
3082       SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3083
3084       return svn_error_createf(
3085          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3086          _("Failed to mark '%s' absent: item of the same name is already "
3087            "scheduled for addition"),
3088          svn_dirent_local_style(local_abspath, pool));
3089     }
3090
3091   {
3092     const char *repos_relpath;
3093     repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3094
3095     /* Insert an excluded node below the parent node to note that this child
3096        is absent. (This puts it in the parent db if the child is obstructed) */
3097     SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3098                                               repos_relpath, eb->repos_root,
3099                                               eb->repos_uuid,
3100                                               *(eb->target_revision),
3101                                               absent_kind,
3102                                               svn_wc__db_status_server_excluded,
3103                                               NULL, NULL,
3104                                               scratch_pool));
3105   }
3106
3107   svn_pool_destroy(scratch_pool);
3108
3109   return SVN_NO_ERROR;
3110 }
3111
3112
3113 /* An svn_delta_editor_t function. */
3114 static svn_error_t *
3115 absent_file(const char *path,
3116             void *parent_baton,
3117             apr_pool_t *pool)
3118 {
3119   return absent_node(path, svn_node_file, parent_baton, pool);
3120 }
3121
3122
3123 /* An svn_delta_editor_t function. */
3124 static svn_error_t *
3125 absent_directory(const char *path,
3126                  void *parent_baton,
3127                  apr_pool_t *pool)
3128 {
3129   return absent_node(path, svn_node_dir, parent_baton, pool);
3130 }
3131
3132
3133 /* An svn_delta_editor_t function. */
3134 static svn_error_t *
3135 add_file(const char *path,
3136          void *parent_baton,
3137          const char *copyfrom_path,
3138          svn_revnum_t copyfrom_rev,
3139          apr_pool_t *pool,
3140          void **file_baton)
3141 {
3142   struct dir_baton *pb = parent_baton;
3143   struct edit_baton *eb = pb->edit_baton;
3144   struct file_baton *fb;
3145   svn_node_kind_t kind = svn_node_none;
3146   svn_node_kind_t wc_kind = svn_node_unknown;
3147   svn_wc__db_status_t status = svn_wc__db_status_normal;
3148   apr_pool_t *scratch_pool;
3149   svn_boolean_t conflicted = FALSE;
3150   svn_boolean_t conflict_ignored = FALSE;
3151   svn_boolean_t versioned_locally_and_present = FALSE;
3152   svn_skel_t *tree_conflict = NULL;
3153   svn_error_t *err = SVN_NO_ERROR;
3154
3155   SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3156
3157   SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3158   *file_baton = fb;
3159
3160   if (fb->skip_this)
3161     return SVN_NO_ERROR;
3162
3163   SVN_ERR(mark_file_edited(fb, pool));
3164
3165   /* The file_pool can stick around for a *long* time, so we want to
3166      use a subpool for any temporary allocations. */
3167   scratch_pool = svn_pool_create(pool);
3168
3169
3170   /* It may not be named the same as the administrative directory. */
3171   if (svn_wc_is_adm_dir(fb->name, pool))
3172     return svn_error_createf(
3173        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3174        _("Failed to add file '%s': object of the same name as the "
3175          "administrative directory"),
3176        svn_dirent_local_style(fb->local_abspath, pool));
3177
3178   if (!eb->clean_checkout)
3179     {
3180       SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3181
3182       err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3183                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3184                                 NULL, NULL, NULL, NULL, NULL,
3185                                 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3186                                 eb->db, fb->local_abspath,
3187                                 scratch_pool, scratch_pool);
3188     }
3189
3190   if (err)
3191     {
3192       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3193         return svn_error_trace(err);
3194
3195       svn_error_clear(err);
3196       wc_kind = svn_node_unknown;
3197       conflicted = FALSE;
3198
3199       versioned_locally_and_present = FALSE;
3200     }
3201   else if (wc_kind == svn_node_dir
3202            && status == svn_wc__db_status_normal)
3203     {
3204       /* !! We found the root of a separate working copy obstructing the wc !!
3205
3206          If the directory would be part of our own working copy then
3207          we wouldn't have been called as an add_file().
3208
3209          The only thing we can do is add a not-present node, to allow
3210          a future update to bring in the new files when the problem is
3211          resolved. */
3212       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3213                     (void *)1);
3214
3215       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3216       fb->skip_this = TRUE;
3217       fb->already_notified = TRUE;
3218
3219       do_notification(eb, fb->local_abspath, svn_node_file,
3220                       svn_wc_notify_update_skip_obstruction, scratch_pool);
3221
3222       svn_pool_destroy(scratch_pool);
3223
3224       return SVN_NO_ERROR;
3225     }
3226   else if (status == svn_wc__db_status_normal
3227            && (wc_kind == svn_node_file
3228                || wc_kind == svn_node_symlink))
3229     {
3230       /* We found a file external occupating the place we need in BASE.
3231
3232          We can't add a not-present node in this case as that would overwrite
3233          the file external. Luckily the file external itself stops us from
3234          forgetting a child of this parent directory like an obstructing
3235          working copy would.
3236
3237          The reason we get here is that the adm crawler doesn't report
3238          file externals.
3239       */
3240       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3241       fb->skip_this = TRUE;
3242       fb->already_notified = TRUE;
3243
3244       do_notification(eb, fb->local_abspath, svn_node_file,
3245                       svn_wc_notify_update_skip_obstruction, scratch_pool);
3246
3247       svn_pool_destroy(scratch_pool);
3248
3249       return SVN_NO_ERROR;
3250     }
3251   else if (wc_kind == svn_node_unknown)
3252     versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3253   else
3254     versioned_locally_and_present = IS_NODE_PRESENT(status);
3255
3256
3257   /* Is this path a conflict victim? */
3258   if (fb->shadowed)
3259     conflicted = FALSE; /* Conflict applies to WORKING */
3260   else if (conflicted)
3261     {
3262       if (pb->deletion_conflicts)
3263         tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3264
3265       if (tree_conflict)
3266         {
3267           svn_wc_conflict_reason_t reason;
3268           const char *move_src_op_root_abspath;
3269           /* So this deletion wasn't just a deletion, it is actually a
3270              replacement. Let's install a better tree conflict. */
3271
3272           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3273                                                       &move_src_op_root_abspath,
3274                                                       eb->db,
3275                                                       fb->local_abspath,
3276                                                       tree_conflict,
3277                                                       fb->pool, fb->pool));
3278
3279           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3280
3281           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3282                                         tree_conflict,
3283                                         eb->db, fb->local_abspath,
3284                                         reason, svn_wc_conflict_action_replace,
3285                                         move_src_op_root_abspath,
3286                                         fb->pool, fb->pool));
3287
3288           /* And now stop checking for conflicts here and just perform
3289              a shadowed update */
3290           fb->edit_conflict = tree_conflict; /* Cache for close_file */
3291           tree_conflict = NULL; /* No direct notification */
3292           fb->shadowed = TRUE; /* Just continue */
3293           conflicted = FALSE; /* No skip */
3294         }
3295       else
3296         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3297                                         eb->db, fb->local_abspath, pool));
3298     }
3299
3300   /* Now the usual conflict handling: skip. */
3301   if (conflicted)
3302     {
3303       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3304
3305       fb->skip_this = TRUE;
3306       fb->already_notified = TRUE;
3307
3308       /* We skip this node, but once the update completes the parent node will
3309          be updated to the new revision. So a future recursive update of the
3310          parent will not bring in this new node as the revision of the parent
3311          describes to the repository that all children are available.
3312
3313          To resolve this problem, we add a not-present node to allow bringing
3314          the node in once this conflict is resolved.
3315
3316          Note that we can safely assume that no present base node exists,
3317          because then we would not have received an add_file.
3318        */
3319       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3320                     (void *)1);
3321
3322       do_notification(eb, fb->local_abspath, svn_node_unknown,
3323                       svn_wc_notify_skip_conflicted, scratch_pool);
3324
3325       svn_pool_destroy(scratch_pool);
3326
3327       return SVN_NO_ERROR;
3328     }
3329   else if (conflict_ignored)
3330     {
3331       fb->shadowed = TRUE;
3332     }
3333
3334   if (fb->shadowed)
3335     {
3336       /* Nothing to check; does not and will not exist in working copy */
3337     }
3338   else if (versioned_locally_and_present)
3339     {
3340       /* What to do with a versioned or schedule-add file:
3341
3342          If the UUID doesn't match the parent's, or the URL isn't a child of
3343          the parent dir's URL, it's an error.
3344
3345          Set add_existed so that user notification is delayed until after any
3346          text or prop conflicts have been found.
3347
3348          Whether the incoming add is a symlink or a file will only be known in
3349          close_file(), when the props are known. So with a locally added file
3350          or symlink, let close_file() check for a tree conflict.
3351
3352          We will never see missing files here, because these would be
3353          re-added during the crawler phase. */
3354       svn_boolean_t local_is_file;
3355
3356       /* Is the local node a copy or move */
3357       if (status == svn_wc__db_status_added)
3358         SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3359                                          NULL, NULL, NULL,
3360                                          eb->db, fb->local_abspath,
3361                                          scratch_pool, scratch_pool));
3362
3363       /* Is there something that is a file? */
3364       local_is_file = (wc_kind == svn_node_file
3365                        || wc_kind == svn_node_symlink);
3366
3367       /* Do tree conflict checking if
3368        *  - if there is a local copy.
3369        *  - if this is a switch operation
3370        *  - the node kinds mismatch
3371        *
3372        * During switch, local adds at the same path as incoming adds get
3373        * "lost" in that switching back to the original will no longer have the
3374        * local add. So switch always alerts the user with a tree conflict. */
3375       if (!eb->adds_as_modification
3376           || !local_is_file
3377           || status != svn_wc__db_status_added)
3378         {
3379           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3380                                       fb->local_abspath,
3381                                       status, FALSE, svn_node_none,
3382                                       svn_wc_conflict_action_add,
3383                                       scratch_pool, scratch_pool));
3384         }
3385
3386       if (tree_conflict == NULL)
3387         fb->add_existed = TRUE; /* Take over WORKING */
3388       else
3389         fb->shadowed = TRUE; /* Only update BASE */
3390
3391     }
3392   else if (kind != svn_node_none)
3393     {
3394       /* There's an unversioned node at this path. */
3395       fb->obstruction_found = TRUE;
3396
3397       /* Unversioned, obstructing files are handled by text merge/conflict,
3398        * if unversioned obstructions are allowed. */
3399       if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3400         {
3401           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3402           fb->shadowed = TRUE;
3403
3404           /* Mark a conflict */
3405           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3406
3407           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3408                                         tree_conflict,
3409                                         eb->db, fb->local_abspath,
3410                                         svn_wc_conflict_reason_unversioned,
3411                                         svn_wc_conflict_action_add,
3412                                         NULL,
3413                                         fb->pool, scratch_pool));
3414         }
3415     }
3416
3417   /* When this is not the update target add a not-present BASE node now,
3418      to allow marking the parent directory complete in its close_edit() call.
3419      This resolves issues when that occurs before the close_file(). */
3420   if (pb->parent_baton
3421       || *eb->target_basename == '\0'
3422       || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3423     {
3424       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3425                     (void *)1);
3426     }
3427
3428   if (tree_conflict != NULL)
3429     {
3430       SVN_ERR(complete_conflict(tree_conflict,
3431                                 fb->edit_baton,
3432                                 fb->local_abspath,
3433                                 fb->old_repos_relpath,
3434                                 fb->old_revision,
3435                                 fb->new_relpath,
3436                                 wc_kind,
3437                                 svn_node_file,
3438                                 fb->pool, scratch_pool));
3439
3440       SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3441                                           fb->local_abspath,
3442                                           tree_conflict, NULL,
3443                                           scratch_pool));
3444
3445       if (eb->conflict_func)
3446         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3447                                                  tree_conflict,
3448                                                  NULL /* merge_options */,
3449                                                  eb->conflict_func,
3450                                                  eb->conflict_baton,
3451                                                  eb->cancel_func,
3452                                                  eb->cancel_baton,
3453                                                  scratch_pool));
3454
3455       fb->already_notified = TRUE;
3456       do_notification(eb, fb->local_abspath, svn_node_file,
3457                       svn_wc_notify_tree_conflict, scratch_pool);
3458     }
3459
3460   svn_pool_destroy(scratch_pool);
3461
3462   return SVN_NO_ERROR;
3463 }
3464
3465
3466 /* An svn_delta_editor_t function. */
3467 static svn_error_t *
3468 open_file(const char *path,
3469           void *parent_baton,
3470           svn_revnum_t base_revision,
3471           apr_pool_t *pool,
3472           void **file_baton)
3473 {
3474   struct dir_baton *pb = parent_baton;
3475   struct edit_baton *eb = pb->edit_baton;
3476   struct file_baton *fb;
3477   svn_boolean_t conflicted;
3478   svn_boolean_t conflict_ignored = FALSE;
3479   svn_boolean_t have_work;
3480   svn_wc__db_status_t status;
3481   svn_node_kind_t wc_kind;
3482   svn_skel_t *tree_conflict = NULL;
3483
3484   /* the file_pool can stick around for a *long* time, so we want to use
3485      a subpool for any temporary allocations. */
3486   apr_pool_t *scratch_pool = svn_pool_create(pool);
3487
3488   SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3489   *file_baton = fb;
3490
3491   if (fb->skip_this)
3492     return SVN_NO_ERROR;
3493
3494   /* Detect obstructing working copies */
3495   {
3496     svn_boolean_t is_root;
3497
3498     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3499                                  pool));
3500
3501     if (is_root)
3502       {
3503         /* Just skip this node; a future update will handle it */
3504         SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3505         fb->skip_this = TRUE;
3506         fb->already_notified = TRUE;
3507
3508         do_notification(eb, fb->local_abspath, svn_node_file,
3509                         svn_wc_notify_update_skip_obstruction, pool);
3510
3511         return SVN_NO_ERROR;
3512       }
3513   }
3514
3515   /* Sanity check. */
3516
3517   /* If replacing, make sure the .svn entry already exists. */
3518   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3519                                &fb->old_repos_relpath, NULL, NULL,
3520                                &fb->changed_rev, &fb->changed_date,
3521                                &fb->changed_author, NULL,
3522                                &fb->original_checksum, NULL, NULL, NULL,
3523                                NULL, NULL, NULL, NULL, NULL, NULL,
3524                                &conflicted, NULL, NULL, &fb->local_prop_mods,
3525                                NULL, NULL, &have_work,
3526                                eb->db, fb->local_abspath,
3527                                fb->pool, scratch_pool));
3528
3529   if (have_work)
3530     SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3531                                      &fb->old_repos_relpath, NULL, NULL,
3532                                      &fb->changed_rev, &fb->changed_date,
3533                                      &fb->changed_author, NULL,
3534                                      &fb->original_checksum, NULL, NULL,
3535                                      NULL, NULL, NULL,
3536                                      eb->db, fb->local_abspath,
3537                                      fb->pool, scratch_pool));
3538
3539   /* Is this path a conflict victim? */
3540   if (fb->shadowed)
3541     conflicted = FALSE; /* Conflict applies to WORKING */
3542   else if (conflicted)
3543     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3544                                     eb->db, fb->local_abspath, pool));
3545   if (conflicted)
3546     {
3547       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3548
3549       fb->skip_this = TRUE;
3550       fb->already_notified = TRUE;
3551
3552       do_notification(eb, fb->local_abspath, svn_node_unknown,
3553                       svn_wc_notify_skip_conflicted, scratch_pool);
3554
3555       svn_pool_destroy(scratch_pool);
3556
3557       return SVN_NO_ERROR;
3558     }
3559   else if (conflict_ignored)
3560     {
3561       fb->shadowed = TRUE;
3562     }
3563
3564   /* Check for conflicts only when we haven't already recorded
3565    * a tree-conflict on a parent node. */
3566   if (!fb->shadowed)
3567     SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3568                                 status, TRUE, svn_node_file,
3569                                 svn_wc_conflict_action_edit,
3570                                 fb->pool, scratch_pool));
3571
3572   /* Is this path the victim of a newly-discovered tree conflict? */
3573   if (tree_conflict != NULL)
3574     {
3575       svn_wc_conflict_reason_t reason;
3576       fb->edit_conflict = tree_conflict;
3577       /* Other modifications wouldn't be a tree conflict */
3578
3579       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3580                                                   eb->db, fb->local_abspath,
3581                                                   tree_conflict,
3582                                                   scratch_pool, scratch_pool));
3583       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3584                      || reason == svn_wc_conflict_reason_moved_away
3585                      || reason == svn_wc_conflict_reason_replaced
3586                      || reason == svn_wc_conflict_reason_obstructed);
3587
3588       /* Continue updating BASE */
3589       if (reason == svn_wc_conflict_reason_obstructed)
3590         fb->edit_obstructed = TRUE;
3591       else
3592         fb->shadowed = TRUE;
3593     }
3594
3595   svn_pool_destroy(scratch_pool);
3596
3597   return SVN_NO_ERROR;
3598 }
3599
3600 /* Implements svn_stream_lazyopen_func_t. */
3601 static svn_error_t *
3602 lazy_open_source(svn_stream_t **stream,
3603                  void *baton,
3604                  apr_pool_t *result_pool,
3605                  apr_pool_t *scratch_pool)
3606 {
3607   struct file_baton *fb = baton;
3608
3609   SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3610                                    fb->local_abspath,
3611                                    fb->original_checksum,
3612                                    result_pool, scratch_pool));
3613
3614
3615   return SVN_NO_ERROR;
3616 }
3617
3618 struct lazy_target_baton {
3619   struct file_baton *fb;
3620   struct handler_baton *hb;
3621   struct edit_baton *eb;
3622 };
3623
3624 /* Implements svn_stream_lazyopen_func_t. */
3625 static svn_error_t *
3626 lazy_open_target(svn_stream_t **stream,
3627                  void *baton,
3628                  apr_pool_t *result_pool,
3629                  apr_pool_t *scratch_pool)
3630 {
3631   struct lazy_target_baton *tb = baton;
3632
3633   SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3634                                      NULL, &tb->hb->new_text_base_sha1_checksum,
3635                                      tb->fb->edit_baton->db,
3636                                      tb->eb->wcroot_abspath,
3637                                      result_pool, scratch_pool));
3638
3639   return SVN_NO_ERROR;
3640 }
3641
3642 /* An svn_delta_editor_t function. */
3643 static svn_error_t *
3644 apply_textdelta(void *file_baton,
3645                 const char *expected_checksum,
3646                 apr_pool_t *pool,
3647                 svn_txdelta_window_handler_t *handler,
3648                 void **handler_baton)
3649 {
3650   struct file_baton *fb = file_baton;
3651   apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3652   struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3653   struct edit_baton *eb = fb->edit_baton;
3654   const svn_checksum_t *recorded_base_checksum;
3655   svn_checksum_t *expected_base_checksum;
3656   svn_stream_t *source;
3657   struct lazy_target_baton *tb;
3658   svn_stream_t *target;
3659
3660   if (fb->skip_this)
3661     {
3662       *handler = svn_delta_noop_window_handler;
3663       *handler_baton = NULL;
3664       return SVN_NO_ERROR;
3665     }
3666
3667   SVN_ERR(mark_file_edited(fb, pool));
3668
3669   /* Parse checksum or sets expected_base_checksum to NULL */
3670   SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3671                                  expected_checksum, pool));
3672
3673   /* Before applying incoming svndiff data to text base, make sure
3674      text base hasn't been corrupted, and that its checksum
3675      matches the expected base checksum. */
3676
3677   /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3678      check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3679      for replaced nodes because we didn't store the checksum of the "revert
3680      base".  In WC-NG, we do and we can.) */
3681   recorded_base_checksum = fb->original_checksum;
3682
3683   /* If we have a checksum that we want to compare to a MD5 checksum,
3684      ensure that it is a MD5 checksum */
3685   if (recorded_base_checksum
3686       && expected_base_checksum
3687       && recorded_base_checksum->kind != svn_checksum_md5)
3688     SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3689                                         eb->db, eb->wcroot_abspath,
3690                                         recorded_base_checksum, pool, pool));
3691
3692
3693   if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3694       return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3695                      _("Checksum mismatch for '%s':\n"
3696                        "   expected:  %s\n"
3697                        "   recorded:  %s\n"),
3698                      svn_dirent_local_style(fb->local_abspath, pool),
3699                      svn_checksum_to_cstring_display(expected_base_checksum,
3700                                                      pool),
3701                      svn_checksum_to_cstring_display(recorded_base_checksum,
3702                                                      pool));
3703
3704   /* Open the text base for reading, unless this is an added file. */
3705
3706   /*
3707      kff todo: what we really need to do here is:
3708
3709      1. See if there's a file or dir by this name already here.
3710      2. See if it's under revision control.
3711      3. If both are true, open text-base.
3712      4. If only 1 is true, bail, because we can't go destroying user's
3713         files (or as an alternative to bailing, move it to some tmp
3714         name and somehow tell the user, but communicating with the
3715         user without erroring is a whole callback system we haven't
3716         finished inventing yet.)
3717   */
3718
3719   if (! fb->adding_file)
3720     {
3721       SVN_ERR_ASSERT(!fb->original_checksum
3722                      || fb->original_checksum->kind == svn_checksum_sha1);
3723
3724       source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3725                                           handler_pool);
3726     }
3727   else
3728     {
3729       source = svn_stream_empty(handler_pool);
3730     }
3731
3732   /* If we don't have a recorded checksum, use the ra provided checksum */
3733   if (!recorded_base_checksum)
3734     recorded_base_checksum = expected_base_checksum;
3735
3736   /* Checksum the text base while applying deltas */
3737   if (recorded_base_checksum)
3738     {
3739       hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3740                                                       handler_pool);
3741
3742       /* Wrap stream and store reference to allow calculating the
3743          checksum. */
3744       source = svn_stream_checksummed2(source,
3745                                        &hb->actual_source_checksum,
3746                                        NULL, recorded_base_checksum->kind,
3747                                        TRUE, handler_pool);
3748       hb->source_checksum_stream = source;
3749     }
3750
3751   tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3752   tb->hb = hb;
3753   tb->fb = fb;
3754   tb->eb = eb;
3755   target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3756
3757   /* Prepare to apply the delta.  */
3758   svn_txdelta_apply(source, target,
3759                     hb->new_text_base_md5_digest,
3760                     hb->new_text_base_tmp_abspath /* error_info */,
3761                     handler_pool,
3762                     &hb->apply_handler, &hb->apply_baton);
3763
3764   hb->pool = handler_pool;
3765   hb->fb = fb;
3766
3767   /* We're all set.  */
3768   *handler_baton = hb;
3769   *handler = window_handler;
3770
3771   return SVN_NO_ERROR;
3772 }
3773
3774
3775 /* An svn_delta_editor_t function. */
3776 static svn_error_t *
3777 change_file_prop(void *file_baton,
3778                  const char *name,
3779                  const svn_string_t *value,
3780                  apr_pool_t *scratch_pool)
3781 {
3782   struct file_baton *fb = file_baton;
3783   svn_prop_t *propchange;
3784
3785   if (fb->skip_this)
3786     return SVN_NO_ERROR;
3787
3788   /* Push a new propchange to the file baton's array of propchanges */
3789   propchange = apr_array_push(fb->propchanges);
3790   propchange->name = apr_pstrdup(fb->pool, name);
3791   propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3792
3793   if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3794     SVN_ERR(mark_file_edited(fb, scratch_pool));
3795
3796   if (! fb->shadowed
3797       && strcmp(name, SVN_PROP_SPECIAL) == 0)
3798     {
3799       struct edit_baton *eb = fb->edit_baton;
3800       svn_boolean_t modified = FALSE;
3801       svn_boolean_t becomes_symlink;
3802       svn_boolean_t was_symlink;
3803
3804       /* Let's see if we have a change as in some scenarios servers report
3805          non-changes of properties. */
3806       becomes_symlink = (value != NULL);
3807
3808       if (fb->adding_file)
3809         was_symlink = becomes_symlink; /* No change */
3810       else
3811         {
3812           apr_hash_t *props;
3813
3814           /* We read the server-props, not the ACTUAL props here as we just
3815              want to see if this is really an incoming prop change. */
3816           SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3817                                             fb->local_abspath,
3818                                             scratch_pool, scratch_pool));
3819
3820           was_symlink = ((props
3821                               && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3822                               ? svn_tristate_true
3823                               : svn_tristate_false);
3824         }
3825
3826       if (was_symlink != becomes_symlink)
3827         {
3828           /* If the local node was not modified, we continue as usual, if
3829              modified we want a tree conflict just like how we would handle
3830              it when receiving a delete + add (aka "replace") */
3831           if (fb->local_prop_mods)
3832             modified = TRUE;
3833           else
3834             SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3835                                                      fb->local_abspath,
3836                                                      FALSE, scratch_pool));
3837         }
3838
3839       if (modified)
3840         {
3841           if (!fb->edit_conflict)
3842             fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3843
3844           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3845                                      fb->edit_conflict,
3846                                      eb->db, fb->local_abspath,
3847                                      svn_wc_conflict_reason_edited,
3848                                      svn_wc_conflict_action_replace,
3849                                      NULL,
3850                                      fb->pool, scratch_pool));
3851
3852           SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3853                                     fb->local_abspath, fb->old_repos_relpath,
3854                                     fb->old_revision, fb->new_relpath,
3855                                     svn_node_file, svn_node_file,
3856                                     fb->pool, scratch_pool));
3857
3858           /* Create a copy of the existing (pre update) BASE node in WORKING,
3859              mark a tree conflict and handle the rest of the update as
3860              shadowed */
3861           SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3862                                           fb->edit_conflict, NULL,
3863                                           scratch_pool));
3864
3865           do_notification(eb, fb->local_abspath, svn_node_file,
3866                           svn_wc_notify_tree_conflict, scratch_pool);
3867
3868           /* Ok, we introduced a replacement, so we can now handle the rest
3869              as a normal shadowed update */
3870           fb->shadowed = TRUE;
3871           fb->add_existed = FALSE;
3872           fb->already_notified = TRUE;
3873       }
3874     }
3875
3876   return SVN_NO_ERROR;
3877 }
3878
3879 /* Perform the actual merge of file changes between an original file,
3880    identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3881    identified by NEW_CHECKSUM.
3882
3883    Merge the result into LOCAL_ABSPATH, which is part of the working copy
3884    identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3885    the intermediate files.
3886
3887    The rest of the arguments are passed to svn_wc__internal_merge().
3888  */
3889 svn_error_t *
3890 svn_wc__perform_file_merge(svn_skel_t **work_items,
3891                            svn_skel_t **conflict_skel,
3892                            svn_boolean_t *found_conflict,
3893                            svn_wc__db_t *db,
3894                            const char *local_abspath,
3895                            const char *wri_abspath,
3896                            const svn_checksum_t *new_checksum,
3897                            const svn_checksum_t *original_checksum,
3898                            apr_hash_t *old_actual_props,
3899                            const apr_array_header_t *ext_patterns,
3900                            svn_revnum_t old_revision,
3901                            svn_revnum_t target_revision,
3902                            const apr_array_header_t *propchanges,
3903                            const char *diff3_cmd,
3904                            svn_cancel_func_t cancel_func,
3905                            void *cancel_baton,
3906                            apr_pool_t *result_pool,
3907                            apr_pool_t *scratch_pool)
3908 {
3909   /* Actual file exists and has local mods:
3910      Now we need to let loose svn_wc__internal_merge() to merge
3911      the textual changes into the working file. */
3912   const char *oldrev_str, *newrev_str, *mine_str;
3913   const char *merge_left;
3914   svn_boolean_t delete_left = FALSE;
3915   const char *path_ext = "";
3916   const char *new_text_base_tmp_abspath;
3917   enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3918   svn_skel_t *work_item;
3919
3920   *work_items = NULL;
3921
3922   SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3923                                        db, wri_abspath, new_checksum,
3924                                        scratch_pool, scratch_pool));
3925
3926   /* If we have any file extensions we're supposed to
3927      preserve in generated conflict file names, then find
3928      this path's extension.  But then, if it isn't one of
3929      the ones we want to keep in conflict filenames,
3930      pretend it doesn't have an extension at all. */
3931   if (ext_patterns && ext_patterns->nelts)
3932     {
3933       svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3934       if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3935         path_ext = "";
3936     }
3937
3938   /* old_revision can be invalid when the conflict is against a
3939      local addition */
3940   if (!SVN_IS_VALID_REVNUM(old_revision))
3941     old_revision = 0;
3942
3943   oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3944                             old_revision,
3945                             *path_ext ? "." : "",
3946                             *path_ext ? path_ext : "");
3947
3948   newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3949                             target_revision,
3950                             *path_ext ? "." : "",
3951                             *path_ext ? path_ext : "");
3952   mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3953                           *path_ext ? "." : "",
3954                           *path_ext ? path_ext : "");
3955
3956   if (! original_checksum)
3957     {
3958       SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3959                                  result_pool, scratch_pool));
3960       delete_left = TRUE;
3961     }
3962   else
3963     SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3964                                          original_checksum,
3965                                          result_pool, scratch_pool));
3966
3967   /* Merge the changes from the old textbase to the new
3968      textbase into the file we're updating.
3969      Remember that this function wants full paths! */
3970   SVN_ERR(svn_wc__internal_merge(&work_item,
3971                                  conflict_skel,
3972                                  &merge_outcome,
3973                                  db,
3974                                  merge_left,
3975                                  new_text_base_tmp_abspath,
3976                                  local_abspath,
3977                                  wri_abspath,
3978                                  oldrev_str, newrev_str, mine_str,
3979                                  old_actual_props,
3980                                  FALSE /* dry_run */,
3981                                  diff3_cmd, NULL, propchanges,
3982                                  cancel_func, cancel_baton,
3983                                  result_pool, scratch_pool));
3984
3985   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3986   *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3987
3988   /* If we created a temporary left merge file, get rid of it. */
3989   if (delete_left)
3990     {
3991       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3992                                            merge_left,
3993                                            result_pool, scratch_pool));
3994       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3995     }
3996
3997   return SVN_NO_ERROR;
3998 }
3999
4000 /* This is the small planet.  It has the complex responsibility of
4001  * "integrating" a new revision of a file into a working copy.
4002  *
4003  * Given a file_baton FB for a file either already under version control, or
4004  * prepared (see below) to join version control, fully install a
4005  * new revision of the file.
4006  *
4007  * ### transitional: installation of the working file will be handled
4008  * ### by the *INSTALL_PRISTINE flag.
4009  *
4010  * By "install", we mean: create a new text-base and prop-base, merge
4011  * any textual and property changes into the working file, and finally
4012  * update all metadata so that the working copy believes it has a new
4013  * working revision of the file.  All of this work includes being
4014  * sensitive to eol translation, keyword substitution, and performing
4015  * all actions accumulated the parent directory's work queue.
4016  *
4017  * Set *CONTENT_STATE to the state of the contents after the
4018  * installation.
4019  *
4020  * Return values are allocated in RESULT_POOL and temporary allocations
4021  * are performed in SCRATCH_POOL.
4022  */
4023 static svn_error_t *
4024 merge_file(svn_skel_t **work_items,
4025            svn_skel_t **conflict_skel,
4026            svn_boolean_t *install_pristine,
4027            const char **install_from,
4028            svn_wc_notify_state_t *content_state,
4029            struct file_baton *fb,
4030            apr_hash_t *actual_props,
4031            apr_time_t last_changed_date,
4032            apr_pool_t *result_pool,
4033            apr_pool_t *scratch_pool)
4034 {
4035   struct edit_baton *eb = fb->edit_baton;
4036   struct dir_baton *pb = fb->dir_baton;
4037   svn_boolean_t is_locally_modified;
4038   svn_boolean_t found_text_conflict = FALSE;
4039
4040   SVN_ERR_ASSERT(! fb->shadowed
4041                  && ! fb->obstruction_found
4042                  && ! fb->edit_obstructed);
4043
4044   /*
4045      When this function is called on file F, we assume the following
4046      things are true:
4047
4048          - The new pristine text of F is present in the pristine store
4049            iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4050
4051          - The WC metadata still reflects the old version of F.
4052            (We can still access the old pristine base text of F.)
4053
4054      The goal is to update the local working copy of F to reflect
4055      the changes received from the repository, preserving any local
4056      modifications.
4057   */
4058
4059   *work_items = NULL;
4060   *install_pristine = FALSE;
4061   *install_from = NULL;
4062
4063   /* Start by splitting the file path, getting an access baton for the parent,
4064      and an entry for the file if any. */
4065
4066   /* Has the user made local mods to the working file?
4067      Note that this compares to the current pristine file, which is
4068      different from fb->old_text_base_path if we have a replaced-with-history
4069      file.  However, in the case we had an obstruction, we check against the
4070      new text base.
4071    */
4072   if (fb->adding_file && !fb->add_existed)
4073     {
4074       is_locally_modified = FALSE; /* There is no file: Don't check */
4075     }
4076   else
4077     {
4078       /* The working file is not an obstruction.
4079          So: is the file modified, relative to its ORIGINAL pristine?
4080
4081          This function sets is_locally_modified to FALSE for
4082          files that do not exist and for directories. */
4083
4084       SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4085                                                eb->db, fb->local_abspath,
4086                                                FALSE /* exact_comparison */,
4087                                                scratch_pool));
4088     }
4089
4090   /* For 'textual' merging, we use the following system:
4091
4092      When a file is modified and we have a new BASE:
4093       - For text files
4094           * svn_wc_merge uses diff3
4095           * possibly makes backups and marks files as conflicted.
4096
4097       - For binary files
4098           * svn_wc_merge makes backups and marks files as conflicted.
4099
4100      If a file is not modified and we have a new BASE:
4101        * Install from pristine.
4102
4103      If we have property changes related to magic properties or if the
4104      svn:keywords property is set:
4105        * Retranslate from the working file.
4106    */
4107   if (! is_locally_modified
4108       && fb->new_text_base_sha1_checksum)
4109     {
4110           /* If there are no local mods, who cares whether it's a text
4111              or binary file!  Just write a log command to overwrite
4112              any working file with the new text-base.  If newline
4113              conversion or keyword substitution is activated, this
4114              will happen as well during the copy.
4115              For replaced files, though, we want to merge in the changes
4116              even if the file is not modified compared to the (non-revert)
4117              text-base. */
4118
4119       *install_pristine = TRUE;
4120     }
4121   else if (fb->new_text_base_sha1_checksum)
4122     {
4123       /* Actual file exists and has local mods:
4124          Now we need to let loose svn_wc__merge_internal() to merge
4125          the textual changes into the working file. */
4126       SVN_ERR(svn_wc__perform_file_merge(work_items,
4127                                          conflict_skel,
4128                                          &found_text_conflict,
4129                                          eb->db,
4130                                          fb->local_abspath,
4131                                          pb->local_abspath,
4132                                          fb->new_text_base_sha1_checksum,
4133                                          fb->add_existed
4134                                                   ? NULL
4135                                                   : fb->original_checksum,
4136                                          actual_props,
4137                                          eb->ext_patterns,
4138                                          fb->old_revision,
4139                                          *eb->target_revision,
4140                                          fb->propchanges,
4141                                          eb->diff3_cmd,
4142                                          eb->cancel_func, eb->cancel_baton,
4143                                          result_pool, scratch_pool));
4144     } /* end: working file exists and has mods */
4145   else
4146     {
4147       /* There is no new text base, but let's see if the working file needs
4148          to be updated for any other reason. */
4149
4150       apr_hash_t *keywords;
4151
4152       /* Determine if any of the propchanges are the "magic" ones that
4153          might require changing the working file. */
4154       svn_boolean_t magic_props_changed;
4155
4156       magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4157
4158       SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4159                                          &keywords,
4160                                          NULL,
4161                                          eb->db, fb->local_abspath,
4162                                          actual_props, TRUE,
4163                                          scratch_pool, scratch_pool));
4164       if (magic_props_changed || keywords)
4165         {
4166           /* Special edge-case: it's possible that this file installation
4167              only involves propchanges, but that some of those props still
4168              require a retranslation of the working file.
4169
4170              OR that the file doesn't involve propchanges which by themselves
4171              require retranslation, but receiving a change bumps the revision
4172              number which requires re-expansion of keywords... */
4173
4174           if (is_locally_modified)
4175             {
4176               const char *tmptext;
4177
4178               /* Copy and DEtranslate the working file to a temp text-base.
4179                  Note that detranslation is done according to the old props. */
4180               SVN_ERR(svn_wc__internal_translated_file(
4181                         &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4182                         SVN_WC_TRANSLATE_TO_NF
4183                           | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4184                         eb->cancel_func, eb->cancel_baton,
4185                         result_pool, scratch_pool));
4186
4187               /* We always want to reinstall the working file if the magic
4188                  properties have changed, or there are any keywords present.
4189                  Note that TMPTEXT might actually refer to the working file
4190                  itself (the above function skips a detranslate when not
4191                  required). This is acceptable, as we will (re)translate
4192                  according to the new properties into a temporary file (from
4193                  the working file), and then rename the temp into place. Magic!
4194                */
4195               *install_pristine = TRUE;
4196               *install_from = tmptext;
4197             }
4198           else
4199             {
4200               /* Use our existing 'copy' from the pristine store instead
4201                  of making a new copy. This way we can use the standard code
4202                  to update the recorded size and modification time.
4203                  (Issue #3842) */
4204               *install_pristine = TRUE;
4205             }
4206         }
4207     }
4208
4209   /* Set the returned content state. */
4210
4211   if (found_text_conflict)
4212     *content_state = svn_wc_notify_state_conflicted;
4213   else if (fb->new_text_base_sha1_checksum)
4214     {
4215       if (is_locally_modified)
4216         *content_state = svn_wc_notify_state_merged;
4217       else
4218         *content_state = svn_wc_notify_state_changed;
4219     }
4220   else
4221     *content_state = svn_wc_notify_state_unchanged;
4222
4223   return SVN_NO_ERROR;
4224 }
4225
4226
4227 /* An svn_delta_editor_t function. */
4228 /* Mostly a wrapper around merge_file. */
4229 static svn_error_t *
4230 close_file(void *file_baton,
4231            const char *expected_md5_digest,
4232            apr_pool_t *pool)
4233 {
4234   struct file_baton *fb = file_baton;
4235   struct dir_baton *pdb = fb->dir_baton;
4236   struct edit_baton *eb = fb->edit_baton;
4237   svn_wc_notify_state_t content_state, prop_state;
4238   svn_wc_notify_lock_state_t lock_state;
4239   svn_checksum_t *expected_md5_checksum = NULL;
4240   apr_hash_t *new_base_props = NULL;
4241   apr_hash_t *new_actual_props = NULL;
4242   apr_array_header_t *entry_prop_changes;
4243   apr_array_header_t *dav_prop_changes;
4244   apr_array_header_t *regular_prop_changes;
4245   apr_hash_t *current_base_props = NULL;
4246   apr_hash_t *current_actual_props = NULL;
4247   apr_hash_t *local_actual_props = NULL;
4248   svn_skel_t *all_work_items = NULL;
4249   svn_skel_t *conflict_skel = NULL;
4250   svn_skel_t *work_item;
4251   apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4252   svn_boolean_t keep_recorded_info = FALSE;
4253   const svn_checksum_t *new_checksum;
4254   apr_array_header_t *iprops = NULL;
4255
4256   if (fb->skip_this)
4257     {
4258       svn_pool_destroy(fb->pool);
4259       SVN_ERR(maybe_release_dir_info(pdb));
4260       return SVN_NO_ERROR;
4261     }
4262
4263   if (fb->edited)
4264     conflict_skel = fb->edit_conflict;
4265
4266   if (expected_md5_digest)
4267     SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4268                                    expected_md5_digest, scratch_pool));
4269
4270   if (fb->new_text_base_md5_checksum && expected_md5_checksum
4271       && !svn_checksum_match(expected_md5_checksum,
4272                              fb->new_text_base_md5_checksum))
4273     return svn_error_trace(
4274                 svn_checksum_mismatch_err(expected_md5_checksum,
4275                                           fb->new_text_base_md5_checksum,
4276                                           scratch_pool,
4277                                           _("Checksum mismatch for '%s'"),
4278                                           svn_dirent_local_style(
4279                                                 fb->local_abspath, pool)));
4280
4281   /* Gather the changes for each kind of property.  */
4282   SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4283                                &dav_prop_changes, &regular_prop_changes,
4284                                scratch_pool));
4285
4286   /* Extract the changed_* and lock state information.  */
4287   {
4288     svn_revnum_t new_changed_rev;
4289     apr_time_t new_changed_date;
4290     const char *new_changed_author;
4291
4292     SVN_ERR(accumulate_last_change(&new_changed_rev,
4293                                    &new_changed_date,
4294                                    &new_changed_author,
4295                                    entry_prop_changes,
4296                                    scratch_pool, scratch_pool));
4297
4298     if (SVN_IS_VALID_REVNUM(new_changed_rev))
4299       fb->changed_rev = new_changed_rev;
4300     if (new_changed_date != 0)
4301       fb->changed_date = new_changed_date;
4302     if (new_changed_author != NULL)
4303       fb->changed_author = new_changed_author;
4304   }
4305
4306   /* Determine whether the file has become unlocked.  */
4307   {
4308     int i;
4309
4310     lock_state = svn_wc_notify_lock_state_unchanged;
4311
4312     for (i = 0; i < entry_prop_changes->nelts; ++i)
4313       {
4314         const svn_prop_t *prop
4315           = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4316
4317         /* If we see a change to the LOCK_TOKEN entry prop, then the only
4318            possible change is its REMOVAL. Thus, the lock has been removed,
4319            and we should likewise remove our cached copy of it.  */
4320         if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4321           {
4322             /* If we lose the lock, but not because we are switching to
4323                another url, remove the state lock from the wc */
4324             if (! eb->switch_relpath
4325                 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4326               {
4327                 SVN_ERR_ASSERT(prop->value == NULL);
4328                 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4329                                                scratch_pool));
4330
4331                 lock_state = svn_wc_notify_lock_state_unlocked;
4332               }
4333             break;
4334           }
4335       }
4336   }
4337
4338   /* Install all kinds of properties.  It is important to do this before
4339      any file content merging, since that process might expand keywords, in
4340      which case we want the new entryprops to be in place. */
4341
4342   /* Write log commands to merge REGULAR_PROPS into the existing
4343      properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4344      the result of the regular prop merge.
4345
4346      BASE_PROPS and WORKING_PROPS are hashes of the base and
4347      working props of the file; if NULL they are read from the wc.  */
4348
4349   /* ### some of this feels like voodoo... */
4350
4351   if ((!fb->adding_file || fb->add_existed)
4352       && !fb->shadowed)
4353     SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4354                                      eb->db, fb->local_abspath,
4355                                      scratch_pool, scratch_pool));
4356   if (local_actual_props == NULL)
4357     local_actual_props = apr_hash_make(scratch_pool);
4358
4359   if (fb->add_existed)
4360     {
4361       /* This node already exists. Grab the current pristine properties. */
4362       SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4363                                              eb->db, fb->local_abspath,
4364                                              scratch_pool, scratch_pool));
4365       current_actual_props = local_actual_props;
4366     }
4367   else if (!fb->adding_file)
4368     {
4369       /* Get the BASE properties for proper merging. */
4370       SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4371                                         eb->db, fb->local_abspath,
4372                                         scratch_pool, scratch_pool));
4373       current_actual_props = local_actual_props;
4374     }
4375
4376   /* Note: even if the node existed before, it may not have
4377      pristine props (e.g a local-add)  */
4378   if (current_base_props == NULL)
4379     current_base_props = apr_hash_make(scratch_pool);
4380
4381   /* And new nodes need an empty set of ACTUAL props.  */
4382   if (current_actual_props == NULL)
4383     current_actual_props = apr_hash_make(scratch_pool);
4384
4385   prop_state = svn_wc_notify_state_unknown;
4386
4387   if (! fb->shadowed)
4388     {
4389       svn_boolean_t install_pristine;
4390       const char *install_from = NULL;
4391
4392       /* Merge the 'regular' props into the existing working proplist. */
4393       /* This will merge the old and new props into a new prop db, and
4394          write <cp> commands to the logfile to install the merged
4395          props.  */
4396       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4397                                        scratch_pool);
4398       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4399                                   &prop_state,
4400                                   &new_actual_props,
4401                                   eb->db,
4402                                   fb->local_abspath,
4403                                   NULL /* server_baseprops (update, not merge)  */,
4404                                   current_base_props,
4405                                   current_actual_props,
4406                                   regular_prop_changes, /* propchanges */
4407                                   scratch_pool,
4408                                   scratch_pool));
4409       /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4410       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4411
4412       /* Merge the text. This will queue some additional work.  */
4413       if (!fb->obstruction_found && !fb->edit_obstructed)
4414         {
4415           svn_error_t *err;
4416           err = merge_file(&work_item, &conflict_skel,
4417                            &install_pristine, &install_from,
4418                            &content_state, fb, current_actual_props,
4419                            fb->changed_date, scratch_pool, scratch_pool);
4420
4421           if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4422             {
4423               if (eb->notify_func)
4424                 {
4425                   svn_wc_notify_t *notify =svn_wc_create_notify(
4426                                 fb->local_abspath,
4427                                 svn_wc_notify_update_skip_access_denied,
4428                                 scratch_pool);
4429
4430                   notify->kind = svn_node_file;
4431                   notify->err = err;
4432
4433                   eb->notify_func(eb->notify_baton, notify, scratch_pool);
4434                 }
4435               svn_error_clear(err);
4436
4437               SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4438                                             scratch_pool));
4439               fb->skip_this = TRUE;
4440
4441               svn_pool_destroy(fb->pool);
4442               SVN_ERR(maybe_release_dir_info(pdb));
4443               return SVN_NO_ERROR;
4444             }
4445           else
4446             SVN_ERR(err);
4447
4448           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4449                                             scratch_pool);
4450         }
4451       else
4452         {
4453           install_pristine = FALSE;
4454           if (fb->new_text_base_sha1_checksum)
4455             content_state = svn_wc_notify_state_changed;
4456           else
4457             content_state = svn_wc_notify_state_unchanged;
4458         }
4459
4460       if (install_pristine)
4461         {
4462           svn_boolean_t record_fileinfo;
4463
4464           /* If we are installing from the pristine contents, then go ahead and
4465              record the fileinfo. That will be the "proper" values. Installing
4466              from some random file means the fileinfo does NOT correspond to
4467              the pristine (in which case, the fileinfo will be cleared for
4468              safety's sake).  */
4469           record_fileinfo = (install_from == NULL);
4470
4471           SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4472                                                 eb->db,
4473                                                 fb->local_abspath,
4474                                                 install_from,
4475                                                 eb->use_commit_times,
4476                                                 record_fileinfo,
4477                                                 scratch_pool, scratch_pool));
4478           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4479                                             scratch_pool);
4480         }
4481       else if (lock_state == svn_wc_notify_lock_state_unlocked
4482                && !fb->obstruction_found)
4483         {
4484           /* If a lock was removed and we didn't update the text contents, we
4485              might need to set the file read-only.
4486
4487              Note: this will also update the executable flag, but ... meh.  */
4488           SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4489                                                    fb->local_abspath,
4490                                                    scratch_pool, scratch_pool));
4491           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4492                                             scratch_pool);
4493         }
4494
4495       if (! install_pristine
4496           && (content_state == svn_wc_notify_state_unchanged))
4497         {
4498           /* It is safe to keep the current recorded timestamp and size */
4499           keep_recorded_info = TRUE;
4500         }
4501
4502       /* Clean up any temporary files.  */
4503
4504       /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4505          working file.  */
4506       if (install_from != NULL
4507           && strcmp(install_from, fb->local_abspath) != 0)
4508         {
4509           SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4510                                                fb->local_abspath, install_from,
4511                                                scratch_pool, scratch_pool));
4512           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4513                                             scratch_pool);
4514         }
4515     }
4516   else
4517     {
4518       /* Adding or updating a BASE node under a locally added node. */
4519       apr_hash_t *fake_actual_props;
4520
4521       if (fb->adding_file)
4522         fake_actual_props = apr_hash_make(scratch_pool);
4523       else
4524         fake_actual_props = current_base_props;
4525
4526       /* Store the incoming props (sent as propchanges) in new_base_props
4527          and create a set of new actual props to use for notifications */
4528       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4529                                        scratch_pool);
4530       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4531                                   &prop_state,
4532                                   &new_actual_props,
4533                                   eb->db,
4534                                   fb->local_abspath,
4535                                   NULL /* server_baseprops (not merging) */,
4536                                   current_base_props /* pristine_props */,
4537                                   fake_actual_props /* actual_props */,
4538                                   regular_prop_changes, /* propchanges */
4539                                   scratch_pool,
4540                                   scratch_pool));
4541
4542       if (fb->new_text_base_sha1_checksum)
4543         content_state = svn_wc_notify_state_changed;
4544       else
4545         content_state = svn_wc_notify_state_unchanged;
4546     }
4547
4548   /* Insert/replace the BASE node with all of the new metadata.  */
4549
4550   /* Set the 'checksum' column of the file's BASE_NODE row to
4551    * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4552    * checksum is already in the pristine store. */
4553   new_checksum = fb->new_text_base_sha1_checksum;
4554
4555   /* If we don't have a NEW checksum, then the base must not have changed.
4556      Just carry over the old checksum.  */
4557   if (new_checksum == NULL)
4558     new_checksum = fb->original_checksum;
4559
4560   if (conflict_skel)
4561     {
4562       SVN_ERR(complete_conflict(conflict_skel,
4563                                 fb->edit_baton,
4564                                 fb->local_abspath,
4565                                 fb->old_repos_relpath,
4566                                 fb->old_revision,
4567                                 fb->new_relpath,
4568                                 svn_node_file, svn_node_file,
4569                                 fb->pool, scratch_pool));
4570
4571       SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4572                                               eb->db, fb->local_abspath,
4573                                               conflict_skel,
4574                                               scratch_pool, scratch_pool));
4575
4576       all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4577                                         scratch_pool);
4578     }
4579
4580   /* Any inherited props to be set set for this base node? */
4581   if (eb->wcroot_iprops)
4582     {
4583       iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4584
4585       /* close_edit may also update iprops for switched nodes, catching
4586          those for which close_directory is never called (e.g. a switch
4587          with no changes).  So as a minor optimization we remove any
4588          iprops from the hash so as not to set them again in
4589          close_edit. */
4590       if (iprops)
4591         svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4592     }
4593
4594   SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4595                                    eb->wcroot_abspath,
4596                                    fb->new_relpath,
4597                                    eb->repos_root, eb->repos_uuid,
4598                                    *eb->target_revision,
4599                                    new_base_props,
4600                                    fb->changed_rev,
4601                                    fb->changed_date,
4602                                    fb->changed_author,
4603                                    new_checksum,
4604                                    (dav_prop_changes->nelts > 0)
4605                                      ? svn_prop_array_to_hash(
4606                                                       dav_prop_changes,
4607                                                       scratch_pool)
4608                                      : NULL,
4609                                    (fb->add_existed && fb->adding_file),
4610                                    (! fb->shadowed) && new_base_props,
4611                                    new_actual_props,
4612                                    iprops,
4613                                    keep_recorded_info,
4614                                    (fb->shadowed && fb->obstruction_found),
4615                                    conflict_skel,
4616                                    all_work_items,
4617                                    scratch_pool));
4618
4619   if (conflict_skel && eb->conflict_func)
4620     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4621                                              conflict_skel,
4622                                              NULL /* merge_options */,
4623                                              eb->conflict_func,
4624                                              eb->conflict_baton,
4625                                              eb->cancel_func,
4626                                              eb->cancel_baton,
4627                                              scratch_pool));
4628
4629   /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4630
4631   svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4632
4633   /* Send a notification to the callback function.  (Skip notifications
4634      about files which were already notified for another reason.) */
4635   if (eb->notify_func && !fb->already_notified
4636       && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4637     {
4638       svn_wc_notify_t *notify;
4639       svn_wc_notify_action_t action = svn_wc_notify_update_update;
4640
4641       if (fb->edited)
4642         {
4643           if (fb->shadowed || fb->edit_obstructed)
4644             action = fb->adding_file
4645                             ? svn_wc_notify_update_shadowed_add
4646                             : svn_wc_notify_update_shadowed_update;
4647           else if (fb->obstruction_found || fb->add_existed)
4648             {
4649               if (content_state != svn_wc_notify_state_conflicted)
4650                 action = svn_wc_notify_exists;
4651             }
4652           else if (fb->adding_file)
4653             {
4654               action = svn_wc_notify_update_add;
4655             }
4656         }
4657       else
4658         {
4659           SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4660           action = svn_wc_notify_update_broken_lock;
4661         }
4662
4663       /* If the file was moved-away, notify for the moved-away node.
4664        * The original location only had its BASE info changed and
4665        * we don't usually notify about such changes. */
4666       notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4667       notify->kind = svn_node_file;
4668       notify->content_state = content_state;
4669       notify->prop_state = prop_state;
4670       notify->lock_state = lock_state;
4671       notify->revision = *eb->target_revision;
4672       notify->old_revision = fb->old_revision;
4673
4674       /* Fetch the mimetype from the actual properties */
4675       notify->mime_type = svn_prop_get_value(new_actual_props,
4676                                              SVN_PROP_MIME_TYPE);
4677
4678       eb->notify_func(eb->notify_baton, notify, scratch_pool);
4679     }
4680
4681   svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4682
4683   /* We have one less referrer to the directory */
4684   SVN_ERR(maybe_release_dir_info(pdb));
4685
4686   return SVN_NO_ERROR;
4687 }
4688
4689
4690 /* An svn_delta_editor_t function. */
4691 static svn_error_t *
4692 close_edit(void *edit_baton,
4693            apr_pool_t *pool)
4694 {
4695   struct edit_baton *eb = edit_baton;
4696   apr_pool_t *scratch_pool = eb->pool;
4697
4698   /* The editor didn't even open the root; we have to take care of
4699      some cleanup stuffs. */
4700   if (! eb->root_opened
4701       && *eb->target_basename == '\0')
4702     {
4703       /* We need to "un-incomplete" the root directory. */
4704       SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4705                                                       eb->anchor_abspath,
4706                                                       scratch_pool));
4707     }
4708
4709   /* By definition, anybody "driving" this editor for update or switch
4710      purposes at a *minimum* must have called set_target_revision() at
4711      the outset, and close_edit() at the end -- even if it turned out
4712      that no changes ever had to be made, and open_root() was never
4713      called.  That's fine.  But regardless, when the edit is over,
4714      this editor needs to make sure that *all* paths have had their
4715      revisions bumped to the new target revision. */
4716
4717   /* Make sure our update target now has the new working revision.
4718      Also, if this was an 'svn switch', then rewrite the target's
4719      url.  All of this tweaking might happen recursively!  Note
4720      that if eb->target is NULL, that's okay (albeit "sneaky",
4721      some might say).  */
4722
4723   /* Extra check: if the update did nothing but make its target
4724      'deleted', then do *not* run cleanup on the target, as it
4725      will only remove the deleted entry!  */
4726   if (! eb->target_deleted)
4727     {
4728       SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4729                                                        eb->target_abspath,
4730                                                        eb->requested_depth,
4731                                                        eb->switch_relpath,
4732                                                        eb->repos_root,
4733                                                        eb->repos_uuid,
4734                                                        *(eb->target_revision),
4735                                                        eb->skipped_trees,
4736                                                        eb->wcroot_iprops,
4737                                                        eb->notify_func,
4738                                                        eb->notify_baton,
4739                                                        eb->pool));
4740
4741       if (*eb->target_basename != '\0')
4742         {
4743           svn_wc__db_status_t status;
4744           svn_error_t *err;
4745
4746           /* Note: we are fetching information about the *target*, not anchor.
4747              There is no guarantee that the target has a BASE node.
4748              For example:
4749
4750                The node was not present in BASE, but locally-added, and the
4751                update did not create a new BASE node "under" the local-add.
4752
4753              If there is no BASE node for the target, then we certainly don't
4754              have to worry about removing it. */
4755           err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4756                                          NULL, NULL, NULL, NULL, NULL, NULL,
4757                                          NULL, NULL, NULL, NULL,
4758                                          eb->db, eb->target_abspath,
4759                                          scratch_pool, scratch_pool);
4760           if (err)
4761             {
4762               if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4763                 return svn_error_trace(err);
4764
4765               svn_error_clear(err);
4766             }
4767           else if (status == svn_wc__db_status_excluded)
4768             {
4769               /* There is a small chance that the explicit target of an update/
4770                  switch is gone in the repository, in that specific case the
4771                  node hasn't been re-added to the BASE tree by this update.
4772
4773                  If so, we should get rid of this excluded node now. */
4774
4775               SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4776                                              FALSE /* keep_as_working */,
4777                                              FALSE /* queue_deletes */,
4778                                              FALSE /* remove_locks */,
4779                                              SVN_INVALID_REVNUM,
4780                                              NULL, NULL, scratch_pool));
4781             }
4782         }
4783     }
4784
4785   /* The edit is over: run the wq with proper cancel support,
4786      but first kill the handler that would run it on the pool
4787      cleanup at the end of this function. */
4788   apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4789
4790   SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4791                          eb->cancel_func, eb->cancel_baton,
4792                          eb->pool));
4793
4794   /* The edit is over, free its pool.
4795      ### No, this is wrong.  Who says this editor/baton won't be used
4796      again?  But the change is not merely to remove this call.  We
4797      should also make eb->pool not be a subpool (see make_editor),
4798      and change callers of svn_client_{checkout,update,switch} to do
4799      better pool management. ### */
4800
4801   svn_pool_destroy(eb->pool);
4802
4803   return SVN_NO_ERROR;
4804 }
4805
4806 \f
4807 /*** Returning editors. ***/
4808
4809 /* Helper for the three public editor-supplying functions. */
4810 static svn_error_t *
4811 make_editor(svn_revnum_t *target_revision,
4812             svn_wc__db_t *db,
4813             const char *anchor_abspath,
4814             const char *target_basename,
4815             apr_hash_t *wcroot_iprops,
4816             svn_boolean_t use_commit_times,
4817             const char *switch_url,
4818             svn_depth_t depth,
4819             svn_boolean_t depth_is_sticky,
4820             svn_boolean_t allow_unver_obstructions,
4821             svn_boolean_t adds_as_modification,
4822             svn_boolean_t server_performs_filtering,
4823             svn_boolean_t clean_checkout,
4824             svn_wc_notify_func2_t notify_func,
4825             void *notify_baton,
4826             svn_cancel_func_t cancel_func,
4827             void *cancel_baton,
4828             svn_wc_dirents_func_t fetch_dirents_func,
4829             void *fetch_dirents_baton,
4830             svn_wc_conflict_resolver_func2_t conflict_func,
4831             void *conflict_baton,
4832             svn_wc_external_update_t external_func,
4833             void *external_baton,
4834             const char *diff3_cmd,
4835             const apr_array_header_t *preserved_exts,
4836             const svn_delta_editor_t **editor,
4837             void **edit_baton,
4838             apr_pool_t *result_pool,
4839             apr_pool_t *scratch_pool)
4840 {
4841   struct edit_baton *eb;
4842   void *inner_baton;
4843   apr_pool_t *edit_pool = svn_pool_create(result_pool);
4844   svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4845   const svn_delta_editor_t *inner_editor;
4846   const char *repos_root, *repos_uuid;
4847   struct svn_wc__shim_fetch_baton_t *sfb;
4848   svn_delta_shim_callbacks_t *shim_callbacks =
4849                                 svn_delta_shim_callbacks_default(edit_pool);
4850
4851   /* An unknown depth can't be sticky. */
4852   if (depth == svn_depth_unknown)
4853     depth_is_sticky = FALSE;
4854
4855   /* Get the anchor's repository root and uuid. The anchor must already exist
4856      in BASE. */
4857   SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4858                                      db, anchor_abspath,
4859                                      result_pool, scratch_pool));
4860
4861   /* With WC-NG we need a valid repository root */
4862   SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4863
4864   /* Disallow a switch operation to change the repository root of the target,
4865      if that is known. */
4866   if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4867     return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4868                              _("'%s'\nis not the same repository as\n'%s'"),
4869                              switch_url, repos_root);
4870
4871   /* Construct an edit baton. */
4872   eb = apr_pcalloc(edit_pool, sizeof(*eb));
4873   eb->pool                     = edit_pool;
4874   eb->use_commit_times         = use_commit_times;
4875   eb->target_revision          = target_revision;
4876   eb->repos_root               = repos_root;
4877   eb->repos_uuid               = repos_uuid;
4878   eb->db                       = db;
4879   eb->target_basename          = target_basename;
4880   eb->anchor_abspath           = anchor_abspath;
4881   eb->wcroot_iprops            = wcroot_iprops;
4882
4883   SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4884                                 edit_pool, scratch_pool));
4885
4886   if (switch_url)
4887     eb->switch_relpath =
4888       svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4889   else
4890     eb->switch_relpath = NULL;
4891
4892   if (svn_path_is_empty(target_basename))
4893     eb->target_abspath = eb->anchor_abspath;
4894   else
4895     eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4896                                          edit_pool);
4897
4898   eb->requested_depth          = depth;
4899   eb->depth_is_sticky          = depth_is_sticky;
4900   eb->notify_func              = notify_func;
4901   eb->notify_baton             = notify_baton;
4902   eb->external_func            = external_func;
4903   eb->external_baton           = external_baton;
4904   eb->diff3_cmd                = diff3_cmd;
4905   eb->cancel_func              = cancel_func;
4906   eb->cancel_baton             = cancel_baton;
4907   eb->conflict_func            = conflict_func;
4908   eb->conflict_baton           = conflict_baton;
4909   eb->allow_unver_obstructions = allow_unver_obstructions;
4910   eb->adds_as_modification     = adds_as_modification;
4911   eb->clean_checkout           = clean_checkout;
4912   eb->skipped_trees            = apr_hash_make(edit_pool);
4913   eb->dir_dirents              = apr_hash_make(edit_pool);
4914   eb->ext_patterns             = preserved_exts;
4915
4916   apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4917                             apr_pool_cleanup_null);
4918
4919   /* Construct an editor. */
4920   tree_editor->set_target_revision = set_target_revision;
4921   tree_editor->open_root = open_root;
4922   tree_editor->delete_entry = delete_entry;
4923   tree_editor->add_directory = add_directory;
4924   tree_editor->open_directory = open_directory;
4925   tree_editor->change_dir_prop = change_dir_prop;
4926   tree_editor->close_directory = close_directory;
4927   tree_editor->absent_directory = absent_directory;
4928   tree_editor->add_file = add_file;
4929   tree_editor->open_file = open_file;
4930   tree_editor->apply_textdelta = apply_textdelta;
4931   tree_editor->change_file_prop = change_file_prop;
4932   tree_editor->close_file = close_file;
4933   tree_editor->absent_file = absent_file;
4934   tree_editor->close_edit = close_edit;
4935
4936   /* Fiddle with the type system. */
4937   inner_editor = tree_editor;
4938   inner_baton = eb;
4939
4940   if (!depth_is_sticky
4941       && depth != svn_depth_unknown
4942       && svn_depth_empty <= depth && depth < svn_depth_infinity
4943       && fetch_dirents_func)
4944     {
4945       /* We are asked to perform an update at a depth less than the ambient
4946          depth. In this case the update won't describe additions that would
4947          have been reported if we updated at the ambient depth. */
4948       svn_error_t *err;
4949       svn_node_kind_t dir_kind;
4950       svn_wc__db_status_t dir_status;
4951       const char *dir_repos_relpath;
4952       svn_depth_t dir_depth;
4953
4954       /* we have to do this on the target of the update, not the anchor */
4955       err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4956                                      &dir_repos_relpath, NULL, NULL, NULL,
4957                                      NULL, NULL, &dir_depth, NULL, NULL, NULL,
4958                                      NULL, NULL, NULL,
4959                                      db, eb->target_abspath,
4960                                      scratch_pool, scratch_pool);
4961
4962       if (!err
4963           && dir_kind == svn_node_dir
4964           && dir_status == svn_wc__db_status_normal)
4965         {
4966           if (dir_depth > depth)
4967             {
4968               apr_hash_t *dirents;
4969
4970               /* If we switch, we should look at the new relpath */
4971               if (eb->switch_relpath)
4972                 dir_repos_relpath = eb->switch_relpath;
4973
4974               SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4975                                          repos_root, dir_repos_relpath,
4976                                          edit_pool, scratch_pool));
4977
4978               if (dirents != NULL && apr_hash_count(dirents))
4979                 svn_hash_sets(eb->dir_dirents,
4980                               apr_pstrdup(edit_pool, dir_repos_relpath),
4981                               dirents);
4982             }
4983
4984           if (depth == svn_depth_immediates)
4985             {
4986               /* Worst case scenario of issue #3569 fix: We have to do the
4987                  same for all existing subdirs, but then we check for
4988                  svn_depth_empty. */
4989               const apr_array_header_t *children;
4990               apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4991               int i;
4992               SVN_ERR(svn_wc__db_base_get_children(&children, db,
4993                                                    eb->target_abspath,
4994                                                    scratch_pool,
4995                                                    iterpool));
4996
4997               for (i = 0; i < children->nelts; i++)
4998                 {
4999                   const char *child_abspath;
5000                   const char *child_name;
5001
5002                   svn_pool_clear(iterpool);
5003
5004                   child_name = APR_ARRAY_IDX(children, i, const char *);
5005
5006                   child_abspath = svn_dirent_join(eb->target_abspath,
5007                                                   child_name, iterpool);
5008
5009                   SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5010                                                    NULL, &dir_repos_relpath,
5011                                                    NULL, NULL, NULL, NULL,
5012                                                    NULL, &dir_depth, NULL,
5013                                                    NULL, NULL, NULL, NULL,
5014                                                    NULL,
5015                                                    db, child_abspath,
5016                                                    iterpool, iterpool));
5017
5018                   if (dir_kind == svn_node_dir
5019                       && dir_status == svn_wc__db_status_normal
5020                       && dir_depth > svn_depth_empty)
5021                     {
5022                       apr_hash_t *dirents;
5023
5024                       /* If we switch, we should look at the new relpath */
5025                       if (eb->switch_relpath)
5026                         dir_repos_relpath = svn_relpath_join(
5027                                                 eb->switch_relpath,
5028                                                 child_name, iterpool);
5029
5030                       SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5031                                                  repos_root, dir_repos_relpath,
5032                                                  edit_pool, iterpool));
5033
5034                       if (dirents != NULL && apr_hash_count(dirents))
5035                         svn_hash_sets(eb->dir_dirents,
5036                                       apr_pstrdup(edit_pool,
5037                                                   dir_repos_relpath),
5038                                       dirents);
5039                     }
5040                 }
5041             }
5042         }
5043       else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5044         svn_error_clear(err);
5045       else
5046         SVN_ERR(err);
5047     }
5048
5049   /* We need to limit the scope of our operation to the ambient depths
5050      present in the working copy already, but only if the requested
5051      depth is not sticky. If a depth was explicitly requested,
5052      libsvn_delta/depth_filter_editor.c will ensure that we never see
5053      editor calls that extend beyond the scope of the requested depth.
5054      But even what we do so might extend beyond the scope of our
5055      ambient depth.  So we use another filtering editor to avoid
5056      modifying the ambient working copy depth when not asked to do so.
5057      (This can also be skipped if the server understands depth.) */
5058   if (!server_performs_filtering
5059       && !depth_is_sticky)
5060     SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5061                                                 &inner_baton,
5062                                                 db,
5063                                                 anchor_abspath,
5064                                                 target_basename,
5065                                                 inner_editor,
5066                                                 inner_baton,
5067                                                 result_pool));
5068
5069   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5070                                             cancel_baton,
5071                                             inner_editor,
5072                                             inner_baton,
5073                                             editor,
5074                                             edit_baton,
5075                                             result_pool));
5076
5077   sfb = apr_palloc(result_pool, sizeof(*sfb));
5078   sfb->db = db;
5079   sfb->base_abspath = eb->anchor_abspath;
5080   sfb->fetch_base = TRUE;
5081
5082   shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5083   shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5084   shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5085   shim_callbacks->fetch_baton = sfb;
5086
5087   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5088                                    NULL, NULL, shim_callbacks,
5089                                    result_pool, scratch_pool));
5090
5091   return SVN_NO_ERROR;
5092 }
5093
5094
5095 svn_error_t *
5096 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5097                           void **edit_baton,
5098                           svn_revnum_t *target_revision,
5099                           svn_wc_context_t *wc_ctx,
5100                           const char *anchor_abspath,
5101                           const char *target_basename,
5102                           apr_hash_t *wcroot_iprops,
5103                           svn_boolean_t use_commit_times,
5104                           svn_depth_t depth,
5105                           svn_boolean_t depth_is_sticky,
5106                           svn_boolean_t allow_unver_obstructions,
5107                           svn_boolean_t adds_as_modification,
5108                           svn_boolean_t server_performs_filtering,
5109                           svn_boolean_t clean_checkout,
5110                           const char *diff3_cmd,
5111                           const apr_array_header_t *preserved_exts,
5112                           svn_wc_dirents_func_t fetch_dirents_func,
5113                           void *fetch_dirents_baton,
5114                           svn_wc_conflict_resolver_func2_t conflict_func,
5115                           void *conflict_baton,
5116                           svn_wc_external_update_t external_func,
5117                           void *external_baton,
5118                           svn_cancel_func_t cancel_func,
5119                           void *cancel_baton,
5120                           svn_wc_notify_func2_t notify_func,
5121                           void *notify_baton,
5122                           apr_pool_t *result_pool,
5123                           apr_pool_t *scratch_pool)
5124 {
5125   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5126                      target_basename, wcroot_iprops, use_commit_times,
5127                      NULL, depth, depth_is_sticky, allow_unver_obstructions,
5128                      adds_as_modification, server_performs_filtering,
5129                      clean_checkout,
5130                      notify_func, notify_baton,
5131                      cancel_func, cancel_baton,
5132                      fetch_dirents_func, fetch_dirents_baton,
5133                      conflict_func, conflict_baton,
5134                      external_func, external_baton,
5135                      diff3_cmd, preserved_exts, editor, edit_baton,
5136                      result_pool, scratch_pool);
5137 }
5138
5139 svn_error_t *
5140 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5141                           void **edit_baton,
5142                           svn_revnum_t *target_revision,
5143                           svn_wc_context_t *wc_ctx,
5144                           const char *anchor_abspath,
5145                           const char *target_basename,
5146                           const char *switch_url,
5147                           apr_hash_t *wcroot_iprops,
5148                           svn_boolean_t use_commit_times,
5149                           svn_depth_t depth,
5150                           svn_boolean_t depth_is_sticky,
5151                           svn_boolean_t allow_unver_obstructions,
5152                           svn_boolean_t server_performs_filtering,
5153                           const char *diff3_cmd,
5154                           const apr_array_header_t *preserved_exts,
5155                           svn_wc_dirents_func_t fetch_dirents_func,
5156                           void *fetch_dirents_baton,
5157                           svn_wc_conflict_resolver_func2_t conflict_func,
5158                           void *conflict_baton,
5159                           svn_wc_external_update_t external_func,
5160                           void *external_baton,
5161                           svn_cancel_func_t cancel_func,
5162                           void *cancel_baton,
5163                           svn_wc_notify_func2_t notify_func,
5164                           void *notify_baton,
5165                           apr_pool_t *result_pool,
5166                           apr_pool_t *scratch_pool)
5167 {
5168   SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5169
5170   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5171                      target_basename, wcroot_iprops, use_commit_times,
5172                      switch_url,
5173                      depth, depth_is_sticky, allow_unver_obstructions,
5174                      FALSE /* adds_as_modification */,
5175                      server_performs_filtering,
5176                      FALSE /* clean_checkout */,
5177                      notify_func, notify_baton,
5178                      cancel_func, cancel_baton,
5179                      fetch_dirents_func, fetch_dirents_baton,
5180                      conflict_func, conflict_baton,
5181                      external_func, external_baton,
5182                      diff3_cmd, preserved_exts,
5183                      editor, edit_baton,
5184                      result_pool, scratch_pool);
5185 }
5186
5187
5188
5189 /* ### Note that this function is completely different from the rest of the
5190        update editor in what it updates. The update editor changes only BASE
5191        and ACTUAL and this function just changes WORKING and ACTUAL.
5192
5193        In the entries world this function shared a lot of code with the
5194        update editor but in the wonderful new WC-NG world it will probably
5195        do more and more by itself and would be more logically grouped with
5196        the add/copy functionality in adm_ops.c and copy.c. */
5197 svn_error_t *
5198 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5199                        const char *local_abspath,
5200                        svn_stream_t *new_base_contents,
5201                        svn_stream_t *new_contents,
5202                        apr_hash_t *new_base_props,
5203                        apr_hash_t *new_props,
5204                        const char *copyfrom_url,
5205                        svn_revnum_t copyfrom_rev,
5206                        svn_cancel_func_t cancel_func,
5207                        void *cancel_baton,
5208                        apr_pool_t *scratch_pool)
5209 {
5210   svn_wc__db_t *db = wc_ctx->db;
5211   const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5212   svn_wc__db_status_t status;
5213   svn_node_kind_t kind;
5214   const char *tmp_text_base_abspath;
5215   svn_checksum_t *new_text_base_md5_checksum;
5216   svn_checksum_t *new_text_base_sha1_checksum;
5217   const char *source_abspath = NULL;
5218   svn_skel_t *all_work_items = NULL;
5219   svn_skel_t *work_item;
5220   const char *repos_root_url;
5221   const char *repos_uuid;
5222   const char *original_repos_relpath;
5223   svn_revnum_t changed_rev;
5224   apr_time_t changed_date;
5225   const char *changed_author;
5226   svn_error_t *err;
5227   apr_pool_t *pool = scratch_pool;
5228
5229   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5230   SVN_ERR_ASSERT(new_base_contents != NULL);
5231   SVN_ERR_ASSERT(new_base_props != NULL);
5232
5233   /* We should have a write lock on this file's parent directory.  */
5234   SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5235
5236   err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5237                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5238                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5239                              NULL, NULL, NULL,
5240                              db, local_abspath, scratch_pool, scratch_pool);
5241
5242   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5243     return svn_error_trace(err);
5244   else if(err)
5245     svn_error_clear(err);
5246   else
5247     switch (status)
5248       {
5249         case svn_wc__db_status_not_present:
5250         case svn_wc__db_status_deleted:
5251           break;
5252         default:
5253           return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5254                                    _("Node '%s' exists."),
5255                                    svn_dirent_local_style(local_abspath,
5256                                                           scratch_pool));
5257       }
5258
5259   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5260                                &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5261                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5262                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5263                                db, dir_abspath, scratch_pool, scratch_pool));
5264
5265   switch (status)
5266     {
5267       case svn_wc__db_status_normal:
5268       case svn_wc__db_status_added:
5269         break;
5270       case svn_wc__db_status_deleted:
5271         return
5272           svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5273                             _("Can't add '%s' to a parent directory"
5274                               " scheduled for deletion"),
5275                             svn_dirent_local_style(local_abspath,
5276                                                    scratch_pool));
5277       default:
5278         return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5279                                  _("Can't find parent directory's node while"
5280                                    " trying to add '%s'"),
5281                                  svn_dirent_local_style(local_abspath,
5282                                                         scratch_pool));
5283     }
5284   if (kind != svn_node_dir)
5285     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5286                              _("Can't schedule an addition of '%s'"
5287                                " below a not-directory node"),
5288                              svn_dirent_local_style(local_abspath,
5289                                                     scratch_pool));
5290
5291   /* Fabricate the anticipated new URL of the target and check the
5292      copyfrom URL to be in the same repository. */
5293   if (copyfrom_url != NULL)
5294     {
5295       /* Find the repository_root via the parent directory, which
5296          is always versioned before this function is called */
5297
5298       if (!repos_root_url)
5299         {
5300           /* The parent is an addition, scan upwards to find the right info */
5301           SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5302                                            &repos_root_url, &repos_uuid,
5303                                            NULL, NULL, NULL, NULL,
5304                                            wc_ctx->db, dir_abspath,
5305                                            scratch_pool, scratch_pool));
5306         }
5307       SVN_ERR_ASSERT(repos_root_url);
5308
5309       original_repos_relpath =
5310           svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5311
5312       if (!original_repos_relpath)
5313         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5314                                  _("Copyfrom-url '%s' has different repository"
5315                                    " root than '%s'"),
5316                                  copyfrom_url, repos_root_url);
5317     }
5318   else
5319     {
5320       original_repos_relpath = NULL;
5321       copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5322     }
5323
5324   /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5325      filter NEW_BASE_PROPS so it contains only regular props. */
5326   {
5327     apr_array_header_t *regular_props;
5328     apr_array_header_t *entry_props;
5329
5330     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5331                                  &entry_props, NULL, &regular_props,
5332                                  pool));
5333
5334     /* Put regular props back into a hash table. */
5335     new_base_props = svn_prop_array_to_hash(regular_props, pool);
5336
5337     /* Get the change_* info from the entry props.  */
5338     SVN_ERR(accumulate_last_change(&changed_rev,
5339                                    &changed_date,
5340                                    &changed_author,
5341                                    entry_props, pool, pool));
5342   }
5343
5344   /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5345      it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5346      NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5347   {
5348     svn_stream_t *tmp_base_contents;
5349
5350     SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5351                                        &tmp_text_base_abspath,
5352                                        &new_text_base_md5_checksum,
5353                                        &new_text_base_sha1_checksum,
5354                                        wc_ctx->db, local_abspath,
5355                                        pool, pool));
5356     SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5357                              cancel_func, cancel_baton, pool));
5358   }
5359
5360   /* If the caller gave us a new working file, copy it to a safe (temporary)
5361      location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5362      that into place after the node's state has been created.  */
5363   if (new_contents)
5364     {
5365       const char *temp_dir_abspath;
5366       svn_stream_t *tmp_contents;
5367
5368       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5369                                              local_abspath, pool, pool));
5370       SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5371                                      temp_dir_abspath, svn_io_file_del_none,
5372                                      pool, pool));
5373       SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5374                                cancel_func, cancel_baton, pool));
5375     }
5376
5377   /* Install new text base for copied files. Added files do NOT have a
5378      text base.  */
5379   if (copyfrom_url != NULL)
5380     {
5381       SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5382                                           new_text_base_sha1_checksum,
5383                                           new_text_base_md5_checksum, pool));
5384     }
5385   else
5386     {
5387       /* ### There's something wrong around here.  Sometimes (merge from a
5388          foreign repository, at least) we are called with copyfrom_url =
5389          NULL and an empty new_base_contents (and an empty set of
5390          new_base_props).  Why an empty "new base"?
5391
5392          That happens in merge_tests.py 54,87,88,89,143.
5393
5394          In that case, having been given this supposed "new base" file, we
5395          copy it and calculate its checksum but do not install it.  Why?
5396          That must be wrong.
5397
5398          To crudely work around one issue with this, that we shouldn't
5399          record a checksum in the database if we haven't installed the
5400          corresponding pristine text, for now we'll just set the checksum
5401          to NULL.
5402
5403          The proper solution is probably more like: the caller should pass
5404          NULL for the missing information, and this function should learn to
5405          handle that. */
5406
5407       new_text_base_sha1_checksum = NULL;
5408       new_text_base_md5_checksum = NULL;
5409     }
5410
5411   /* For added files without NEW_CONTENTS, then generate the working file
5412      from the provided "pristine" contents.  */
5413   if (new_contents == NULL && copyfrom_url == NULL)
5414     source_abspath = tmp_text_base_abspath;
5415
5416   {
5417     svn_boolean_t record_fileinfo;
5418
5419     /* If new contents were provided, then we do NOT want to record the
5420        file information. We assume the new contents do not match the
5421        "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5422     record_fileinfo = (new_contents == NULL);
5423
5424     /* Install the working copy file (with appropriate translation) from
5425        the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5426        installation from the pristine (available for copied/moved files),
5427        or it will specify a temporary file where we placed a "pristine"
5428        (for an added file) or a detranslated local-mods file.  */
5429     SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5430                                           db, local_abspath,
5431                                           source_abspath,
5432                                           FALSE /* use_commit_times */,
5433                                           record_fileinfo,
5434                                           pool, pool));
5435     all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5436
5437     /* If we installed from somewhere besides the official pristine, then
5438        it is a temporary file, which needs to be removed.  */
5439     if (source_abspath != NULL)
5440       {
5441         SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5442                                              source_abspath,
5443                                              pool, pool));
5444         all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5445       }
5446   }
5447
5448   /* ### ideally, we would have a single DB operation, and queue the work
5449      ### items on that. for now, we'll queue them with the second call.  */
5450
5451   SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5452                                   new_base_props,
5453                                   changed_rev,
5454                                   changed_date,
5455                                   changed_author,
5456                                   original_repos_relpath,
5457                                   original_repos_relpath ? repos_root_url
5458                                                          : NULL,
5459                                   original_repos_relpath ? repos_uuid : NULL,
5460                                   copyfrom_rev,
5461                                   new_text_base_sha1_checksum,
5462                                   TRUE,
5463                                   new_props,
5464                                   FALSE /* is_move */,
5465                                   NULL /* conflict */,
5466                                   all_work_items,
5467                                   pool));
5468
5469   return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5470                                         cancel_func, cancel_baton,
5471                                         pool));
5472 }
5473
5474 svn_error_t *
5475 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5476                                const char *local_abspath,
5477                                apr_hash_t *new_original_props,
5478                                const char *copyfrom_url,
5479                                svn_revnum_t copyfrom_rev,
5480                                apr_pool_t *scratch_pool)
5481 {
5482   svn_wc__db_status_t status;
5483   svn_node_kind_t kind;
5484   const char *original_repos_relpath;
5485   const char *original_root_url;
5486   const char *original_uuid;
5487   svn_boolean_t had_props;
5488   svn_boolean_t props_mod;
5489
5490   svn_revnum_t original_revision;
5491   svn_revnum_t changed_rev;
5492   apr_time_t changed_date;
5493   const char *changed_author;
5494
5495   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5496                                NULL, NULL, NULL, NULL, NULL,
5497                                &original_repos_relpath, &original_root_url,
5498                                &original_uuid, &original_revision, NULL, NULL,
5499                                NULL, NULL, NULL, NULL, &had_props, &props_mod,
5500                                NULL, NULL, NULL,
5501                                wc_ctx->db, local_abspath,
5502                                scratch_pool, scratch_pool));
5503
5504   if (status != svn_wc__db_status_added
5505       || kind != svn_node_dir
5506       || had_props
5507       || props_mod
5508       || !original_repos_relpath)
5509     {
5510       return svn_error_createf(
5511                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5512                     _("'%s' is not an unmodified copied directory"),
5513                     svn_dirent_local_style(local_abspath, scratch_pool));
5514     }
5515   if (original_revision != copyfrom_rev
5516       || strcmp(copyfrom_url,
5517                  svn_path_url_add_component2(original_root_url,
5518                                              original_repos_relpath,
5519                                              scratch_pool)))
5520     {
5521       return svn_error_createf(
5522                     SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5523                     _("Copyfrom '%s' doesn't match original location of '%s'"),
5524                     copyfrom_url,
5525                     svn_dirent_local_style(local_abspath, scratch_pool));
5526     }
5527
5528   {
5529     apr_array_header_t *regular_props;
5530     apr_array_header_t *entry_props;
5531
5532     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5533                                                         scratch_pool),
5534                                  &entry_props, NULL, &regular_props,
5535                                  scratch_pool));
5536
5537     /* Put regular props back into a hash table. */
5538     new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5539
5540     /* Get the change_* info from the entry props.  */
5541     SVN_ERR(accumulate_last_change(&changed_rev,
5542                                    &changed_date,
5543                                    &changed_author,
5544                                    entry_props, scratch_pool, scratch_pool));
5545   }
5546
5547   return svn_error_trace(
5548             svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5549                                    new_original_props,
5550                                    changed_rev, changed_date, changed_author,
5551                                    original_repos_relpath, original_root_url,
5552                                    original_uuid, original_revision,
5553                                    NULL /* children */,
5554                                    svn_depth_infinity,
5555                                    FALSE /* is_move */,
5556                                    NULL /* conflict */,
5557                                    NULL /* work_items */,
5558                                    scratch_pool));
5559 }