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