]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_wc/update_editor.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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           /* So this deletion wasn't just a deletion, it is actually a
2129              replacement. Let's install a better tree conflict. */
2130
2131           /* ### Should store the conflict in DB to allow reinstalling
2132              ### with theoretically more data in close_directory() */
2133
2134           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2135                                                       eb->db,
2136                                                       db->local_abspath,
2137                                                       tree_conflict,
2138                                                       db->pool, db->pool));
2139
2140           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2141
2142           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2143                                         tree_conflict,
2144                                         eb->db, db->local_abspath,
2145                                         reason, svn_wc_conflict_action_replace,
2146                                         NULL,
2147                                         db->pool, db->pool));
2148
2149           /* And now stop checking for conflicts here and just perform
2150              a shadowed update */
2151           db->edit_conflict = tree_conflict; /* Cache for close_directory */
2152           tree_conflict = NULL; /* No direct notification */
2153           db->shadowed = TRUE; /* Just continue */
2154           conflicted = FALSE; /* No skip */
2155         }
2156       else
2157         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2158                                         eb->db, db->local_abspath, pool));
2159     }
2160
2161   /* Now the "usual" behaviour if already conflicted. Skip it. */
2162   if (conflicted)
2163     {
2164       /* Record this conflict so that its descendants are skipped silently. */
2165       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2166
2167       db->skip_this = TRUE;
2168       db->already_notified = TRUE;
2169
2170       /* We skip this node, but once the update completes the parent node will
2171          be updated to the new revision. So a future recursive update of the
2172          parent will not bring in this new node as the revision of the parent
2173          describes to the repository that all children are available.
2174
2175          To resolve this problem, we add a not-present node to allow bringing
2176          the node in once this conflict is resolved.
2177
2178          Note that we can safely assume that no present base node exists,
2179          because then we would not have received an add_directory.
2180        */
2181       SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2182                                                    db->new_relpath,
2183                                                    eb->repos_root,
2184                                                    eb->repos_uuid,
2185                                                    *eb->target_revision,
2186                                                    svn_node_dir,
2187                                                    NULL, NULL,
2188                                                    pool));
2189
2190       /* ### TODO: Also print victim_path in the skip msg. */
2191       do_notification(eb, db->local_abspath, svn_node_dir,
2192                       svn_wc_notify_skip_conflicted, pool);
2193       return SVN_NO_ERROR;
2194     }
2195   else if (conflict_ignored)
2196     {
2197       db->shadowed = TRUE;
2198     }
2199
2200   if (db->shadowed)
2201     {
2202       /* Nothing to check; does not and will not exist in working copy */
2203     }
2204   else if (versioned_locally_and_present)
2205     {
2206       /* What to do with a versioned or schedule-add dir:
2207
2208          A dir already added without history is OK.  Set add_existed
2209          so that user notification is delayed until after any prop
2210          conflicts have been found.
2211
2212          An existing versioned dir is an error.  In the future we may
2213          relax this restriction and simply update such dirs.
2214
2215          A dir added with history is a tree conflict. */
2216
2217       svn_boolean_t local_is_non_dir;
2218       svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2219
2220       /* Is the local add a copy? */
2221       if (status == svn_wc__db_status_added)
2222         SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2223                                          NULL, NULL, NULL, NULL,
2224                                          eb->db, db->local_abspath,
2225                                          pool, pool));
2226
2227
2228       /* Is there *something* that is not a dir? */
2229       local_is_non_dir = (wc_kind != svn_node_dir
2230                           && status != svn_wc__db_status_deleted);
2231
2232       /* Do tree conflict checking if
2233        *  - if there is a local copy.
2234        *  - if this is a switch operation
2235        *  - the node kinds mismatch
2236        *
2237        * During switch, local adds at the same path as incoming adds get
2238        * "lost" in that switching back to the original will no longer have the
2239        * local add. So switch always alerts the user with a tree conflict. */
2240       if (!eb->adds_as_modification
2241           || local_is_non_dir
2242           || add_status != svn_wc__db_status_added)
2243         {
2244           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2245                                       db->local_abspath,
2246                                       status, FALSE, svn_node_none,
2247                                       svn_wc_conflict_action_add,
2248                                       pool, pool));
2249         }
2250
2251       if (tree_conflict == NULL)
2252         db->add_existed = TRUE; /* Take over WORKING */
2253       else
2254         db->shadowed = TRUE; /* Only update BASE */
2255     }
2256   else if (kind != svn_node_none)
2257     {
2258       /* There's an unversioned node at this path. */
2259       db->obstruction_found = TRUE;
2260
2261       /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2262        * if unversioned obstructions are allowed. */
2263       if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2264         {
2265           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2266           db->shadowed = TRUE;
2267
2268           /* Mark a conflict */
2269           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2270
2271           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2272                                         tree_conflict,
2273                                         eb->db, db->local_abspath,
2274                                         svn_wc_conflict_reason_unversioned,
2275                                         svn_wc_conflict_action_add, NULL,
2276                                         db->pool, pool));
2277           db->edit_conflict = tree_conflict;
2278         }
2279     }
2280
2281   if (tree_conflict)
2282     SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2283                               db->old_repos_relpath, db->old_revision,
2284                               db->new_relpath,
2285                               wc_kind,
2286                               svn_node_dir,
2287                               db->pool, pool));
2288
2289   SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2290                                      eb->db, db->local_abspath,
2291                                      db->new_relpath,
2292                                      eb->repos_root,
2293                                      eb->repos_uuid,
2294                                      *eb->target_revision,
2295                                      db->ambient_depth,
2296                                      (db->shadowed && db->obstruction_found),
2297                                      (! db->shadowed
2298                                       && status == svn_wc__db_status_added),
2299                                      tree_conflict, NULL,
2300                                      pool));
2301
2302   /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2303      updating the DB */
2304   if (!db->shadowed)
2305     SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2306
2307   if (tree_conflict != NULL)
2308     {
2309       if (eb->conflict_func)
2310         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2311                                                  tree_conflict,
2312                                                  NULL /* merge_options */,
2313                                                  eb->conflict_func,
2314                                                  eb->conflict_baton,
2315                                                  eb->cancel_func,
2316                                                  eb->cancel_baton,
2317                                                  pool));
2318
2319       db->already_notified = TRUE;
2320       do_notification(eb, db->local_abspath, svn_node_dir,
2321                       svn_wc_notify_tree_conflict, pool);
2322     }
2323
2324
2325   /* If this add was obstructed by dir scheduled for addition without
2326      history let close_directory() handle the notification because there
2327      might be properties to deal with.  If PATH was added inside a locally
2328      deleted tree, then suppress notification, a tree conflict was already
2329      issued. */
2330   if (eb->notify_func && !db->already_notified && !db->add_existed)
2331     {
2332       svn_wc_notify_action_t action;
2333
2334       if (db->shadowed)
2335         action = svn_wc_notify_update_shadowed_add;
2336       else if (db->obstruction_found || db->add_existed)
2337         action = svn_wc_notify_exists;
2338       else
2339         action = svn_wc_notify_update_add;
2340
2341       db->already_notified = TRUE;
2342
2343       do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2344     }
2345
2346   return SVN_NO_ERROR;
2347 }
2348
2349 /* An svn_delta_editor_t function. */
2350 static svn_error_t *
2351 open_directory(const char *path,
2352                void *parent_baton,
2353                svn_revnum_t base_revision,
2354                apr_pool_t *pool,
2355                void **child_baton)
2356 {
2357   struct dir_baton *db, *pb = parent_baton;
2358   struct edit_baton *eb = pb->edit_baton;
2359   svn_boolean_t have_work;
2360   svn_boolean_t conflicted;
2361   svn_boolean_t conflict_ignored = FALSE;
2362   svn_skel_t *tree_conflict = NULL;
2363   svn_wc__db_status_t status, base_status;
2364   svn_node_kind_t wc_kind;
2365
2366   SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2367   *child_baton = db;
2368
2369   if (db->skip_this)
2370     return SVN_NO_ERROR;
2371
2372   /* Detect obstructing working copies */
2373   {
2374     svn_boolean_t is_root;
2375
2376     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2377                                  pool));
2378
2379     if (is_root)
2380       {
2381         /* Just skip this node; a future update will handle it */
2382         SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2383         db->skip_this = TRUE;
2384         db->already_notified = TRUE;
2385
2386         do_notification(eb, db->local_abspath, svn_node_dir,
2387                         svn_wc_notify_update_skip_obstruction, pool);
2388
2389         return SVN_NO_ERROR;
2390       }
2391   }
2392
2393   /* We should have a write lock on every directory touched.  */
2394   SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2395
2396   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2397                                &db->old_repos_relpath, NULL, NULL,
2398                                &db->changed_rev, &db->changed_date,
2399                                &db->changed_author, &db->ambient_depth,
2400                                NULL, NULL, NULL, NULL,
2401                                NULL, NULL, NULL, NULL, NULL, NULL,
2402                                &conflicted, NULL, NULL, NULL,
2403                                NULL, NULL, &have_work,
2404                                eb->db, db->local_abspath,
2405                                db->pool, pool));
2406
2407   if (!have_work)
2408     base_status = status;
2409   else
2410     SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2411                                      &db->old_repos_relpath, NULL, NULL,
2412                                      &db->changed_rev, &db->changed_date,
2413                                      &db->changed_author, &db->ambient_depth,
2414                                      NULL, NULL, NULL, NULL, NULL, NULL,
2415                                      eb->db, db->local_abspath,
2416                                      db->pool, pool));
2417
2418   db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2419
2420   /* Is this path a conflict victim? */
2421   if (db->shadowed)
2422     conflicted = FALSE; /* Conflict applies to WORKING */
2423   else if (conflicted)
2424     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2425                                     eb->db, db->local_abspath, pool));
2426   if (conflicted)
2427     {
2428       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2429
2430       db->skip_this = TRUE;
2431       db->already_notified = TRUE;
2432
2433       do_notification(eb, db->local_abspath, svn_node_unknown,
2434                       svn_wc_notify_skip_conflicted, pool);
2435
2436       return SVN_NO_ERROR;
2437     }
2438   else if (conflict_ignored)
2439     {
2440       db->shadowed = TRUE;
2441     }
2442
2443   /* Is this path a fresh tree conflict victim?  If so, skip the tree
2444      with one notification. */
2445
2446   /* Check for conflicts only when we haven't already recorded
2447    * a tree-conflict on a parent node. */
2448   if (!db->shadowed)
2449     SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2450                                 status, TRUE, svn_node_dir,
2451                                 svn_wc_conflict_action_edit,
2452                                 db->pool, pool));
2453
2454   /* Remember the roots of any locally deleted trees. */
2455   if (tree_conflict != NULL)
2456     {
2457       svn_wc_conflict_reason_t reason;
2458       db->edit_conflict = tree_conflict;
2459       /* Other modifications wouldn't be a tree conflict */
2460
2461       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2462                                                   eb->db, db->local_abspath,
2463                                                   tree_conflict,
2464                                                   db->pool, db->pool));
2465       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2466                      || reason == svn_wc_conflict_reason_moved_away
2467                      || reason == svn_wc_conflict_reason_replaced
2468                      || reason == svn_wc_conflict_reason_obstructed);
2469
2470       /* Continue updating BASE */
2471       if (reason == svn_wc_conflict_reason_obstructed)
2472         db->edit_obstructed = TRUE;
2473       else
2474         db->shadowed = TRUE;
2475     }
2476
2477   /* Mark directory as being at target_revision and URL, but incomplete. */
2478   SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2479                                                     db->new_relpath,
2480                                                     *eb->target_revision,
2481                                                     pool));
2482
2483   return SVN_NO_ERROR;
2484 }
2485
2486
2487 /* An svn_delta_editor_t function. */
2488 static svn_error_t *
2489 change_dir_prop(void *dir_baton,
2490                 const char *name,
2491                 const svn_string_t *value,
2492                 apr_pool_t *pool)
2493 {
2494   svn_prop_t *propchange;
2495   struct dir_baton *db = dir_baton;
2496
2497   if (db->skip_this)
2498     return SVN_NO_ERROR;
2499
2500   propchange = apr_array_push(db->propchanges);
2501   propchange->name = apr_pstrdup(db->pool, name);
2502   propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2503
2504   if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2505     SVN_ERR(mark_directory_edited(db, pool));
2506
2507   return SVN_NO_ERROR;
2508 }
2509
2510 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2511    to the SVN_PROP_EXTERNALS property, return that change, else return
2512    null.  If PROPCHANGES contains more than one such change, return
2513    the first. */
2514 static const svn_prop_t *
2515 externals_prop_changed(const apr_array_header_t *propchanges)
2516 {
2517   int i;
2518
2519   for (i = 0; i < propchanges->nelts; i++)
2520     {
2521       const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2522       if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2523         return p;
2524     }
2525
2526   return NULL;
2527 }
2528
2529
2530
2531 /* An svn_delta_editor_t function. */
2532 static svn_error_t *
2533 close_directory(void *dir_baton,
2534                 apr_pool_t *pool)
2535 {
2536   struct dir_baton *db = dir_baton;
2537   struct edit_baton *eb = db->edit_baton;
2538   svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2539   apr_array_header_t *entry_prop_changes;
2540   apr_array_header_t *dav_prop_changes;
2541   apr_array_header_t *regular_prop_changes;
2542   apr_hash_t *base_props;
2543   apr_hash_t *actual_props;
2544   apr_hash_t *new_base_props = NULL;
2545   apr_hash_t *new_actual_props = NULL;
2546   svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2547   apr_time_t new_changed_date = 0;
2548   const char *new_changed_author = NULL;
2549   apr_pool_t *scratch_pool = db->pool;
2550   svn_skel_t *all_work_items = NULL;
2551   svn_skel_t *conflict_skel = NULL;
2552
2553   /* Skip if we're in a conflicted tree. */
2554   if (db->skip_this)
2555     {
2556       /* Allow the parent to complete its update. */
2557       SVN_ERR(maybe_release_dir_info(db));
2558
2559       return SVN_NO_ERROR;
2560     }
2561
2562   if (db->edited)
2563     conflict_skel = db->edit_conflict;
2564
2565   SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2566                                &dav_prop_changes, &regular_prop_changes, pool));
2567
2568   /* Fetch the existing properties.  */
2569   if ((!db->adding_dir || db->add_existed)
2570       && !db->shadowed)
2571     {
2572       SVN_ERR(svn_wc__get_actual_props(&actual_props,
2573                                        eb->db, db->local_abspath,
2574                                        scratch_pool, scratch_pool));
2575     }
2576   else
2577     actual_props = apr_hash_make(pool);
2578
2579   if (db->add_existed)
2580     {
2581       /* This node already exists. Grab the current pristine properties. */
2582       SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2583                                              eb->db, db->local_abspath,
2584                                              scratch_pool, scratch_pool));
2585     }
2586   else if (!db->adding_dir)
2587     {
2588       /* Get the BASE properties for proper merging. */
2589       SVN_ERR(svn_wc__db_base_get_props(&base_props,
2590                                         eb->db, db->local_abspath,
2591                                         scratch_pool, scratch_pool));
2592     }
2593   else
2594     base_props = apr_hash_make(pool);
2595
2596   /* An incomplete directory might have props which were supposed to be
2597      deleted but weren't.  Because the server sent us all the props we're
2598      supposed to have, any previous base props not in this list must be
2599      deleted (issue #1672). */
2600   if (db->was_incomplete)
2601     {
2602       int i;
2603       apr_hash_t *props_to_delete;
2604       apr_hash_index_t *hi;
2605
2606       /* In a copy of the BASE props, remove every property that we see an
2607          incoming change for. The remaining unmentioned properties are those
2608          which need to be deleted.  */
2609       props_to_delete = apr_hash_copy(pool, base_props);
2610       for (i = 0; i < regular_prop_changes->nelts; i++)
2611         {
2612           const svn_prop_t *prop;
2613           prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2614           svn_hash_sets(props_to_delete, prop->name, NULL);
2615         }
2616
2617       /* Add these props to the incoming propchanges (in
2618        * regular_prop_changes).  */
2619       for (hi = apr_hash_first(pool, props_to_delete);
2620            hi != NULL;
2621            hi = apr_hash_next(hi))
2622         {
2623           const char *propname = svn__apr_hash_index_key(hi);
2624           svn_prop_t *prop = apr_array_push(regular_prop_changes);
2625
2626           /* Record a deletion for PROPNAME.  */
2627           prop->name = propname;
2628           prop->value = NULL;
2629         }
2630     }
2631
2632   /* If this directory has property changes stored up, now is the time
2633      to deal with them. */
2634   if (regular_prop_changes->nelts)
2635     {
2636       /* If recording traversal info, then see if the
2637          SVN_PROP_EXTERNALS property on this directory changed,
2638          and record before and after for the change. */
2639       if (eb->external_func)
2640         {
2641           const svn_prop_t *change
2642             = externals_prop_changed(regular_prop_changes);
2643
2644           if (change)
2645             {
2646               const svn_string_t *new_val_s = change->value;
2647               const svn_string_t *old_val_s;
2648
2649               old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2650
2651               if ((new_val_s == NULL) && (old_val_s == NULL))
2652                 ; /* No value before, no value after... so do nothing. */
2653               else if (new_val_s && old_val_s
2654                        && (svn_string_compare(old_val_s, new_val_s)))
2655                 ; /* Value did not change... so do nothing. */
2656               else if (old_val_s || new_val_s)
2657                 /* something changed, record the change */
2658                 {
2659                   SVN_ERR((eb->external_func)(
2660                                        eb->external_baton,
2661                                        db->local_abspath,
2662                                        old_val_s,
2663                                        new_val_s,
2664                                        db->ambient_depth,
2665                                        db->pool));
2666                 }
2667             }
2668         }
2669
2670       if (db->shadowed)
2671         {
2672           /* We don't have a relevant actual row, but we need actual properties
2673              to allow property merging without conflicts. */
2674           if (db->adding_dir)
2675             actual_props = apr_hash_make(scratch_pool);
2676           else
2677             actual_props = base_props;
2678         }
2679
2680       /* Merge pending properties. */
2681       new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2682                                        db->pool);
2683       SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2684                                     &prop_state,
2685                                     &new_actual_props,
2686                                     eb->db,
2687                                     db->local_abspath,
2688                                     NULL /* use baseprops */,
2689                                     base_props,
2690                                     actual_props,
2691                                     regular_prop_changes,
2692                                     db->pool,
2693                                     scratch_pool),
2694                 _("Couldn't do property merge"));
2695       /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2696       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2697     }
2698
2699   SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2700                                  &new_changed_author, entry_prop_changes,
2701                                  scratch_pool, scratch_pool));
2702
2703   /* Check if we should add some not-present markers before marking the
2704      directory complete (Issue #3569) */
2705   {
2706     apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2707
2708     if (new_children != NULL)
2709       {
2710         apr_hash_index_t *hi;
2711         apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2712
2713         for (hi = apr_hash_first(scratch_pool, new_children);
2714              hi;
2715              hi = apr_hash_next(hi))
2716           {
2717             const char *child_name;
2718             const char *child_abspath;
2719             const char *child_relpath;
2720             const svn_dirent_t *dirent;
2721             svn_wc__db_status_t status;
2722             svn_node_kind_t child_kind;
2723             svn_error_t *err;
2724
2725             svn_pool_clear(iterpool);
2726
2727             child_name = svn__apr_hash_index_key(hi);
2728             child_abspath = svn_dirent_join(db->local_abspath, child_name,
2729                                             iterpool);
2730
2731             dirent = svn__apr_hash_index_val(hi);
2732             child_kind = (dirent->kind == svn_node_dir)
2733                                         ? svn_node_dir
2734                                         : svn_node_file;
2735
2736             if (db->ambient_depth < svn_depth_immediates
2737                 && child_kind == svn_node_dir)
2738               continue; /* We don't need the subdirs */
2739
2740             /* ### We just check if there is some node in BASE at this path */
2741             err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2742                                            NULL, NULL, NULL, NULL, NULL, NULL,
2743                                            NULL, NULL, NULL, NULL, NULL,
2744                                            eb->db, child_abspath,
2745                                            iterpool, iterpool);
2746
2747             if (!err)
2748               {
2749                 svn_boolean_t is_wcroot;
2750                 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2751                                              iterpool));
2752
2753                 if (!is_wcroot)
2754                   continue; /* Everything ok... Nothing to do here */
2755                 /* Fall through to allow recovering later */
2756               }
2757             else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2758               return svn_error_trace(err);
2759
2760             svn_error_clear(err);
2761
2762             child_relpath = svn_relpath_join(db->new_relpath, child_name,
2763                                              iterpool);
2764
2765             SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2766                                                          child_abspath,
2767                                                          child_relpath,
2768                                                          eb->repos_root,
2769                                                          eb->repos_uuid,
2770                                                          *eb->target_revision,
2771                                                          child_kind,
2772                                                          NULL, NULL,
2773                                                          iterpool));
2774           }
2775
2776         svn_pool_destroy(iterpool);
2777       }
2778   }
2779
2780   if (apr_hash_count(db->not_present_files))
2781     {
2782       apr_hash_index_t *hi;
2783       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2784
2785       /* This should call some new function (which could also be used
2786          for new_children above) to add all the names in single
2787          transaction, but I can't even trigger it.  I've tried
2788          ra_local, ra_svn, ra_neon, ra_serf and they all call
2789          close_file before close_dir. */
2790       for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2791            hi;
2792            hi = apr_hash_next(hi))
2793         {
2794           const char *child = svn__apr_hash_index_key(hi);
2795           const char *child_abspath, *child_relpath;
2796
2797           svn_pool_clear(iterpool);
2798
2799           child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2800           child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2801
2802           SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2803                                                        child_abspath,
2804                                                        child_relpath,
2805                                                        eb->repos_root,
2806                                                        eb->repos_uuid,
2807                                                        *eb->target_revision,
2808                                                        svn_node_file,
2809                                                        NULL, NULL,
2810                                                        iterpool));
2811         }
2812       svn_pool_destroy(iterpool);
2813     }
2814
2815   /* If this directory is merely an anchor for a targeted child, then we
2816      should not be updating the node at all.  */
2817   if (db->parent_baton == NULL
2818       && *eb->target_basename != '\0')
2819     {
2820       /* And we should not have received any changes!  */
2821       SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2822       /* ... which also implies NEW_CHANGED_* are not set,
2823          and NEW_BASE_PROPS == NULL.  */
2824     }
2825   else
2826     {
2827       apr_hash_t *props;
2828       apr_array_header_t *iprops = NULL;
2829
2830       /* ### we know a base node already exists. it was created in
2831          ### open_directory or add_directory.  let's just preserve the
2832          ### existing DEPTH value, and possibly CHANGED_*.  */
2833       /* If we received any changed_* values, then use them.  */
2834       if (SVN_IS_VALID_REVNUM(new_changed_rev))
2835         db->changed_rev = new_changed_rev;
2836       if (new_changed_date != 0)
2837         db->changed_date = new_changed_date;
2838       if (new_changed_author != NULL)
2839         db->changed_author = new_changed_author;
2840
2841       /* If no depth is set yet, set to infinity. */
2842       if (db->ambient_depth == svn_depth_unknown)
2843         db->ambient_depth = svn_depth_infinity;
2844
2845       if (eb->depth_is_sticky
2846           && db->ambient_depth != eb->requested_depth)
2847         {
2848           /* After a depth upgrade the entry must reflect the new depth.
2849              Upgrading to infinity changes the depth of *all* directories,
2850              upgrading to something else only changes the target. */
2851
2852           if (eb->requested_depth == svn_depth_infinity
2853               || (strcmp(db->local_abspath, eb->target_abspath) == 0
2854                   && eb->requested_depth > db->ambient_depth))
2855             {
2856               db->ambient_depth = eb->requested_depth;
2857             }
2858         }
2859
2860       /* Do we have new properties to install? Or shall we simply retain
2861          the prior set of properties? If we're installing new properties,
2862          then we also want to write them to an old-style props file.  */
2863       props = new_base_props;
2864       if (props == NULL)
2865         props = base_props;
2866
2867       if (conflict_skel)
2868         {
2869           svn_skel_t *work_item;
2870
2871           SVN_ERR(complete_conflict(conflict_skel,
2872                                     db->edit_baton,
2873                                     db->local_abspath,
2874                                     db->old_repos_relpath,
2875                                     db->old_revision,
2876                                     db->new_relpath,
2877                                     svn_node_dir, svn_node_dir,
2878                                     db->pool, scratch_pool));
2879
2880           SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2881                                                   eb->db, db->local_abspath,
2882                                                   conflict_skel,
2883                                                   scratch_pool, scratch_pool));
2884
2885           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2886                                             scratch_pool);
2887         }
2888
2889       /* Any inherited props to be set set for this base node? */
2890       if (eb->wcroot_iprops)
2891         {
2892           iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2893
2894           /* close_edit may also update iprops for switched nodes, catching
2895              those for which close_directory is never called (e.g. a switch
2896              with no changes).  So as a minor optimization we remove any
2897              iprops from the hash so as not to set them again in
2898              close_edit. */
2899           if (iprops)
2900             svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2901         }
2902
2903       /* Update the BASE data for the directory and mark the directory
2904          complete */
2905       SVN_ERR(svn_wc__db_base_add_directory(
2906                 eb->db, db->local_abspath,
2907                 eb->wcroot_abspath,
2908                 db->new_relpath,
2909                 eb->repos_root, eb->repos_uuid,
2910                 *eb->target_revision,
2911                 props,
2912                 db->changed_rev, db->changed_date, db->changed_author,
2913                 NULL /* children */,
2914                 db->ambient_depth,
2915                 (dav_prop_changes->nelts > 0)
2916                     ? svn_prop_array_to_hash(dav_prop_changes, pool)
2917                     : NULL,
2918                 conflict_skel,
2919                 (! db->shadowed) && new_base_props != NULL,
2920                 new_actual_props,
2921                 iprops, all_work_items,
2922                 scratch_pool));
2923     }
2924
2925   /* Process all of the queued work items for this directory.  */
2926   SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2927                          eb->cancel_func, eb->cancel_baton,
2928                          scratch_pool));
2929
2930   if (conflict_skel && eb->conflict_func)
2931     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2932                                              conflict_skel,
2933                                              NULL /* merge_options */,
2934                                              eb->conflict_func,
2935                                              eb->conflict_baton,
2936                                              eb->cancel_func,
2937                                              eb->cancel_baton,
2938                                              scratch_pool));
2939
2940   /* Notify of any prop changes on this directory -- but do nothing if
2941      it's an added or skipped directory, because notification has already
2942      happened in that case - unless the add was obstructed by a dir
2943      scheduled for addition without history, in which case we handle
2944      notification here). */
2945   if (!db->already_notified && eb->notify_func && db->edited)
2946     {
2947       svn_wc_notify_t *notify;
2948       svn_wc_notify_action_t action;
2949
2950       if (db->shadowed || db->edit_obstructed)
2951         action = svn_wc_notify_update_shadowed_update;
2952       else if (db->obstruction_found || db->add_existed)
2953         action = svn_wc_notify_exists;
2954       else
2955         action = svn_wc_notify_update_update;
2956
2957       notify = svn_wc_create_notify(db->local_abspath, action, pool);
2958       notify->kind = svn_node_dir;
2959       notify->prop_state = prop_state;
2960       notify->revision = *eb->target_revision;
2961       notify->old_revision = db->old_revision;
2962
2963       eb->notify_func(eb->notify_baton, notify, scratch_pool);
2964     }
2965
2966   /* We're done with this directory, so remove one reference from the
2967      bump information. */
2968   SVN_ERR(maybe_release_dir_info(db));
2969
2970   return SVN_NO_ERROR;
2971 }
2972
2973
2974 /* Common code for 'absent_file' and 'absent_directory'. */
2975 static svn_error_t *
2976 absent_node(const char *path,
2977             svn_node_kind_t absent_kind,
2978             void *parent_baton,
2979             apr_pool_t *pool)
2980 {
2981   struct dir_baton *pb = parent_baton;
2982   struct edit_baton *eb = pb->edit_baton;
2983   apr_pool_t *scratch_pool = svn_pool_create(pool);
2984   const char *name = svn_dirent_basename(path, NULL);
2985   const char *local_abspath;
2986   svn_error_t *err;
2987   svn_wc__db_status_t status;
2988   svn_node_kind_t kind;
2989
2990   if (pb->skip_this)
2991     return SVN_NO_ERROR;
2992
2993   SVN_ERR(mark_directory_edited(pb, scratch_pool));
2994
2995   local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2996
2997   /* If an item by this name is scheduled for addition that's a
2998      genuine tree-conflict.  */
2999   err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
3000                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3001                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3002                              NULL, NULL, NULL, NULL,
3003                              eb->db, local_abspath,
3004                              scratch_pool, scratch_pool);
3005
3006   if (err)
3007     {
3008       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3009         return svn_error_trace(err);
3010
3011       svn_error_clear(err);
3012       status = svn_wc__db_status_not_present;
3013       kind = svn_node_unknown;
3014     }
3015
3016   if (status == svn_wc__db_status_normal)
3017     {
3018       svn_boolean_t wcroot;
3019       /* We found an obstructing working copy or a file external! */
3020
3021       SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
3022                                    scratch_pool));
3023
3024       if (wcroot)
3025         {
3026           /*
3027              We have an obstructing working copy; possibly a directory external
3028
3029              We can do two things now:
3030              1) notify the user, record a skip, etc.
3031              2) Just record the absent node in BASE in the parent
3032                 working copy.
3033
3034              As option 2 happens to be exactly what we do anyway, fall through.
3035            */
3036         }
3037       else
3038         {
3039           /* The server asks us to replace a file external
3040              (Existing BASE node; not reported by the working copy crawler or
3041               there would have been a delete_entry() call.
3042
3043              There is no way we can store this state in the working copy as
3044              the BASE layer is already filled.
3045
3046              We could error out, but that is not helping anybody; the user is not
3047              even seeing with what the file external would be replaced, so let's
3048              report a skip and continue the update.
3049            */
3050
3051           if (eb->notify_func)
3052             {
3053               svn_wc_notify_t *notify;
3054               notify = svn_wc_create_notify(
3055                                     local_abspath,
3056                                     svn_wc_notify_update_skip_obstruction,
3057                                     scratch_pool);
3058
3059               eb->notify_func(eb->notify_baton, notify, scratch_pool);
3060             }
3061
3062           svn_pool_destroy(scratch_pool);
3063           return SVN_NO_ERROR;
3064         }
3065     }
3066   else if (status == svn_wc__db_status_not_present
3067            || status == svn_wc__db_status_server_excluded
3068            || status == svn_wc__db_status_excluded)
3069     {
3070       /* The BASE node is not actually there, so we can safely turn it into
3071          an absent node */
3072     }
3073   else
3074     {
3075       /* We have a local addition. If this would be a BASE node it would have
3076          been deleted before we get here. (Which might have turned it into
3077          a copy).
3078
3079          ### This should be recorded as a tree conflict and the update
3080          ### can just continue, as we can just record the absent status
3081          ### in BASE.
3082        */
3083       SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3084
3085       return svn_error_createf(
3086          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3087          _("Failed to mark '%s' absent: item of the same name is already "
3088            "scheduled for addition"),
3089          svn_dirent_local_style(local_abspath, pool));
3090     }
3091
3092   {
3093     const char *repos_relpath;
3094     repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3095
3096     /* Insert an excluded node below the parent node to note that this child
3097        is absent. (This puts it in the parent db if the child is obstructed) */
3098     SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3099                                               repos_relpath, eb->repos_root,
3100                                               eb->repos_uuid,
3101                                               *(eb->target_revision),
3102                                               absent_kind,
3103                                               svn_wc__db_status_server_excluded,
3104                                               NULL, NULL,
3105                                               scratch_pool));
3106   }
3107
3108   svn_pool_destroy(scratch_pool);
3109
3110   return SVN_NO_ERROR;
3111 }
3112
3113
3114 /* An svn_delta_editor_t function. */
3115 static svn_error_t *
3116 absent_file(const char *path,
3117             void *parent_baton,
3118             apr_pool_t *pool)
3119 {
3120   return absent_node(path, svn_node_file, parent_baton, pool);
3121 }
3122
3123
3124 /* An svn_delta_editor_t function. */
3125 static svn_error_t *
3126 absent_directory(const char *path,
3127                  void *parent_baton,
3128                  apr_pool_t *pool)
3129 {
3130   return absent_node(path, svn_node_dir, parent_baton, pool);
3131 }
3132
3133
3134 /* An svn_delta_editor_t function. */
3135 static svn_error_t *
3136 add_file(const char *path,
3137          void *parent_baton,
3138          const char *copyfrom_path,
3139          svn_revnum_t copyfrom_rev,
3140          apr_pool_t *pool,
3141          void **file_baton)
3142 {
3143   struct dir_baton *pb = parent_baton;
3144   struct edit_baton *eb = pb->edit_baton;
3145   struct file_baton *fb;
3146   svn_node_kind_t kind = svn_node_none;
3147   svn_node_kind_t wc_kind = svn_node_unknown;
3148   svn_wc__db_status_t status = svn_wc__db_status_normal;
3149   apr_pool_t *scratch_pool;
3150   svn_boolean_t conflicted = FALSE;
3151   svn_boolean_t conflict_ignored = FALSE;
3152   svn_boolean_t versioned_locally_and_present = FALSE;
3153   svn_skel_t *tree_conflict = NULL;
3154   svn_error_t *err = SVN_NO_ERROR;
3155
3156   SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3157
3158   SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3159   *file_baton = fb;
3160
3161   if (fb->skip_this)
3162     return SVN_NO_ERROR;
3163
3164   SVN_ERR(mark_file_edited(fb, pool));
3165
3166   /* The file_pool can stick around for a *long* time, so we want to
3167      use a subpool for any temporary allocations. */
3168   scratch_pool = svn_pool_create(pool);
3169
3170
3171   /* It may not be named the same as the administrative directory. */
3172   if (svn_wc_is_adm_dir(fb->name, pool))
3173     return svn_error_createf(
3174        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3175        _("Failed to add file '%s': object of the same name as the "
3176          "administrative directory"),
3177        svn_dirent_local_style(fb->local_abspath, pool));
3178
3179   if (!eb->clean_checkout)
3180     {
3181       SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3182
3183       err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3184                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3185                                 NULL, NULL, NULL, NULL, NULL,
3186                                 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3187                                 eb->db, fb->local_abspath,
3188                                 scratch_pool, scratch_pool);
3189     }
3190
3191   if (err)
3192     {
3193       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194         return svn_error_trace(err);
3195
3196       svn_error_clear(err);
3197       wc_kind = svn_node_unknown;
3198       conflicted = FALSE;
3199
3200       versioned_locally_and_present = FALSE;
3201     }
3202   else if (wc_kind == svn_node_dir
3203            && status == svn_wc__db_status_normal)
3204     {
3205       /* !! We found the root of a separate working copy obstructing the wc !!
3206
3207          If the directory would be part of our own working copy then
3208          we wouldn't have been called as an add_file().
3209
3210          The only thing we can do is add a not-present node, to allow
3211          a future update to bring in the new files when the problem is
3212          resolved. */
3213       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3214                     (void *)1);
3215
3216       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3217       fb->skip_this = TRUE;
3218       fb->already_notified = TRUE;
3219
3220       do_notification(eb, fb->local_abspath, svn_node_file,
3221                       svn_wc_notify_update_skip_obstruction, scratch_pool);
3222
3223       svn_pool_destroy(scratch_pool);
3224
3225       return SVN_NO_ERROR;
3226     }
3227   else if (status == svn_wc__db_status_normal
3228            && (wc_kind == svn_node_file
3229                || wc_kind == svn_node_symlink))
3230     {
3231       /* We found a file external occupating the place we need in BASE.
3232
3233          We can't add a not-present node in this case as that would overwrite
3234          the file external. Luckily the file external itself stops us from
3235          forgetting a child of this parent directory like an obstructing
3236          working copy would.
3237
3238          The reason we get here is that the adm crawler doesn't report
3239          file externals.
3240       */
3241       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3242       fb->skip_this = TRUE;
3243       fb->already_notified = TRUE;
3244
3245       do_notification(eb, fb->local_abspath, svn_node_file,
3246                       svn_wc_notify_update_skip_obstruction, scratch_pool);
3247
3248       svn_pool_destroy(scratch_pool);
3249
3250       return SVN_NO_ERROR;
3251     }
3252   else if (wc_kind == svn_node_unknown)
3253     versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3254   else
3255     versioned_locally_and_present = IS_NODE_PRESENT(status);
3256
3257
3258   /* Is this path a conflict victim? */
3259   if (fb->shadowed)
3260     conflicted = FALSE; /* Conflict applies to WORKING */
3261   else if (conflicted)
3262     {
3263       if (pb->deletion_conflicts)
3264         tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3265
3266       if (tree_conflict)
3267         {
3268           svn_wc_conflict_reason_t reason;
3269           /* So this deletion wasn't just a deletion, it is actually a
3270              replacement. Let's install a better tree conflict. */
3271
3272           /* ### Should store the conflict in DB to allow reinstalling
3273              ### with theoretically more data in close_directory() */
3274
3275           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3276                                                       eb->db,
3277                                                       fb->local_abspath,
3278                                                       tree_conflict,
3279                                                       fb->pool, fb->pool));
3280
3281           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3282
3283           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3284                                         tree_conflict,
3285                                         eb->db, fb->local_abspath,
3286                                         reason, svn_wc_conflict_action_replace,
3287                                         NULL,
3288                                         fb->pool, fb->pool));
3289
3290           /* And now stop checking for conflicts here and just perform
3291              a shadowed update */
3292           fb->edit_conflict = tree_conflict; /* Cache for close_file */
3293           tree_conflict = NULL; /* No direct notification */
3294           fb->shadowed = TRUE; /* Just continue */
3295           conflicted = FALSE; /* No skip */
3296         }
3297       else
3298         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3299                                         eb->db, fb->local_abspath, pool));
3300     }
3301
3302   /* Now the usual conflict handling: skip. */
3303   if (conflicted)
3304     {
3305       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3306
3307       fb->skip_this = TRUE;
3308       fb->already_notified = TRUE;
3309
3310       /* We skip this node, but once the update completes the parent node will
3311          be updated to the new revision. So a future recursive update of the
3312          parent will not bring in this new node as the revision of the parent
3313          describes to the repository that all children are available.
3314
3315          To resolve this problem, we add a not-present node to allow bringing
3316          the node in once this conflict is resolved.
3317
3318          Note that we can safely assume that no present base node exists,
3319          because then we would not have received an add_file.
3320        */
3321       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3322                     (void *)1);
3323
3324       do_notification(eb, fb->local_abspath, svn_node_unknown,
3325                       svn_wc_notify_skip_conflicted, scratch_pool);
3326
3327       svn_pool_destroy(scratch_pool);
3328
3329       return SVN_NO_ERROR;
3330     }
3331   else if (conflict_ignored)
3332     {
3333       fb->shadowed = TRUE;
3334     }
3335
3336   if (fb->shadowed)
3337     {
3338       /* Nothing to check; does not and will not exist in working copy */
3339     }
3340   else if (versioned_locally_and_present)
3341     {
3342       /* What to do with a versioned or schedule-add file:
3343
3344          If the UUID doesn't match the parent's, or the URL isn't a child of
3345          the parent dir's URL, it's an error.
3346
3347          Set add_existed so that user notification is delayed until after any
3348          text or prop conflicts have been found.
3349
3350          Whether the incoming add is a symlink or a file will only be known in
3351          close_file(), when the props are known. So with a locally added file
3352          or symlink, let close_file() check for a tree conflict.
3353
3354          We will never see missing files here, because these would be
3355          re-added during the crawler phase. */
3356       svn_boolean_t local_is_file;
3357
3358       /* Is the local node a copy or move */
3359       if (status == svn_wc__db_status_added)
3360         SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3361                                          NULL, NULL, NULL,
3362                                          eb->db, fb->local_abspath,
3363                                          scratch_pool, scratch_pool));
3364
3365       /* Is there something that is a file? */
3366       local_is_file = (wc_kind == svn_node_file
3367                        || wc_kind == svn_node_symlink);
3368
3369       /* Do tree conflict checking if
3370        *  - if there is a local copy.
3371        *  - if this is a switch operation
3372        *  - the node kinds mismatch
3373        *
3374        * During switch, local adds at the same path as incoming adds get
3375        * "lost" in that switching back to the original will no longer have the
3376        * local add. So switch always alerts the user with a tree conflict. */
3377       if (!eb->adds_as_modification
3378           || !local_is_file
3379           || status != svn_wc__db_status_added)
3380         {
3381           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3382                                       fb->local_abspath,
3383                                       status, FALSE, svn_node_none,
3384                                       svn_wc_conflict_action_add,
3385                                       scratch_pool, scratch_pool));
3386         }
3387
3388       if (tree_conflict == NULL)
3389         fb->add_existed = TRUE; /* Take over WORKING */
3390       else
3391         fb->shadowed = TRUE; /* Only update BASE */
3392
3393     }
3394   else if (kind != svn_node_none)
3395     {
3396       /* There's an unversioned node at this path. */
3397       fb->obstruction_found = TRUE;
3398
3399       /* Unversioned, obstructing files are handled by text merge/conflict,
3400        * if unversioned obstructions are allowed. */
3401       if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3402         {
3403           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3404           fb->shadowed = TRUE;
3405
3406           /* Mark a conflict */
3407           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3408
3409           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3410                                         tree_conflict,
3411                                         eb->db, fb->local_abspath,
3412                                         svn_wc_conflict_reason_unversioned,
3413                                         svn_wc_conflict_action_add,
3414                                         NULL,
3415                                         fb->pool, scratch_pool));
3416         }
3417     }
3418
3419   /* When this is not the update target add a not-present BASE node now,
3420      to allow marking the parent directory complete in its close_edit() call.
3421      This resolves issues when that occurs before the close_file(). */
3422   if (pb->parent_baton
3423       || *eb->target_basename == '\0'
3424       || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3425     {
3426       svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3427                     (void *)1);
3428     }
3429
3430   if (tree_conflict != NULL)
3431     {
3432       SVN_ERR(complete_conflict(tree_conflict,
3433                                 fb->edit_baton,
3434                                 fb->local_abspath,
3435                                 fb->old_repos_relpath,
3436                                 fb->old_revision,
3437                                 fb->new_relpath,
3438                                 wc_kind,
3439                                 svn_node_file,
3440                                 fb->pool, scratch_pool));
3441
3442       SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3443                                           fb->local_abspath,
3444                                           tree_conflict, NULL,
3445                                           scratch_pool));
3446
3447       if (eb->conflict_func)
3448         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3449                                                  tree_conflict,
3450                                                  NULL /* merge_options */,
3451                                                  eb->conflict_func,
3452                                                  eb->conflict_baton,
3453                                                  eb->cancel_func,
3454                                                  eb->cancel_baton,
3455                                                  scratch_pool));
3456
3457       fb->already_notified = TRUE;
3458       do_notification(eb, fb->local_abspath, svn_node_file,
3459                       svn_wc_notify_tree_conflict, scratch_pool);
3460     }
3461
3462   svn_pool_destroy(scratch_pool);
3463
3464   return SVN_NO_ERROR;
3465 }
3466
3467
3468 /* An svn_delta_editor_t function. */
3469 static svn_error_t *
3470 open_file(const char *path,
3471           void *parent_baton,
3472           svn_revnum_t base_revision,
3473           apr_pool_t *pool,
3474           void **file_baton)
3475 {
3476   struct dir_baton *pb = parent_baton;
3477   struct edit_baton *eb = pb->edit_baton;
3478   struct file_baton *fb;
3479   svn_boolean_t conflicted;
3480   svn_boolean_t conflict_ignored = FALSE;
3481   svn_boolean_t have_work;
3482   svn_wc__db_status_t status;
3483   svn_node_kind_t wc_kind;
3484   svn_skel_t *tree_conflict = NULL;
3485
3486   /* the file_pool can stick around for a *long* time, so we want to use
3487      a subpool for any temporary allocations. */
3488   apr_pool_t *scratch_pool = svn_pool_create(pool);
3489
3490   SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3491   *file_baton = fb;
3492
3493   if (fb->skip_this)
3494     return SVN_NO_ERROR;
3495
3496   /* Detect obstructing working copies */
3497   {
3498     svn_boolean_t is_root;
3499
3500     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3501                                  pool));
3502
3503     if (is_root)
3504       {
3505         /* Just skip this node; a future update will handle it */
3506         SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3507         fb->skip_this = TRUE;
3508         fb->already_notified = TRUE;
3509
3510         do_notification(eb, fb->local_abspath, svn_node_file,
3511                         svn_wc_notify_update_skip_obstruction, pool);
3512
3513         return SVN_NO_ERROR;
3514       }
3515   }
3516
3517   /* Sanity check. */
3518
3519   /* If replacing, make sure the .svn entry already exists. */
3520   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3521                                &fb->old_repos_relpath, NULL, NULL,
3522                                &fb->changed_rev, &fb->changed_date,
3523                                &fb->changed_author, NULL,
3524                                &fb->original_checksum, NULL, NULL, NULL,
3525                                NULL, NULL, NULL, NULL, NULL, NULL,
3526                                &conflicted, NULL, NULL, &fb->local_prop_mods,
3527                                NULL, NULL, &have_work,
3528                                eb->db, fb->local_abspath,
3529                                fb->pool, scratch_pool));
3530
3531   if (have_work)
3532     SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3533                                      &fb->old_repos_relpath, NULL, NULL,
3534                                      &fb->changed_rev, &fb->changed_date,
3535                                      &fb->changed_author, NULL,
3536                                      &fb->original_checksum, NULL, NULL,
3537                                      NULL, NULL, NULL,
3538                                      eb->db, fb->local_abspath,
3539                                      fb->pool, scratch_pool));
3540
3541   /* Is this path a conflict victim? */
3542   if (fb->shadowed)
3543     conflicted = FALSE; /* Conflict applies to WORKING */
3544   else if (conflicted)
3545     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3546                                     eb->db, fb->local_abspath, pool));
3547   if (conflicted)
3548     {
3549       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3550
3551       fb->skip_this = TRUE;
3552       fb->already_notified = TRUE;
3553
3554       do_notification(eb, fb->local_abspath, svn_node_unknown,
3555                       svn_wc_notify_skip_conflicted, scratch_pool);
3556
3557       svn_pool_destroy(scratch_pool);
3558
3559       return SVN_NO_ERROR;
3560     }
3561   else if (conflict_ignored)
3562     {
3563       fb->shadowed = TRUE;
3564     }
3565
3566   /* Check for conflicts only when we haven't already recorded
3567    * a tree-conflict on a parent node. */
3568   if (!fb->shadowed)
3569     SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3570                                 status, TRUE, svn_node_file,
3571                                 svn_wc_conflict_action_edit,
3572                                 fb->pool, scratch_pool));
3573
3574   /* Is this path the victim of a newly-discovered tree conflict? */
3575   if (tree_conflict != NULL)
3576     {
3577       svn_wc_conflict_reason_t reason;
3578       fb->edit_conflict = tree_conflict;
3579       /* Other modifications wouldn't be a tree conflict */
3580
3581       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3582                                                   eb->db, fb->local_abspath,
3583                                                   tree_conflict,
3584                                                   scratch_pool, scratch_pool));
3585       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3586                      || reason == svn_wc_conflict_reason_moved_away
3587                      || reason == svn_wc_conflict_reason_replaced
3588                      || reason == svn_wc_conflict_reason_obstructed);
3589
3590       /* Continue updating BASE */
3591       if (reason == svn_wc_conflict_reason_obstructed)
3592         fb->edit_obstructed = TRUE;
3593       else
3594         fb->shadowed = TRUE;
3595     }
3596
3597   svn_pool_destroy(scratch_pool);
3598
3599   return SVN_NO_ERROR;
3600 }
3601
3602 /* Implements svn_stream_lazyopen_func_t. */
3603 static svn_error_t *
3604 lazy_open_source(svn_stream_t **stream,
3605                  void *baton,
3606                  apr_pool_t *result_pool,
3607                  apr_pool_t *scratch_pool)
3608 {
3609   struct file_baton *fb = baton;
3610
3611   SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3612                                    fb->local_abspath,
3613                                    fb->original_checksum,
3614                                    result_pool, scratch_pool));
3615
3616
3617   return SVN_NO_ERROR;
3618 }
3619
3620 struct lazy_target_baton {
3621   struct file_baton *fb;
3622   struct handler_baton *hb;
3623   struct edit_baton *eb;
3624 };
3625
3626 /* Implements svn_stream_lazyopen_func_t. */
3627 static svn_error_t *
3628 lazy_open_target(svn_stream_t **stream,
3629                  void *baton,
3630                  apr_pool_t *result_pool,
3631                  apr_pool_t *scratch_pool)
3632 {
3633   struct lazy_target_baton *tb = baton;
3634
3635   SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3636                                      NULL, &tb->hb->new_text_base_sha1_checksum,
3637                                      tb->fb->edit_baton->db,
3638                                      tb->eb->wcroot_abspath,
3639                                      result_pool, scratch_pool));
3640
3641   return SVN_NO_ERROR;
3642 }
3643
3644 /* An svn_delta_editor_t function. */
3645 static svn_error_t *
3646 apply_textdelta(void *file_baton,
3647                 const char *expected_checksum,
3648                 apr_pool_t *pool,
3649                 svn_txdelta_window_handler_t *handler,
3650                 void **handler_baton)
3651 {
3652   struct file_baton *fb = file_baton;
3653   apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3654   struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3655   struct edit_baton *eb = fb->edit_baton;
3656   const svn_checksum_t *recorded_base_checksum;
3657   svn_checksum_t *expected_base_checksum;
3658   svn_stream_t *source;
3659   struct lazy_target_baton *tb;
3660   svn_stream_t *target;
3661
3662   if (fb->skip_this)
3663     {
3664       *handler = svn_delta_noop_window_handler;
3665       *handler_baton = NULL;
3666       return SVN_NO_ERROR;
3667     }
3668
3669   SVN_ERR(mark_file_edited(fb, pool));
3670
3671   /* Parse checksum or sets expected_base_checksum to NULL */
3672   SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3673                                  expected_checksum, pool));
3674
3675   /* Before applying incoming svndiff data to text base, make sure
3676      text base hasn't been corrupted, and that its checksum
3677      matches the expected base checksum. */
3678
3679   /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3680      check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3681      for replaced nodes because we didn't store the checksum of the "revert
3682      base".  In WC-NG, we do and we can.) */
3683   recorded_base_checksum = fb->original_checksum;
3684
3685   /* If we have a checksum that we want to compare to a MD5 checksum,
3686      ensure that it is a MD5 checksum */
3687   if (recorded_base_checksum
3688       && expected_base_checksum
3689       && recorded_base_checksum->kind != svn_checksum_md5)
3690     SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3691                                         eb->db, eb->wcroot_abspath,
3692                                         recorded_base_checksum, pool, pool));
3693
3694
3695   if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3696       return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3697                      _("Checksum mismatch for '%s':\n"
3698                        "   expected:  %s\n"
3699                        "   recorded:  %s\n"),
3700                      svn_dirent_local_style(fb->local_abspath, pool),
3701                      svn_checksum_to_cstring_display(expected_base_checksum,
3702                                                      pool),
3703                      svn_checksum_to_cstring_display(recorded_base_checksum,
3704                                                      pool));
3705
3706   /* Open the text base for reading, unless this is an added file. */
3707
3708   /*
3709      kff todo: what we really need to do here is:
3710
3711      1. See if there's a file or dir by this name already here.
3712      2. See if it's under revision control.
3713      3. If both are true, open text-base.
3714      4. If only 1 is true, bail, because we can't go destroying user's
3715         files (or as an alternative to bailing, move it to some tmp
3716         name and somehow tell the user, but communicating with the
3717         user without erroring is a whole callback system we haven't
3718         finished inventing yet.)
3719   */
3720
3721   if (! fb->adding_file)
3722     {
3723       SVN_ERR_ASSERT(!fb->original_checksum
3724                      || fb->original_checksum->kind == svn_checksum_sha1);
3725
3726       source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3727                                           handler_pool);
3728     }
3729   else
3730     {
3731       source = svn_stream_empty(handler_pool);
3732     }
3733
3734   /* If we don't have a recorded checksum, use the ra provided checksum */
3735   if (!recorded_base_checksum)
3736     recorded_base_checksum = expected_base_checksum;
3737
3738   /* Checksum the text base while applying deltas */
3739   if (recorded_base_checksum)
3740     {
3741       hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3742                                                       handler_pool);
3743
3744       /* Wrap stream and store reference to allow calculating the
3745          checksum. */
3746       source = svn_stream_checksummed2(source,
3747                                        &hb->actual_source_checksum,
3748                                        NULL, recorded_base_checksum->kind,
3749                                        TRUE, handler_pool);
3750       hb->source_checksum_stream = source;
3751     }
3752
3753   tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3754   tb->hb = hb;
3755   tb->fb = fb;
3756   tb->eb = eb;
3757   target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3758
3759   /* Prepare to apply the delta.  */
3760   svn_txdelta_apply(source, target,
3761                     hb->new_text_base_md5_digest,
3762                     hb->new_text_base_tmp_abspath /* error_info */,
3763                     handler_pool,
3764                     &hb->apply_handler, &hb->apply_baton);
3765
3766   hb->pool = handler_pool;
3767   hb->fb = fb;
3768
3769   /* We're all set.  */
3770   *handler_baton = hb;
3771   *handler = window_handler;
3772
3773   return SVN_NO_ERROR;
3774 }
3775
3776
3777 /* An svn_delta_editor_t function. */
3778 static svn_error_t *
3779 change_file_prop(void *file_baton,
3780                  const char *name,
3781                  const svn_string_t *value,
3782                  apr_pool_t *scratch_pool)
3783 {
3784   struct file_baton *fb = file_baton;
3785   svn_prop_t *propchange;
3786
3787   if (fb->skip_this)
3788     return SVN_NO_ERROR;
3789
3790   /* Push a new propchange to the file baton's array of propchanges */
3791   propchange = apr_array_push(fb->propchanges);
3792   propchange->name = apr_pstrdup(fb->pool, name);
3793   propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3794
3795   if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3796     SVN_ERR(mark_file_edited(fb, scratch_pool));
3797
3798   if (! fb->shadowed
3799       && strcmp(name, SVN_PROP_SPECIAL) == 0)
3800     {
3801       struct edit_baton *eb = fb->edit_baton;
3802       svn_boolean_t modified = FALSE;
3803       svn_boolean_t becomes_symlink;
3804       svn_boolean_t was_symlink;
3805
3806       /* Let's see if we have a change as in some scenarios servers report
3807          non-changes of properties. */
3808       becomes_symlink = (value != NULL);
3809
3810       if (fb->adding_file)
3811         was_symlink = becomes_symlink; /* No change */
3812       else
3813         {
3814           apr_hash_t *props;
3815
3816           /* We read the server-props, not the ACTUAL props here as we just
3817              want to see if this is really an incoming prop change. */
3818           SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3819                                             fb->local_abspath,
3820                                             scratch_pool, scratch_pool));
3821
3822           was_symlink = ((props
3823                               && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3824                               ? svn_tristate_true
3825                               : svn_tristate_false);
3826         }
3827
3828       if (was_symlink != becomes_symlink)
3829         {
3830           /* If the local node was not modified, we continue as usual, if
3831              modified we want a tree conflict just like how we would handle
3832              it when receiving a delete + add (aka "replace") */
3833           if (fb->local_prop_mods)
3834             modified = TRUE;
3835           else
3836             SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3837                                                      fb->local_abspath,
3838                                                      FALSE, scratch_pool));
3839         }
3840
3841       if (modified)
3842         {
3843           if (!fb->edit_conflict)
3844             fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3845
3846           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3847                                      fb->edit_conflict,
3848                                      eb->db, fb->local_abspath,
3849                                      svn_wc_conflict_reason_edited,
3850                                      svn_wc_conflict_action_replace,
3851                                      NULL,
3852                                      fb->pool, scratch_pool));
3853
3854           SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3855                                     fb->local_abspath, fb->old_repos_relpath,
3856                                     fb->old_revision, fb->new_relpath,
3857                                     svn_node_file, svn_node_file,
3858                                     fb->pool, scratch_pool));
3859
3860           /* Create a copy of the existing (pre update) BASE node in WORKING,
3861              mark a tree conflict and handle the rest of the update as
3862              shadowed */
3863           SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3864                                           fb->edit_conflict, NULL,
3865                                           scratch_pool));
3866
3867           do_notification(eb, fb->local_abspath, svn_node_file,
3868                           svn_wc_notify_tree_conflict, scratch_pool);
3869
3870           /* Ok, we introduced a replacement, so we can now handle the rest
3871              as a normal shadowed update */
3872           fb->shadowed = TRUE;
3873           fb->add_existed = FALSE;
3874           fb->already_notified = TRUE;
3875       }
3876     }
3877
3878   return SVN_NO_ERROR;
3879 }
3880
3881 /* Perform the actual merge of file changes between an original file,
3882    identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3883    identified by NEW_CHECKSUM.
3884
3885    Merge the result into LOCAL_ABSPATH, which is part of the working copy
3886    identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3887    the intermediate files.
3888
3889    The rest of the arguments are passed to svn_wc__internal_merge().
3890  */
3891 svn_error_t *
3892 svn_wc__perform_file_merge(svn_skel_t **work_items,
3893                            svn_skel_t **conflict_skel,
3894                            svn_boolean_t *found_conflict,
3895                            svn_wc__db_t *db,
3896                            const char *local_abspath,
3897                            const char *wri_abspath,
3898                            const svn_checksum_t *new_checksum,
3899                            const svn_checksum_t *original_checksum,
3900                            apr_hash_t *old_actual_props,
3901                            const apr_array_header_t *ext_patterns,
3902                            svn_revnum_t old_revision,
3903                            svn_revnum_t target_revision,
3904                            const apr_array_header_t *propchanges,
3905                            const char *diff3_cmd,
3906                            svn_cancel_func_t cancel_func,
3907                            void *cancel_baton,
3908                            apr_pool_t *result_pool,
3909                            apr_pool_t *scratch_pool)
3910 {
3911   /* Actual file exists and has local mods:
3912      Now we need to let loose svn_wc__internal_merge() to merge
3913      the textual changes into the working file. */
3914   const char *oldrev_str, *newrev_str, *mine_str;
3915   const char *merge_left;
3916   svn_boolean_t delete_left = FALSE;
3917   const char *path_ext = "";
3918   const char *new_text_base_tmp_abspath;
3919   enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3920   svn_skel_t *work_item;
3921
3922   *work_items = NULL;
3923
3924   SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3925                                        db, wri_abspath, new_checksum,
3926                                        scratch_pool, scratch_pool));
3927
3928   /* If we have any file extensions we're supposed to
3929      preserve in generated conflict file names, then find
3930      this path's extension.  But then, if it isn't one of
3931      the ones we want to keep in conflict filenames,
3932      pretend it doesn't have an extension at all. */
3933   if (ext_patterns && ext_patterns->nelts)
3934     {
3935       svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3936       if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3937         path_ext = "";
3938     }
3939
3940   /* old_revision can be invalid when the conflict is against a
3941      local addition */
3942   if (!SVN_IS_VALID_REVNUM(old_revision))
3943     old_revision = 0;
3944
3945   oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3946                             old_revision,
3947                             *path_ext ? "." : "",
3948                             *path_ext ? path_ext : "");
3949
3950   newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3951                             target_revision,
3952                             *path_ext ? "." : "",
3953                             *path_ext ? path_ext : "");
3954   mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3955                           *path_ext ? "." : "",
3956                           *path_ext ? path_ext : "");
3957
3958   if (! original_checksum)
3959     {
3960       SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3961                                  result_pool, scratch_pool));
3962       delete_left = TRUE;
3963     }
3964   else
3965     SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3966                                          original_checksum,
3967                                          result_pool, scratch_pool));
3968
3969   /* Merge the changes from the old textbase to the new
3970      textbase into the file we're updating.
3971      Remember that this function wants full paths! */
3972   SVN_ERR(svn_wc__internal_merge(&work_item,
3973                                  conflict_skel,
3974                                  &merge_outcome,
3975                                  db,
3976                                  merge_left,
3977                                  new_text_base_tmp_abspath,
3978                                  local_abspath,
3979                                  wri_abspath,
3980                                  oldrev_str, newrev_str, mine_str,
3981                                  old_actual_props,
3982                                  FALSE /* dry_run */,
3983                                  diff3_cmd, NULL, propchanges,
3984                                  cancel_func, cancel_baton,
3985                                  result_pool, scratch_pool));
3986
3987   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3988   *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3989
3990   /* If we created a temporary left merge file, get rid of it. */
3991   if (delete_left)
3992     {
3993       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3994                                            merge_left,
3995                                            result_pool, scratch_pool));
3996       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3997     }
3998
3999   return SVN_NO_ERROR;
4000 }
4001
4002 /* This is the small planet.  It has the complex responsibility of
4003  * "integrating" a new revision of a file into a working copy.
4004  *
4005  * Given a file_baton FB for a file either already under version control, or
4006  * prepared (see below) to join version control, fully install a
4007  * new revision of the file.
4008  *
4009  * ### transitional: installation of the working file will be handled
4010  * ### by the *INSTALL_PRISTINE flag.
4011  *
4012  * By "install", we mean: create a new text-base and prop-base, merge
4013  * any textual and property changes into the working file, and finally
4014  * update all metadata so that the working copy believes it has a new
4015  * working revision of the file.  All of this work includes being
4016  * sensitive to eol translation, keyword substitution, and performing
4017  * all actions accumulated the parent directory's work queue.
4018  *
4019  * Set *CONTENT_STATE to the state of the contents after the
4020  * installation.
4021  *
4022  * Return values are allocated in RESULT_POOL and temporary allocations
4023  * are performed in SCRATCH_POOL.
4024  */
4025 static svn_error_t *
4026 merge_file(svn_skel_t **work_items,
4027            svn_skel_t **conflict_skel,
4028            svn_boolean_t *install_pristine,
4029            const char **install_from,
4030            svn_wc_notify_state_t *content_state,
4031            struct file_baton *fb,
4032            apr_hash_t *actual_props,
4033            apr_time_t last_changed_date,
4034            apr_pool_t *result_pool,
4035            apr_pool_t *scratch_pool)
4036 {
4037   struct edit_baton *eb = fb->edit_baton;
4038   struct dir_baton *pb = fb->dir_baton;
4039   svn_boolean_t is_locally_modified;
4040   svn_boolean_t found_text_conflict = FALSE;
4041
4042   SVN_ERR_ASSERT(! fb->shadowed
4043                  && ! fb->obstruction_found
4044                  && ! fb->edit_obstructed);
4045
4046   /*
4047      When this function is called on file F, we assume the following
4048      things are true:
4049
4050          - The new pristine text of F is present in the pristine store
4051            iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4052
4053          - The WC metadata still reflects the old version of F.
4054            (We can still access the old pristine base text of F.)
4055
4056      The goal is to update the local working copy of F to reflect
4057      the changes received from the repository, preserving any local
4058      modifications.
4059   */
4060
4061   *work_items = NULL;
4062   *install_pristine = FALSE;
4063   *install_from = NULL;
4064
4065   /* Start by splitting the file path, getting an access baton for the parent,
4066      and an entry for the file if any. */
4067
4068   /* Has the user made local mods to the working file?
4069      Note that this compares to the current pristine file, which is
4070      different from fb->old_text_base_path if we have a replaced-with-history
4071      file.  However, in the case we had an obstruction, we check against the
4072      new text base.
4073    */
4074   if (fb->adding_file && !fb->add_existed)
4075     {
4076       is_locally_modified = FALSE; /* There is no file: Don't check */
4077     }
4078   else
4079     {
4080       /* The working file is not an obstruction.
4081          So: is the file modified, relative to its ORIGINAL pristine?
4082
4083          This function sets is_locally_modified to FALSE for
4084          files that do not exist and for directories. */
4085
4086       SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4087                                                eb->db, fb->local_abspath,
4088                                                FALSE /* exact_comparison */,
4089                                                scratch_pool));
4090     }
4091
4092   /* For 'textual' merging, we use the following system:
4093
4094      When a file is modified and we have a new BASE:
4095       - For text files
4096           * svn_wc_merge uses diff3
4097           * possibly makes backups and marks files as conflicted.
4098
4099       - For binary files
4100           * svn_wc_merge makes backups and marks files as conflicted.
4101
4102      If a file is not modified and we have a new BASE:
4103        * Install from pristine.
4104
4105      If we have property changes related to magic properties or if the
4106      svn:keywords property is set:
4107        * Retranslate from the working file.
4108    */
4109   if (! is_locally_modified
4110       && fb->new_text_base_sha1_checksum)
4111     {
4112           /* If there are no local mods, who cares whether it's a text
4113              or binary file!  Just write a log command to overwrite
4114              any working file with the new text-base.  If newline
4115              conversion or keyword substitution is activated, this
4116              will happen as well during the copy.
4117              For replaced files, though, we want to merge in the changes
4118              even if the file is not modified compared to the (non-revert)
4119              text-base. */
4120
4121       *install_pristine = TRUE;
4122     }
4123   else if (fb->new_text_base_sha1_checksum)
4124     {
4125       /* Actual file exists and has local mods:
4126          Now we need to let loose svn_wc__merge_internal() to merge
4127          the textual changes into the working file. */
4128       SVN_ERR(svn_wc__perform_file_merge(work_items,
4129                                          conflict_skel,
4130                                          &found_text_conflict,
4131                                          eb->db,
4132                                          fb->local_abspath,
4133                                          pb->local_abspath,
4134                                          fb->new_text_base_sha1_checksum,
4135                                          fb->add_existed
4136                                                   ? NULL
4137                                                   : fb->original_checksum,
4138                                          actual_props,
4139                                          eb->ext_patterns,
4140                                          fb->old_revision,
4141                                          *eb->target_revision,
4142                                          fb->propchanges,
4143                                          eb->diff3_cmd,
4144                                          eb->cancel_func, eb->cancel_baton,
4145                                          result_pool, scratch_pool));
4146     } /* end: working file exists and has mods */
4147   else
4148     {
4149       /* There is no new text base, but let's see if the working file needs
4150          to be updated for any other reason. */
4151
4152       apr_hash_t *keywords;
4153
4154       /* Determine if any of the propchanges are the "magic" ones that
4155          might require changing the working file. */
4156       svn_boolean_t magic_props_changed;
4157
4158       magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4159
4160       SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4161                                          &keywords,
4162                                          NULL,
4163                                          eb->db, fb->local_abspath,
4164                                          actual_props, TRUE,
4165                                          scratch_pool, scratch_pool));
4166       if (magic_props_changed || keywords)
4167         {
4168           /* Special edge-case: it's possible that this file installation
4169              only involves propchanges, but that some of those props still
4170              require a retranslation of the working file.
4171
4172              OR that the file doesn't involve propchanges which by themselves
4173              require retranslation, but receiving a change bumps the revision
4174              number which requires re-expansion of keywords... */
4175
4176           if (is_locally_modified)
4177             {
4178               const char *tmptext;
4179
4180               /* Copy and DEtranslate the working file to a temp text-base.
4181                  Note that detranslation is done according to the old props. */
4182               SVN_ERR(svn_wc__internal_translated_file(
4183                         &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4184                         SVN_WC_TRANSLATE_TO_NF
4185                           | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4186                         eb->cancel_func, eb->cancel_baton,
4187                         result_pool, scratch_pool));
4188
4189               /* We always want to reinstall the working file if the magic
4190                  properties have changed, or there are any keywords present.
4191                  Note that TMPTEXT might actually refer to the working file
4192                  itself (the above function skips a detranslate when not
4193                  required). This is acceptable, as we will (re)translate
4194                  according to the new properties into a temporary file (from
4195                  the working file), and then rename the temp into place. Magic!
4196                */
4197               *install_pristine = TRUE;
4198               *install_from = tmptext;
4199             }
4200           else
4201             {
4202               /* Use our existing 'copy' from the pristine store instead
4203                  of making a new copy. This way we can use the standard code
4204                  to update the recorded size and modification time.
4205                  (Issue #3842) */
4206               *install_pristine = TRUE;
4207             }
4208         }
4209     }
4210
4211   /* Set the returned content state. */
4212
4213   if (found_text_conflict)
4214     *content_state = svn_wc_notify_state_conflicted;
4215   else if (fb->new_text_base_sha1_checksum)
4216     {
4217       if (is_locally_modified)
4218         *content_state = svn_wc_notify_state_merged;
4219       else
4220         *content_state = svn_wc_notify_state_changed;
4221     }
4222   else
4223     *content_state = svn_wc_notify_state_unchanged;
4224
4225   return SVN_NO_ERROR;
4226 }
4227
4228
4229 /* An svn_delta_editor_t function. */
4230 /* Mostly a wrapper around merge_file. */
4231 static svn_error_t *
4232 close_file(void *file_baton,
4233            const char *expected_md5_digest,
4234            apr_pool_t *pool)
4235 {
4236   struct file_baton *fb = file_baton;
4237   struct dir_baton *pdb = fb->dir_baton;
4238   struct edit_baton *eb = fb->edit_baton;
4239   svn_wc_notify_state_t content_state, prop_state;
4240   svn_wc_notify_lock_state_t lock_state;
4241   svn_checksum_t *expected_md5_checksum = NULL;
4242   apr_hash_t *new_base_props = NULL;
4243   apr_hash_t *new_actual_props = NULL;
4244   apr_array_header_t *entry_prop_changes;
4245   apr_array_header_t *dav_prop_changes;
4246   apr_array_header_t *regular_prop_changes;
4247   apr_hash_t *current_base_props = NULL;
4248   apr_hash_t *current_actual_props = NULL;
4249   apr_hash_t *local_actual_props = NULL;
4250   svn_skel_t *all_work_items = NULL;
4251   svn_skel_t *conflict_skel = NULL;
4252   svn_skel_t *work_item;
4253   apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4254   svn_boolean_t keep_recorded_info = FALSE;
4255   const svn_checksum_t *new_checksum;
4256   apr_array_header_t *iprops = NULL;
4257
4258   if (fb->skip_this)
4259     {
4260       svn_pool_destroy(fb->pool);
4261       SVN_ERR(maybe_release_dir_info(pdb));
4262       return SVN_NO_ERROR;
4263     }
4264
4265   if (fb->edited)
4266     conflict_skel = fb->edit_conflict;
4267
4268   if (expected_md5_digest)
4269     SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4270                                    expected_md5_digest, scratch_pool));
4271
4272   if (fb->new_text_base_md5_checksum && expected_md5_checksum
4273       && !svn_checksum_match(expected_md5_checksum,
4274                              fb->new_text_base_md5_checksum))
4275     return svn_error_trace(
4276                 svn_checksum_mismatch_err(expected_md5_checksum,
4277                                           fb->new_text_base_md5_checksum,
4278                                           scratch_pool,
4279                                           _("Checksum mismatch for '%s'"),
4280                                           svn_dirent_local_style(
4281                                                 fb->local_abspath, pool)));
4282
4283   /* Gather the changes for each kind of property.  */
4284   SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4285                                &dav_prop_changes, &regular_prop_changes,
4286                                scratch_pool));
4287
4288   /* Extract the changed_* and lock state information.  */
4289   {
4290     svn_revnum_t new_changed_rev;
4291     apr_time_t new_changed_date;
4292     const char *new_changed_author;
4293
4294     SVN_ERR(accumulate_last_change(&new_changed_rev,
4295                                    &new_changed_date,
4296                                    &new_changed_author,
4297                                    entry_prop_changes,
4298                                    scratch_pool, scratch_pool));
4299
4300     if (SVN_IS_VALID_REVNUM(new_changed_rev))
4301       fb->changed_rev = new_changed_rev;
4302     if (new_changed_date != 0)
4303       fb->changed_date = new_changed_date;
4304     if (new_changed_author != NULL)
4305       fb->changed_author = new_changed_author;
4306   }
4307
4308   /* Determine whether the file has become unlocked.  */
4309   {
4310     int i;
4311
4312     lock_state = svn_wc_notify_lock_state_unchanged;
4313
4314     for (i = 0; i < entry_prop_changes->nelts; ++i)
4315       {
4316         const svn_prop_t *prop
4317           = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4318
4319         /* If we see a change to the LOCK_TOKEN entry prop, then the only
4320            possible change is its REMOVAL. Thus, the lock has been removed,
4321            and we should likewise remove our cached copy of it.  */
4322         if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4323           {
4324             /* If we lose the lock, but not because we are switching to
4325                another url, remove the state lock from the wc */
4326             if (! eb->switch_relpath
4327                 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4328               {
4329                 SVN_ERR_ASSERT(prop->value == NULL);
4330                 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4331                                                scratch_pool));
4332
4333                 lock_state = svn_wc_notify_lock_state_unlocked;
4334               }
4335             break;
4336           }
4337       }
4338   }
4339
4340   /* Install all kinds of properties.  It is important to do this before
4341      any file content merging, since that process might expand keywords, in
4342      which case we want the new entryprops to be in place. */
4343
4344   /* Write log commands to merge REGULAR_PROPS into the existing
4345      properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4346      the result of the regular prop merge.
4347
4348      BASE_PROPS and WORKING_PROPS are hashes of the base and
4349      working props of the file; if NULL they are read from the wc.  */
4350
4351   /* ### some of this feels like voodoo... */
4352
4353   if ((!fb->adding_file || fb->add_existed)
4354       && !fb->shadowed)
4355     SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4356                                      eb->db, fb->local_abspath,
4357                                      scratch_pool, scratch_pool));
4358   if (local_actual_props == NULL)
4359     local_actual_props = apr_hash_make(scratch_pool);
4360
4361   if (fb->add_existed)
4362     {
4363       /* This node already exists. Grab the current pristine properties. */
4364       SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4365                                              eb->db, fb->local_abspath,
4366                                              scratch_pool, scratch_pool));
4367       current_actual_props = local_actual_props;
4368     }
4369   else if (!fb->adding_file)
4370     {
4371       /* Get the BASE properties for proper merging. */
4372       SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4373                                         eb->db, fb->local_abspath,
4374                                         scratch_pool, scratch_pool));
4375       current_actual_props = local_actual_props;
4376     }
4377
4378   /* Note: even if the node existed before, it may not have
4379      pristine props (e.g a local-add)  */
4380   if (current_base_props == NULL)
4381     current_base_props = apr_hash_make(scratch_pool);
4382
4383   /* And new nodes need an empty set of ACTUAL props.  */
4384   if (current_actual_props == NULL)
4385     current_actual_props = apr_hash_make(scratch_pool);
4386
4387   prop_state = svn_wc_notify_state_unknown;
4388
4389   if (! fb->shadowed)
4390     {
4391       svn_boolean_t install_pristine;
4392       const char *install_from = NULL;
4393
4394       /* Merge the 'regular' props into the existing working proplist. */
4395       /* This will merge the old and new props into a new prop db, and
4396          write <cp> commands to the logfile to install the merged
4397          props.  */
4398       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4399                                        scratch_pool);
4400       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4401                                   &prop_state,
4402                                   &new_actual_props,
4403                                   eb->db,
4404                                   fb->local_abspath,
4405                                   NULL /* server_baseprops (update, not merge)  */,
4406                                   current_base_props,
4407                                   current_actual_props,
4408                                   regular_prop_changes, /* propchanges */
4409                                   scratch_pool,
4410                                   scratch_pool));
4411       /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4412       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4413
4414       /* Merge the text. This will queue some additional work.  */
4415       if (!fb->obstruction_found && !fb->edit_obstructed)
4416         {
4417           svn_error_t *err;
4418           err = merge_file(&work_item, &conflict_skel,
4419                            &install_pristine, &install_from,
4420                            &content_state, fb, current_actual_props,
4421                            fb->changed_date, scratch_pool, scratch_pool);
4422
4423           if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4424             {
4425               if (eb->notify_func)
4426                 {
4427                   svn_wc_notify_t *notify =svn_wc_create_notify(
4428                                 fb->local_abspath,
4429                                 svn_wc_notify_update_skip_access_denied,
4430                                 scratch_pool);
4431
4432                   notify->kind = svn_node_file;
4433                   notify->err = err;
4434
4435                   eb->notify_func(eb->notify_baton, notify, scratch_pool);
4436                 }
4437               svn_error_clear(err);
4438
4439               SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4440                                             scratch_pool));
4441               fb->skip_this = TRUE;
4442
4443               svn_pool_destroy(fb->pool);
4444               SVN_ERR(maybe_release_dir_info(pdb));
4445               return SVN_NO_ERROR;
4446             }
4447           else
4448             SVN_ERR(err);
4449
4450           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4451                                             scratch_pool);
4452         }
4453       else
4454         {
4455           install_pristine = FALSE;
4456           if (fb->new_text_base_sha1_checksum)
4457             content_state = svn_wc_notify_state_changed;
4458           else
4459             content_state = svn_wc_notify_state_unchanged;
4460         }
4461
4462       if (install_pristine)
4463         {
4464           svn_boolean_t record_fileinfo;
4465
4466           /* If we are installing from the pristine contents, then go ahead and
4467              record the fileinfo. That will be the "proper" values. Installing
4468              from some random file means the fileinfo does NOT correspond to
4469              the pristine (in which case, the fileinfo will be cleared for
4470              safety's sake).  */
4471           record_fileinfo = (install_from == NULL);
4472
4473           SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4474                                                 eb->db,
4475                                                 fb->local_abspath,
4476                                                 install_from,
4477                                                 eb->use_commit_times,
4478                                                 record_fileinfo,
4479                                                 scratch_pool, scratch_pool));
4480           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4481                                             scratch_pool);
4482         }
4483       else if (lock_state == svn_wc_notify_lock_state_unlocked
4484                && !fb->obstruction_found)
4485         {
4486           /* If a lock was removed and we didn't update the text contents, we
4487              might need to set the file read-only.
4488
4489              Note: this will also update the executable flag, but ... meh.  */
4490           SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4491                                                    fb->local_abspath,
4492                                                    scratch_pool, scratch_pool));
4493           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4494                                             scratch_pool);
4495         }
4496
4497       if (! install_pristine
4498           && (content_state == svn_wc_notify_state_unchanged))
4499         {
4500           /* It is safe to keep the current recorded timestamp and size */
4501           keep_recorded_info = TRUE;
4502         }
4503
4504       /* Clean up any temporary files.  */
4505
4506       /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4507          working file.  */
4508       if (install_from != NULL
4509           && strcmp(install_from, fb->local_abspath) != 0)
4510         {
4511           SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4512                                                fb->local_abspath, install_from,
4513                                                scratch_pool, scratch_pool));
4514           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4515                                             scratch_pool);
4516         }
4517     }
4518   else
4519     {
4520       /* Adding or updating a BASE node under a locally added node. */
4521       apr_hash_t *fake_actual_props;
4522
4523       if (fb->adding_file)
4524         fake_actual_props = apr_hash_make(scratch_pool);
4525       else
4526         fake_actual_props = current_base_props;
4527
4528       /* Store the incoming props (sent as propchanges) in new_base_props
4529          and create a set of new actual props to use for notifications */
4530       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4531                                        scratch_pool);
4532       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4533                                   &prop_state,
4534                                   &new_actual_props,
4535                                   eb->db,
4536                                   fb->local_abspath,
4537                                   NULL /* server_baseprops (not merging) */,
4538                                   current_base_props /* pristine_props */,
4539                                   fake_actual_props /* actual_props */,
4540                                   regular_prop_changes, /* propchanges */
4541                                   scratch_pool,
4542                                   scratch_pool));
4543
4544       if (fb->new_text_base_sha1_checksum)
4545         content_state = svn_wc_notify_state_changed;
4546       else
4547         content_state = svn_wc_notify_state_unchanged;
4548     }
4549
4550   /* Insert/replace the BASE node with all of the new metadata.  */
4551
4552   /* Set the 'checksum' column of the file's BASE_NODE row to
4553    * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4554    * checksum is already in the pristine store. */
4555   new_checksum = fb->new_text_base_sha1_checksum;
4556
4557   /* If we don't have a NEW checksum, then the base must not have changed.
4558      Just carry over the old checksum.  */
4559   if (new_checksum == NULL)
4560     new_checksum = fb->original_checksum;
4561
4562   if (conflict_skel)
4563     {
4564       SVN_ERR(complete_conflict(conflict_skel,
4565                                 fb->edit_baton,
4566                                 fb->local_abspath,
4567                                 fb->old_repos_relpath,
4568                                 fb->old_revision,
4569                                 fb->new_relpath,
4570                                 svn_node_file, svn_node_file,
4571                                 fb->pool, scratch_pool));
4572
4573       SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4574                                               eb->db, fb->local_abspath,
4575                                               conflict_skel,
4576                                               scratch_pool, scratch_pool));
4577
4578       all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4579                                         scratch_pool);
4580     }
4581
4582   /* Any inherited props to be set set for this base node? */
4583   if (eb->wcroot_iprops)
4584     {
4585       iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4586
4587       /* close_edit may also update iprops for switched nodes, catching
4588          those for which close_directory is never called (e.g. a switch
4589          with no changes).  So as a minor optimization we remove any
4590          iprops from the hash so as not to set them again in
4591          close_edit. */
4592       if (iprops)
4593         svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4594     }
4595
4596   SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4597                                    eb->wcroot_abspath,
4598                                    fb->new_relpath,
4599                                    eb->repos_root, eb->repos_uuid,
4600                                    *eb->target_revision,
4601                                    new_base_props,
4602                                    fb->changed_rev,
4603                                    fb->changed_date,
4604                                    fb->changed_author,
4605                                    new_checksum,
4606                                    (dav_prop_changes->nelts > 0)
4607                                      ? svn_prop_array_to_hash(
4608                                                       dav_prop_changes,
4609                                                       scratch_pool)
4610                                      : NULL,
4611                                    (fb->add_existed && fb->adding_file),
4612                                    (! fb->shadowed) && new_base_props,
4613                                    new_actual_props,
4614                                    iprops,
4615                                    keep_recorded_info,
4616                                    (fb->shadowed && fb->obstruction_found),
4617                                    conflict_skel,
4618                                    all_work_items,
4619                                    scratch_pool));
4620
4621   if (conflict_skel && eb->conflict_func)
4622     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4623                                              conflict_skel,
4624                                              NULL /* merge_options */,
4625                                              eb->conflict_func,
4626                                              eb->conflict_baton,
4627                                              eb->cancel_func,
4628                                              eb->cancel_baton,
4629                                              scratch_pool));
4630
4631   /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4632
4633   svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4634
4635   /* Send a notification to the callback function.  (Skip notifications
4636      about files which were already notified for another reason.) */
4637   if (eb->notify_func && !fb->already_notified
4638       && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4639     {
4640       svn_wc_notify_t *notify;
4641       svn_wc_notify_action_t action = svn_wc_notify_update_update;
4642
4643       if (fb->edited)
4644         {
4645           if (fb->shadowed || fb->edit_obstructed)
4646             action = fb->adding_file
4647                             ? svn_wc_notify_update_shadowed_add
4648                             : svn_wc_notify_update_shadowed_update;
4649           else if (fb->obstruction_found || fb->add_existed)
4650             {
4651               if (content_state != svn_wc_notify_state_conflicted)
4652                 action = svn_wc_notify_exists;
4653             }
4654           else if (fb->adding_file)
4655             {
4656               action = svn_wc_notify_update_add;
4657             }
4658         }
4659       else
4660         {
4661           SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4662           action = svn_wc_notify_update_broken_lock;
4663         }
4664
4665       /* If the file was moved-away, notify for the moved-away node.
4666        * The original location only had its BASE info changed and
4667        * we don't usually notify about such changes. */
4668       notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4669       notify->kind = svn_node_file;
4670       notify->content_state = content_state;
4671       notify->prop_state = prop_state;
4672       notify->lock_state = lock_state;
4673       notify->revision = *eb->target_revision;
4674       notify->old_revision = fb->old_revision;
4675
4676       /* Fetch the mimetype from the actual properties */
4677       notify->mime_type = svn_prop_get_value(new_actual_props,
4678                                              SVN_PROP_MIME_TYPE);
4679
4680       eb->notify_func(eb->notify_baton, notify, scratch_pool);
4681     }
4682
4683   svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4684
4685   /* We have one less referrer to the directory */
4686   SVN_ERR(maybe_release_dir_info(pdb));
4687
4688   return SVN_NO_ERROR;
4689 }
4690
4691
4692 /* An svn_delta_editor_t function. */
4693 static svn_error_t *
4694 close_edit(void *edit_baton,
4695            apr_pool_t *pool)
4696 {
4697   struct edit_baton *eb = edit_baton;
4698   apr_pool_t *scratch_pool = eb->pool;
4699
4700   /* The editor didn't even open the root; we have to take care of
4701      some cleanup stuffs. */
4702   if (! eb->root_opened
4703       && *eb->target_basename == '\0')
4704     {
4705       /* We need to "un-incomplete" the root directory. */
4706       SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4707                                                       eb->anchor_abspath,
4708                                                       scratch_pool));
4709     }
4710
4711   /* By definition, anybody "driving" this editor for update or switch
4712      purposes at a *minimum* must have called set_target_revision() at
4713      the outset, and close_edit() at the end -- even if it turned out
4714      that no changes ever had to be made, and open_root() was never
4715      called.  That's fine.  But regardless, when the edit is over,
4716      this editor needs to make sure that *all* paths have had their
4717      revisions bumped to the new target revision. */
4718
4719   /* Make sure our update target now has the new working revision.
4720      Also, if this was an 'svn switch', then rewrite the target's
4721      url.  All of this tweaking might happen recursively!  Note
4722      that if eb->target is NULL, that's okay (albeit "sneaky",
4723      some might say).  */
4724
4725   /* Extra check: if the update did nothing but make its target
4726      'deleted', then do *not* run cleanup on the target, as it
4727      will only remove the deleted entry!  */
4728   if (! eb->target_deleted)
4729     {
4730       SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4731                                                        eb->target_abspath,
4732                                                        eb->requested_depth,
4733                                                        eb->switch_relpath,
4734                                                        eb->repos_root,
4735                                                        eb->repos_uuid,
4736                                                        *(eb->target_revision),
4737                                                        eb->skipped_trees,
4738                                                        eb->wcroot_iprops,
4739                                                        eb->notify_func,
4740                                                        eb->notify_baton,
4741                                                        eb->pool));
4742
4743       if (*eb->target_basename != '\0')
4744         {
4745           svn_wc__db_status_t status;
4746           svn_error_t *err;
4747
4748           /* Note: we are fetching information about the *target*, not anchor.
4749              There is no guarantee that the target has a BASE node.
4750              For example:
4751
4752                The node was not present in BASE, but locally-added, and the
4753                update did not create a new BASE node "under" the local-add.
4754
4755              If there is no BASE node for the target, then we certainly don't
4756              have to worry about removing it. */
4757           err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4758                                          NULL, NULL, NULL, NULL, NULL, NULL,
4759                                          NULL, NULL, NULL, NULL,
4760                                          eb->db, eb->target_abspath,
4761                                          scratch_pool, scratch_pool);
4762           if (err)
4763             {
4764               if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4765                 return svn_error_trace(err);
4766
4767               svn_error_clear(err);
4768             }
4769           else if (status == svn_wc__db_status_excluded)
4770             {
4771               /* There is a small chance that the explicit target of an update/
4772                  switch is gone in the repository, in that specific case the
4773                  node hasn't been re-added to the BASE tree by this update.
4774
4775                  If so, we should get rid of this excluded node now. */
4776
4777               SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4778                                              FALSE /* keep_as_working */,
4779                                              FALSE /* queue_deletes */,
4780                                              FALSE /* remove_locks */,
4781                                              SVN_INVALID_REVNUM,
4782                                              NULL, NULL, scratch_pool));
4783             }
4784         }
4785     }
4786
4787   /* The edit is over: run the wq with proper cancel support,
4788      but first kill the handler that would run it on the pool
4789      cleanup at the end of this function. */
4790   apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4791
4792   SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4793                          eb->cancel_func, eb->cancel_baton,
4794                          eb->pool));
4795
4796   /* The edit is over, free its pool.
4797      ### No, this is wrong.  Who says this editor/baton won't be used
4798      again?  But the change is not merely to remove this call.  We
4799      should also make eb->pool not be a subpool (see make_editor),
4800      and change callers of svn_client_{checkout,update,switch} to do
4801      better pool management. ### */
4802
4803   svn_pool_destroy(eb->pool);
4804
4805   return SVN_NO_ERROR;
4806 }
4807
4808 \f
4809 /*** Returning editors. ***/
4810
4811 /* Helper for the three public editor-supplying functions. */
4812 static svn_error_t *
4813 make_editor(svn_revnum_t *target_revision,
4814             svn_wc__db_t *db,
4815             const char *anchor_abspath,
4816             const char *target_basename,
4817             apr_hash_t *wcroot_iprops,
4818             svn_boolean_t use_commit_times,
4819             const char *switch_url,
4820             svn_depth_t depth,
4821             svn_boolean_t depth_is_sticky,
4822             svn_boolean_t allow_unver_obstructions,
4823             svn_boolean_t adds_as_modification,
4824             svn_boolean_t server_performs_filtering,
4825             svn_boolean_t clean_checkout,
4826             svn_wc_notify_func2_t notify_func,
4827             void *notify_baton,
4828             svn_cancel_func_t cancel_func,
4829             void *cancel_baton,
4830             svn_wc_dirents_func_t fetch_dirents_func,
4831             void *fetch_dirents_baton,
4832             svn_wc_conflict_resolver_func2_t conflict_func,
4833             void *conflict_baton,
4834             svn_wc_external_update_t external_func,
4835             void *external_baton,
4836             const char *diff3_cmd,
4837             const apr_array_header_t *preserved_exts,
4838             const svn_delta_editor_t **editor,
4839             void **edit_baton,
4840             apr_pool_t *result_pool,
4841             apr_pool_t *scratch_pool)
4842 {
4843   struct edit_baton *eb;
4844   void *inner_baton;
4845   apr_pool_t *edit_pool = svn_pool_create(result_pool);
4846   svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4847   const svn_delta_editor_t *inner_editor;
4848   const char *repos_root, *repos_uuid;
4849   struct svn_wc__shim_fetch_baton_t *sfb;
4850   svn_delta_shim_callbacks_t *shim_callbacks =
4851                                 svn_delta_shim_callbacks_default(edit_pool);
4852
4853   /* An unknown depth can't be sticky. */
4854   if (depth == svn_depth_unknown)
4855     depth_is_sticky = FALSE;
4856
4857   /* Get the anchor's repository root and uuid. The anchor must already exist
4858      in BASE. */
4859   SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4860                                      db, anchor_abspath,
4861                                      result_pool, scratch_pool));
4862
4863   /* With WC-NG we need a valid repository root */
4864   SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4865
4866   /* Disallow a switch operation to change the repository root of the target,
4867      if that is known. */
4868   if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4869     return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4870                              _("'%s'\nis not the same repository as\n'%s'"),
4871                              switch_url, repos_root);
4872
4873   /* Construct an edit baton. */
4874   eb = apr_pcalloc(edit_pool, sizeof(*eb));
4875   eb->pool                     = edit_pool;
4876   eb->use_commit_times         = use_commit_times;
4877   eb->target_revision          = target_revision;
4878   eb->repos_root               = repos_root;
4879   eb->repos_uuid               = repos_uuid;
4880   eb->db                       = db;
4881   eb->target_basename          = target_basename;
4882   eb->anchor_abspath           = anchor_abspath;
4883   eb->wcroot_iprops            = wcroot_iprops;
4884
4885   SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4886                                 edit_pool, scratch_pool));
4887
4888   if (switch_url)
4889     eb->switch_relpath =
4890       svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4891   else
4892     eb->switch_relpath = NULL;
4893
4894   if (svn_path_is_empty(target_basename))
4895     eb->target_abspath = eb->anchor_abspath;
4896   else
4897     eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4898                                          edit_pool);
4899
4900   eb->requested_depth          = depth;
4901   eb->depth_is_sticky          = depth_is_sticky;
4902   eb->notify_func              = notify_func;
4903   eb->notify_baton             = notify_baton;
4904   eb->external_func            = external_func;
4905   eb->external_baton           = external_baton;
4906   eb->diff3_cmd                = diff3_cmd;
4907   eb->cancel_func              = cancel_func;
4908   eb->cancel_baton             = cancel_baton;
4909   eb->conflict_func            = conflict_func;
4910   eb->conflict_baton           = conflict_baton;
4911   eb->allow_unver_obstructions = allow_unver_obstructions;
4912   eb->adds_as_modification     = adds_as_modification;
4913   eb->clean_checkout           = clean_checkout;
4914   eb->skipped_trees            = apr_hash_make(edit_pool);
4915   eb->dir_dirents              = apr_hash_make(edit_pool);
4916   eb->ext_patterns             = preserved_exts;
4917
4918   apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4919                             apr_pool_cleanup_null);
4920
4921   /* Construct an editor. */
4922   tree_editor->set_target_revision = set_target_revision;
4923   tree_editor->open_root = open_root;
4924   tree_editor->delete_entry = delete_entry;
4925   tree_editor->add_directory = add_directory;
4926   tree_editor->open_directory = open_directory;
4927   tree_editor->change_dir_prop = change_dir_prop;
4928   tree_editor->close_directory = close_directory;
4929   tree_editor->absent_directory = absent_directory;
4930   tree_editor->add_file = add_file;
4931   tree_editor->open_file = open_file;
4932   tree_editor->apply_textdelta = apply_textdelta;
4933   tree_editor->change_file_prop = change_file_prop;
4934   tree_editor->close_file = close_file;
4935   tree_editor->absent_file = absent_file;
4936   tree_editor->close_edit = close_edit;
4937
4938   /* Fiddle with the type system. */
4939   inner_editor = tree_editor;
4940   inner_baton = eb;
4941
4942   if (!depth_is_sticky
4943       && depth != svn_depth_unknown
4944       && svn_depth_empty <= depth && depth < svn_depth_infinity
4945       && fetch_dirents_func)
4946     {
4947       /* We are asked to perform an update at a depth less than the ambient
4948          depth. In this case the update won't describe additions that would
4949          have been reported if we updated at the ambient depth. */
4950       svn_error_t *err;
4951       svn_node_kind_t dir_kind;
4952       svn_wc__db_status_t dir_status;
4953       const char *dir_repos_relpath;
4954       svn_depth_t dir_depth;
4955
4956       /* we have to do this on the target of the update, not the anchor */
4957       err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4958                                      &dir_repos_relpath, NULL, NULL, NULL,
4959                                      NULL, NULL, &dir_depth, NULL, NULL, NULL,
4960                                      NULL, NULL, NULL,
4961                                      db, eb->target_abspath,
4962                                      scratch_pool, scratch_pool);
4963
4964       if (!err
4965           && dir_kind == svn_node_dir
4966           && dir_status == svn_wc__db_status_normal)
4967         {
4968           if (dir_depth > depth)
4969             {
4970               apr_hash_t *dirents;
4971
4972               /* If we switch, we should look at the new relpath */
4973               if (eb->switch_relpath)
4974                 dir_repos_relpath = eb->switch_relpath;
4975
4976               SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4977                                          repos_root, dir_repos_relpath,
4978                                          edit_pool, scratch_pool));
4979
4980               if (dirents != NULL && apr_hash_count(dirents))
4981                 svn_hash_sets(eb->dir_dirents,
4982                               apr_pstrdup(edit_pool, dir_repos_relpath),
4983                               dirents);
4984             }
4985
4986           if (depth == svn_depth_immediates)
4987             {
4988               /* Worst case scenario of issue #3569 fix: We have to do the
4989                  same for all existing subdirs, but then we check for
4990                  svn_depth_empty. */
4991               const apr_array_header_t *children;
4992               apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4993               int i;
4994               SVN_ERR(svn_wc__db_base_get_children(&children, db,
4995                                                    eb->target_abspath,
4996                                                    scratch_pool,
4997                                                    iterpool));
4998
4999               for (i = 0; i < children->nelts; i++)
5000                 {
5001                   const char *child_abspath;
5002                   const char *child_name;
5003
5004                   svn_pool_clear(iterpool);
5005
5006                   child_name = APR_ARRAY_IDX(children, i, const char *);
5007
5008                   child_abspath = svn_dirent_join(eb->target_abspath,
5009                                                   child_name, iterpool);
5010
5011                   SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5012                                                    NULL, &dir_repos_relpath,
5013                                                    NULL, NULL, NULL, NULL,
5014                                                    NULL, &dir_depth, NULL,
5015                                                    NULL, NULL, NULL, NULL,
5016                                                    NULL,
5017                                                    db, child_abspath,
5018                                                    iterpool, iterpool));
5019
5020                   if (dir_kind == svn_node_dir
5021                       && dir_status == svn_wc__db_status_normal
5022                       && dir_depth > svn_depth_empty)
5023                     {
5024                       apr_hash_t *dirents;
5025
5026                       /* If we switch, we should look at the new relpath */
5027                       if (eb->switch_relpath)
5028                         dir_repos_relpath = svn_relpath_join(
5029                                                 eb->switch_relpath,
5030                                                 child_name, iterpool);
5031
5032                       SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5033                                                  repos_root, dir_repos_relpath,
5034                                                  edit_pool, iterpool));
5035
5036                       if (dirents != NULL && apr_hash_count(dirents))
5037                         svn_hash_sets(eb->dir_dirents,
5038                                       apr_pstrdup(edit_pool,
5039                                                   dir_repos_relpath),
5040                                       dirents);
5041                     }
5042                 }
5043             }
5044         }
5045       else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5046         svn_error_clear(err);
5047       else
5048         SVN_ERR(err);
5049     }
5050
5051   /* We need to limit the scope of our operation to the ambient depths
5052      present in the working copy already, but only if the requested
5053      depth is not sticky. If a depth was explicitly requested,
5054      libsvn_delta/depth_filter_editor.c will ensure that we never see
5055      editor calls that extend beyond the scope of the requested depth.
5056      But even what we do so might extend beyond the scope of our
5057      ambient depth.  So we use another filtering editor to avoid
5058      modifying the ambient working copy depth when not asked to do so.
5059      (This can also be skipped if the server understands depth.) */
5060   if (!server_performs_filtering
5061       && !depth_is_sticky)
5062     SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5063                                                 &inner_baton,
5064                                                 db,
5065                                                 anchor_abspath,
5066                                                 target_basename,
5067                                                 inner_editor,
5068                                                 inner_baton,
5069                                                 result_pool));
5070
5071   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5072                                             cancel_baton,
5073                                             inner_editor,
5074                                             inner_baton,
5075                                             editor,
5076                                             edit_baton,
5077                                             result_pool));
5078
5079   sfb = apr_palloc(result_pool, sizeof(*sfb));
5080   sfb->db = db;
5081   sfb->base_abspath = eb->anchor_abspath;
5082   sfb->fetch_base = TRUE;
5083
5084   shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5085   shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5086   shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5087   shim_callbacks->fetch_baton = sfb;
5088
5089   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5090                                    NULL, NULL, shim_callbacks,
5091                                    result_pool, scratch_pool));
5092
5093   return SVN_NO_ERROR;
5094 }
5095
5096
5097 svn_error_t *
5098 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5099                           void **edit_baton,
5100                           svn_revnum_t *target_revision,
5101                           svn_wc_context_t *wc_ctx,
5102                           const char *anchor_abspath,
5103                           const char *target_basename,
5104                           apr_hash_t *wcroot_iprops,
5105                           svn_boolean_t use_commit_times,
5106                           svn_depth_t depth,
5107                           svn_boolean_t depth_is_sticky,
5108                           svn_boolean_t allow_unver_obstructions,
5109                           svn_boolean_t adds_as_modification,
5110                           svn_boolean_t server_performs_filtering,
5111                           svn_boolean_t clean_checkout,
5112                           const char *diff3_cmd,
5113                           const apr_array_header_t *preserved_exts,
5114                           svn_wc_dirents_func_t fetch_dirents_func,
5115                           void *fetch_dirents_baton,
5116                           svn_wc_conflict_resolver_func2_t conflict_func,
5117                           void *conflict_baton,
5118                           svn_wc_external_update_t external_func,
5119                           void *external_baton,
5120                           svn_cancel_func_t cancel_func,
5121                           void *cancel_baton,
5122                           svn_wc_notify_func2_t notify_func,
5123                           void *notify_baton,
5124                           apr_pool_t *result_pool,
5125                           apr_pool_t *scratch_pool)
5126 {
5127   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5128                      target_basename, wcroot_iprops, use_commit_times,
5129                      NULL, depth, depth_is_sticky, allow_unver_obstructions,
5130                      adds_as_modification, server_performs_filtering,
5131                      clean_checkout,
5132                      notify_func, notify_baton,
5133                      cancel_func, cancel_baton,
5134                      fetch_dirents_func, fetch_dirents_baton,
5135                      conflict_func, conflict_baton,
5136                      external_func, external_baton,
5137                      diff3_cmd, preserved_exts, editor, edit_baton,
5138                      result_pool, scratch_pool);
5139 }
5140
5141 svn_error_t *
5142 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5143                           void **edit_baton,
5144                           svn_revnum_t *target_revision,
5145                           svn_wc_context_t *wc_ctx,
5146                           const char *anchor_abspath,
5147                           const char *target_basename,
5148                           const char *switch_url,
5149                           apr_hash_t *wcroot_iprops,
5150                           svn_boolean_t use_commit_times,
5151                           svn_depth_t depth,
5152                           svn_boolean_t depth_is_sticky,
5153                           svn_boolean_t allow_unver_obstructions,
5154                           svn_boolean_t server_performs_filtering,
5155                           const char *diff3_cmd,
5156                           const apr_array_header_t *preserved_exts,
5157                           svn_wc_dirents_func_t fetch_dirents_func,
5158                           void *fetch_dirents_baton,
5159                           svn_wc_conflict_resolver_func2_t conflict_func,
5160                           void *conflict_baton,
5161                           svn_wc_external_update_t external_func,
5162                           void *external_baton,
5163                           svn_cancel_func_t cancel_func,
5164                           void *cancel_baton,
5165                           svn_wc_notify_func2_t notify_func,
5166                           void *notify_baton,
5167                           apr_pool_t *result_pool,
5168                           apr_pool_t *scratch_pool)
5169 {
5170   SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5171
5172   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5173                      target_basename, wcroot_iprops, use_commit_times,
5174                      switch_url,
5175                      depth, depth_is_sticky, allow_unver_obstructions,
5176                      FALSE /* adds_as_modification */,
5177                      server_performs_filtering,
5178                      FALSE /* clean_checkout */,
5179                      notify_func, notify_baton,
5180                      cancel_func, cancel_baton,
5181                      fetch_dirents_func, fetch_dirents_baton,
5182                      conflict_func, conflict_baton,
5183                      external_func, external_baton,
5184                      diff3_cmd, preserved_exts,
5185                      editor, edit_baton,
5186                      result_pool, scratch_pool);
5187 }
5188
5189
5190
5191 /* ### Note that this function is completely different from the rest of the
5192        update editor in what it updates. The update editor changes only BASE
5193        and ACTUAL and this function just changes WORKING and ACTUAL.
5194
5195        In the entries world this function shared a lot of code with the
5196        update editor but in the wonderful new WC-NG world it will probably
5197        do more and more by itself and would be more logically grouped with
5198        the add/copy functionality in adm_ops.c and copy.c. */
5199 svn_error_t *
5200 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5201                        const char *local_abspath,
5202                        svn_stream_t *new_base_contents,
5203                        svn_stream_t *new_contents,
5204                        apr_hash_t *new_base_props,
5205                        apr_hash_t *new_props,
5206                        const char *copyfrom_url,
5207                        svn_revnum_t copyfrom_rev,
5208                        svn_cancel_func_t cancel_func,
5209                        void *cancel_baton,
5210                        apr_pool_t *scratch_pool)
5211 {
5212   svn_wc__db_t *db = wc_ctx->db;
5213   const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5214   svn_wc__db_status_t status;
5215   svn_node_kind_t kind;
5216   const char *tmp_text_base_abspath;
5217   svn_checksum_t *new_text_base_md5_checksum;
5218   svn_checksum_t *new_text_base_sha1_checksum;
5219   const char *source_abspath = NULL;
5220   svn_skel_t *all_work_items = NULL;
5221   svn_skel_t *work_item;
5222   const char *repos_root_url;
5223   const char *repos_uuid;
5224   const char *original_repos_relpath;
5225   svn_revnum_t changed_rev;
5226   apr_time_t changed_date;
5227   const char *changed_author;
5228   svn_error_t *err;
5229   apr_pool_t *pool = scratch_pool;
5230
5231   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5232   SVN_ERR_ASSERT(new_base_contents != NULL);
5233   SVN_ERR_ASSERT(new_base_props != NULL);
5234
5235   /* We should have a write lock on this file's parent directory.  */
5236   SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5237
5238   err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5239                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5240                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5241                              NULL, NULL, NULL,
5242                              db, local_abspath, scratch_pool, scratch_pool);
5243
5244   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5245     return svn_error_trace(err);
5246   else if(err)
5247     svn_error_clear(err);
5248   else
5249     switch (status)
5250       {
5251         case svn_wc__db_status_not_present:
5252         case svn_wc__db_status_deleted:
5253           break;
5254         default:
5255           return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5256                                    _("Node '%s' exists."),
5257                                    svn_dirent_local_style(local_abspath,
5258                                                           scratch_pool));
5259       }
5260
5261   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5262                                &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5263                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5264                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5265                                db, dir_abspath, scratch_pool, scratch_pool));
5266
5267   switch (status)
5268     {
5269       case svn_wc__db_status_normal:
5270       case svn_wc__db_status_added:
5271         break;
5272       case svn_wc__db_status_deleted:
5273         return
5274           svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5275                             _("Can't add '%s' to a parent directory"
5276                               " scheduled for deletion"),
5277                             svn_dirent_local_style(local_abspath,
5278                                                    scratch_pool));
5279       default:
5280         return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5281                                  _("Can't find parent directory's node while"
5282                                    " trying to add '%s'"),
5283                                  svn_dirent_local_style(local_abspath,
5284                                                         scratch_pool));
5285     }
5286   if (kind != svn_node_dir)
5287     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5288                              _("Can't schedule an addition of '%s'"
5289                                " below a not-directory node"),
5290                              svn_dirent_local_style(local_abspath,
5291                                                     scratch_pool));
5292
5293   /* Fabricate the anticipated new URL of the target and check the
5294      copyfrom URL to be in the same repository. */
5295   if (copyfrom_url != NULL)
5296     {
5297       /* Find the repository_root via the parent directory, which
5298          is always versioned before this function is called */
5299
5300       if (!repos_root_url)
5301         {
5302           /* The parent is an addition, scan upwards to find the right info */
5303           SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5304                                            &repos_root_url, &repos_uuid,
5305                                            NULL, NULL, NULL, NULL,
5306                                            wc_ctx->db, dir_abspath,
5307                                            scratch_pool, scratch_pool));
5308         }
5309       SVN_ERR_ASSERT(repos_root_url);
5310
5311       original_repos_relpath =
5312           svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5313
5314       if (!original_repos_relpath)
5315         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5316                                  _("Copyfrom-url '%s' has different repository"
5317                                    " root than '%s'"),
5318                                  copyfrom_url, repos_root_url);
5319     }
5320   else
5321     {
5322       original_repos_relpath = NULL;
5323       copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5324     }
5325
5326   /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5327      filter NEW_BASE_PROPS so it contains only regular props. */
5328   {
5329     apr_array_header_t *regular_props;
5330     apr_array_header_t *entry_props;
5331
5332     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5333                                  &entry_props, NULL, &regular_props,
5334                                  pool));
5335
5336     /* Put regular props back into a hash table. */
5337     new_base_props = svn_prop_array_to_hash(regular_props, pool);
5338
5339     /* Get the change_* info from the entry props.  */
5340     SVN_ERR(accumulate_last_change(&changed_rev,
5341                                    &changed_date,
5342                                    &changed_author,
5343                                    entry_props, pool, pool));
5344   }
5345
5346   /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5347      it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5348      NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5349   {
5350     svn_stream_t *tmp_base_contents;
5351
5352     SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5353                                        &tmp_text_base_abspath,
5354                                        &new_text_base_md5_checksum,
5355                                        &new_text_base_sha1_checksum,
5356                                        wc_ctx->db, local_abspath,
5357                                        pool, pool));
5358     SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5359                              cancel_func, cancel_baton, pool));
5360   }
5361
5362   /* If the caller gave us a new working file, copy it to a safe (temporary)
5363      location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5364      that into place after the node's state has been created.  */
5365   if (new_contents)
5366     {
5367       const char *temp_dir_abspath;
5368       svn_stream_t *tmp_contents;
5369
5370       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5371                                              local_abspath, pool, pool));
5372       SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5373                                      temp_dir_abspath, svn_io_file_del_none,
5374                                      pool, pool));
5375       SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5376                                cancel_func, cancel_baton, pool));
5377     }
5378
5379   /* Install new text base for copied files. Added files do NOT have a
5380      text base.  */
5381   if (copyfrom_url != NULL)
5382     {
5383       SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5384                                           new_text_base_sha1_checksum,
5385                                           new_text_base_md5_checksum, pool));
5386     }
5387   else
5388     {
5389       /* ### There's something wrong around here.  Sometimes (merge from a
5390          foreign repository, at least) we are called with copyfrom_url =
5391          NULL and an empty new_base_contents (and an empty set of
5392          new_base_props).  Why an empty "new base"?
5393
5394          That happens in merge_tests.py 54,87,88,89,143.
5395
5396          In that case, having been given this supposed "new base" file, we
5397          copy it and calculate its checksum but do not install it.  Why?
5398          That must be wrong.
5399
5400          To crudely work around one issue with this, that we shouldn't
5401          record a checksum in the database if we haven't installed the
5402          corresponding pristine text, for now we'll just set the checksum
5403          to NULL.
5404
5405          The proper solution is probably more like: the caller should pass
5406          NULL for the missing information, and this function should learn to
5407          handle that. */
5408
5409       new_text_base_sha1_checksum = NULL;
5410       new_text_base_md5_checksum = NULL;
5411     }
5412
5413   /* For added files without NEW_CONTENTS, then generate the working file
5414      from the provided "pristine" contents.  */
5415   if (new_contents == NULL && copyfrom_url == NULL)
5416     source_abspath = tmp_text_base_abspath;
5417
5418   {
5419     svn_boolean_t record_fileinfo;
5420
5421     /* If new contents were provided, then we do NOT want to record the
5422        file information. We assume the new contents do not match the
5423        "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5424     record_fileinfo = (new_contents == NULL);
5425
5426     /* Install the working copy file (with appropriate translation) from
5427        the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5428        installation from the pristine (available for copied/moved files),
5429        or it will specify a temporary file where we placed a "pristine"
5430        (for an added file) or a detranslated local-mods file.  */
5431     SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5432                                           db, local_abspath,
5433                                           source_abspath,
5434                                           FALSE /* use_commit_times */,
5435                                           record_fileinfo,
5436                                           pool, pool));
5437     all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5438
5439     /* If we installed from somewhere besides the official pristine, then
5440        it is a temporary file, which needs to be removed.  */
5441     if (source_abspath != NULL)
5442       {
5443         SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5444                                              source_abspath,
5445                                              pool, pool));
5446         all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5447       }
5448   }
5449
5450   /* ### ideally, we would have a single DB operation, and queue the work
5451      ### items on that. for now, we'll queue them with the second call.  */
5452
5453   SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5454                                   new_base_props,
5455                                   changed_rev,
5456                                   changed_date,
5457                                   changed_author,
5458                                   original_repos_relpath,
5459                                   original_repos_relpath ? repos_root_url
5460                                                          : NULL,
5461                                   original_repos_relpath ? repos_uuid : NULL,
5462                                   copyfrom_rev,
5463                                   new_text_base_sha1_checksum,
5464                                   TRUE,
5465                                   new_props,
5466                                   FALSE /* is_move */,
5467                                   NULL /* conflict */,
5468                                   all_work_items,
5469                                   pool));
5470
5471   return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5472                                         cancel_func, cancel_baton,
5473                                         pool));
5474 }
5475
5476 svn_error_t *
5477 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5478                                const char *local_abspath,
5479                                apr_hash_t *new_original_props,
5480                                const char *copyfrom_url,
5481                                svn_revnum_t copyfrom_rev,
5482                                apr_pool_t *scratch_pool)
5483 {
5484   svn_wc__db_status_t status;
5485   svn_node_kind_t kind;
5486   const char *original_repos_relpath;
5487   const char *original_root_url;
5488   const char *original_uuid;
5489   svn_boolean_t had_props;
5490   svn_boolean_t props_mod;
5491
5492   svn_revnum_t original_revision;
5493   svn_revnum_t changed_rev;
5494   apr_time_t changed_date;
5495   const char *changed_author;
5496
5497   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5498                                NULL, NULL, NULL, NULL, NULL,
5499                                &original_repos_relpath, &original_root_url,
5500                                &original_uuid, &original_revision, NULL, NULL,
5501                                NULL, NULL, NULL, NULL, &had_props, &props_mod,
5502                                NULL, NULL, NULL,
5503                                wc_ctx->db, local_abspath,
5504                                scratch_pool, scratch_pool));
5505
5506   if (status != svn_wc__db_status_added
5507       || kind != svn_node_dir
5508       || had_props
5509       || props_mod
5510       || !original_repos_relpath)
5511     {
5512       return svn_error_createf(
5513                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5514                     _("'%s' is not an unmodified copied directory"),
5515                     svn_dirent_local_style(local_abspath, scratch_pool));
5516     }
5517   if (original_revision != copyfrom_rev
5518       || strcmp(copyfrom_url,
5519                  svn_path_url_add_component2(original_root_url,
5520                                              original_repos_relpath,
5521                                              scratch_pool)))
5522     {
5523       return svn_error_createf(
5524                     SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5525                     _("Copyfrom '%s' doesn't match original location of '%s'"),
5526                     copyfrom_url,
5527                     svn_dirent_local_style(local_abspath, scratch_pool));
5528     }
5529
5530   {
5531     apr_array_header_t *regular_props;
5532     apr_array_header_t *entry_props;
5533
5534     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5535                                                         scratch_pool),
5536                                  &entry_props, NULL, &regular_props,
5537                                  scratch_pool));
5538
5539     /* Put regular props back into a hash table. */
5540     new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5541
5542     /* Get the change_* info from the entry props.  */
5543     SVN_ERR(accumulate_last_change(&changed_rev,
5544                                    &changed_date,
5545                                    &changed_author,
5546                                    entry_props, scratch_pool, scratch_pool));
5547   }
5548
5549   return svn_error_trace(
5550             svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5551                                    new_original_props,
5552                                    changed_rev, changed_date, changed_author,
5553                                    original_repos_relpath, original_root_url,
5554                                    original_uuid, original_revision,
5555                                    NULL /* children */,
5556                                    FALSE /* is_move */,
5557                                    svn_depth_infinity,
5558                                    NULL /* conflict */,
5559                                    NULL /* work_items */,
5560                                    scratch_pool));
5561 }