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