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
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
63static svn_error_t *
64path_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
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
150/*** batons ***/
151
152struct 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 */
274static svn_error_t *
275remember_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 */
292struct 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
397struct 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 */
453static svn_error_t *
454get_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. */
473static apr_status_t
474cleanup_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 */
494static svn_error_t *
495calculate_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.  */
567static svn_error_t *
568make_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. */
633static svn_error_t *
634already_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
641static void
642do_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*/
665static svn_error_t *
666maybe_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 */
685struct 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. */
774static svn_error_t *
775make_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 */
819static svn_error_t *
820complete_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 */
893static svn_error_t *
894mark_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 */
930static svn_error_t *
931mark_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_*. */
968static svn_error_t *
969window_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*/
1048static svn_error_t *
1049accumulate_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 */
1104static svn_error_t *
1105path_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
1138/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1139
1140/* An svn_delta_editor_t function. */
1141static svn_error_t *
1142set_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. */
1153static svn_error_t *
1154open_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 */
1334static svn_error_t *
1335check_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 */
1543static svn_error_t *
1544already_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 */
1583static svn_error_t *
1584node_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. */
1599static svn_error_t *
1600delete_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. */
1842static svn_error_t *
1843add_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. */
2224static svn_error_t *
2225open_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. */
2366static svn_error_t *
2367change_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. */
2392static const svn_prop_t *
2393externals_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. */
2410static svn_error_t *
2411close_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'. */
2867static svn_error_t *
2868absent_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. */
3049static svn_error_t *
3050absent_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. */
3059static svn_error_t *
3060absent_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. */
3069static svn_error_t *
3070add_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. */
3416static svn_error_t *
3417open_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. */
3553static svn_error_t *
3554lazy_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. */
3571static svn_error_t *
3572lazy_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. */
3599static svn_error_t *
3600apply_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. */
3727static svn_error_t *
3728change_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 */
3840svn_error_t *
3841svn_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 */
3974static svn_error_t *
3975merge_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. */
4180static svn_error_t *
4181close_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. */
4652static svn_error_t *
4653update_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. */
4720static svn_error_t *
4721close_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
4858/*** Returning editors. ***/
4859
4860/* Helper for the three public editor-supplying functions. */
4861static svn_error_t *
4862make_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
5148svn_error_t *
5149svn_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
5192svn_error_t *
5193svn_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. */
5250svn_error_t *
5251svn_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
5543svn_error_t *
5544svn_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}
5629