update_editor.c revision 257936
1251881Speter/*
2251881Speter * update_editor.c :  main editor for checkouts and updates
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <stdlib.h>
27251881Speter#include <string.h>
28251881Speter
29251881Speter#include <apr_pools.h>
30251881Speter#include <apr_hash.h>
31251881Speter#include <apr_md5.h>
32251881Speter#include <apr_tables.h>
33251881Speter#include <apr_strings.h>
34251881Speter
35251881Speter#include "svn_types.h"
36251881Speter#include "svn_pools.h"
37251881Speter#include "svn_hash.h"
38251881Speter#include "svn_string.h"
39251881Speter#include "svn_dirent_uri.h"
40251881Speter#include "svn_path.h"
41251881Speter#include "svn_error.h"
42251881Speter#include "svn_io.h"
43251881Speter#include "svn_private_config.h"
44251881Speter#include "svn_time.h"
45251881Speter
46251881Speter#include "wc.h"
47251881Speter#include "adm_files.h"
48251881Speter#include "conflicts.h"
49251881Speter#include "translate.h"
50251881Speter#include "workqueue.h"
51251881Speter
52251881Speter#include "private/svn_subr_private.h"
53251881Speter#include "private/svn_wc_private.h"
54251881Speter#include "private/svn_editor.h"
55251881Speter
56251881Speter/* Checks whether a svn_wc__db_status_t indicates whether a node is
57251881Speter   present in a working copy. Used by the editor implementation */
58251881Speter#define IS_NODE_PRESENT(status)                             \
59251881Speter           ((status) != svn_wc__db_status_server_excluded &&\
60251881Speter            (status) != svn_wc__db_status_excluded &&       \
61251881Speter            (status) != svn_wc__db_status_not_present)
62251881Speter
63251881Speterstatic svn_error_t *
64251881Speterpath_join_under_root(const char **result_path,
65251881Speter                     const char *base_path,
66251881Speter                     const char *add_path,
67251881Speter                     apr_pool_t *result_pool);
68251881Speter
69251881Speter
70251881Speter/*
71251881Speter * This code handles "checkout" and "update" and "switch".
72251881Speter * A checkout is similar to an update that is only adding new items.
73251881Speter *
74251881Speter * The intended behaviour of "update" and "switch", focusing on the checks
75251881Speter * to be made before applying a change, is:
76251881Speter *
77251881Speter *   For each incoming change:
78251881Speter *     if target is already in conflict or obstructed:
79251881Speter *       skip this change
80251881Speter *     else
81251881Speter *     if this action will cause a tree conflict:
82251881Speter *       record the tree conflict
83251881Speter *       skip this change
84251881Speter *     else:
85251881Speter *       make this change
86251881Speter *
87251881Speter * In more detail:
88251881Speter *
89251881Speter *   For each incoming change:
90251881Speter *
91251881Speter *   1.   if  # Incoming change is inside an item already in conflict:
92251881Speter *    a.    tree/text/prop change to node beneath tree-conflicted dir
93251881Speter *        then  # Skip all changes in this conflicted subtree [*1]:
94251881Speter *          do not update the Base nor the Working
95251881Speter *          notify "skipped because already in conflict" just once
96251881Speter *            for the whole conflicted subtree
97251881Speter *
98251881Speter *        if  # Incoming change affects an item already in conflict:
99251881Speter *    b.    tree/text/prop change to tree-conflicted dir/file, or
100251881Speter *    c.    tree change to a text/prop-conflicted file/dir, or
101251881Speter *    d.    text/prop change to a text/prop-conflicted file/dir [*2], or
102251881Speter *    e.    tree change to a dir tree containing any conflicts,
103251881Speter *        then  # Skip this change [*1]:
104251881Speter *          do not update the Base nor the Working
105251881Speter *          notify "skipped because already in conflict"
106251881Speter *
107251881Speter *   2.   if  # Incoming change affects an item that's "obstructed":
108251881Speter *    a.    on-disk node kind doesn't match recorded Working node kind
109251881Speter *            (including an absence/presence mis-match),
110251881Speter *        then  # Skip this change [*1]:
111251881Speter *          do not update the Base nor the Working
112251881Speter *          notify "skipped because obstructed"
113251881Speter *
114251881Speter *   3.   if  # Incoming change raises a tree conflict:
115251881Speter *    a.    tree/text/prop change to node beneath sched-delete dir, or
116251881Speter *    b.    tree/text/prop change to sched-delete dir/file, or
117251881Speter *    c.    text/prop change to tree-scheduled dir/file,
118251881Speter *        then  # Skip this change:
119251881Speter *          do not update the Base nor the Working [*3]
120251881Speter *          notify "tree conflict"
121251881Speter *
122251881Speter *   4.   Apply the change:
123251881Speter *          update the Base
124251881Speter *          update the Working, possibly raising text/prop conflicts
125251881Speter *          notify
126251881Speter *
127251881Speter * Notes:
128251881Speter *
129251881Speter *      "Tree change" here refers to an add or delete of the target node,
130251881Speter *      including the add or delete part of a copy or move or rename.
131251881Speter *
132251881Speter * [*1] We should skip changes to an entire node, as the base revision number
133251881Speter *      applies to the entire node. Not sure how this affects attempts to
134251881Speter *      handle text and prop changes separately.
135251881Speter *
136251881Speter * [*2] Details of which combinations of property and text changes conflict
137251881Speter *      are not specified here.
138251881Speter *
139251881Speter * [*3] For now, we skip the update, and require the user to:
140251881Speter *        - Modify the WC to be compatible with the incoming change;
141251881Speter *        - Mark the conflict as resolved;
142251881Speter *        - Repeat the update.
143251881Speter *      Ideally, it would be possible to resolve any conflict without
144251881Speter *      repeating the update. To achieve this, we would have to store the
145251881Speter *      necessary data at conflict detection time, and delay the update of
146251881Speter *      the Base until the time of resolving.
147251881Speter */
148251881Speter
149251881Speter
150251881Speter/*** batons ***/
151251881Speter
152251881Speterstruct edit_baton
153251881Speter{
154251881Speter  /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
155251881Speter     directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
156251881Speter     target, the values are identical.
157251881Speter
158251881Speter     TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
159251881Speter     ANCHOR_ABSPATH is the target */
160251881Speter  const char *target_basename;
161251881Speter
162251881Speter  /* Absolute variants of ANCHOR and TARGET */
163251881Speter  const char *anchor_abspath;
164251881Speter  const char *target_abspath;
165251881Speter
166251881Speter  /* The DB handle for managing the working copy state.  */
167251881Speter  svn_wc__db_t *db;
168251881Speter
169251881Speter  /* Array of file extension patterns to preserve as extensions in
170251881Speter     generated conflict files. */
171251881Speter  const apr_array_header_t *ext_patterns;
172251881Speter
173251881Speter  /* Hash mapping const char * absolute working copy paths to depth-first
174251881Speter     ordered arrays of svn_prop_inherited_item_t * structures representing
175251881Speter     the properties inherited by the base node at that working copy path.
176251881Speter     May be NULL. */
177251881Speter  apr_hash_t *wcroot_iprops;
178251881Speter
179251881Speter  /* The revision we're targeting...or something like that.  This
180251881Speter     starts off as a pointer to the revision to which we are updating,
181251881Speter     or SVN_INVALID_REVNUM, but by the end of the edit, should be
182251881Speter     pointing to the final revision. */
183251881Speter  svn_revnum_t *target_revision;
184251881Speter
185251881Speter  /* The requested depth of this edit. */
186251881Speter  svn_depth_t requested_depth;
187251881Speter
188251881Speter  /* Is the requested depth merely an operational limitation, or is
189251881Speter     also the new sticky ambient depth of the update target? */
190251881Speter  svn_boolean_t depth_is_sticky;
191251881Speter
192251881Speter  /* Need to know if the user wants us to overwrite the 'now' times on
193251881Speter     edited/added files with the last-commit-time. */
194251881Speter  svn_boolean_t use_commit_times;
195251881Speter
196251881Speter  /* Was the root actually opened (was this a non-empty edit)? */
197251881Speter  svn_boolean_t root_opened;
198251881Speter
199251881Speter  /* Was the update-target deleted?  This is a special situation. */
200251881Speter  svn_boolean_t target_deleted;
201251881Speter
202251881Speter  /* Allow unversioned obstructions when adding a path. */
203251881Speter  svn_boolean_t allow_unver_obstructions;
204251881Speter
205251881Speter  /* Handle local additions as modifications of new nodes */
206251881Speter  svn_boolean_t adds_as_modification;
207251881Speter
208251881Speter  /* If set, we check out into an empty directory. This allows for a number
209251881Speter     of conflict checks to be omitted. */
210251881Speter  svn_boolean_t clean_checkout;
211251881Speter
212251881Speter  /* If this is a 'switch' operation, the new relpath of target_abspath,
213251881Speter     else NULL. */
214251881Speter  const char *switch_relpath;
215251881Speter
216251881Speter  /* The URL to the root of the repository. */
217251881Speter  const char *repos_root;
218251881Speter
219251881Speter  /* The UUID of the repos, or NULL. */
220251881Speter  const char *repos_uuid;
221251881Speter
222251881Speter  /* External diff3 to use for merges (can be null, in which case
223251881Speter     internal merge code is used). */
224251881Speter  const char *diff3_cmd;
225251881Speter
226251881Speter  /* Externals handler */
227251881Speter  svn_wc_external_update_t external_func;
228251881Speter  void *external_baton;
229251881Speter
230251881Speter  /* This editor sends back notifications as it edits. */
231251881Speter  svn_wc_notify_func2_t notify_func;
232251881Speter  void *notify_baton;
233251881Speter
234251881Speter  /* This editor is normally wrapped in a cancellation editor anyway,
235251881Speter     so it doesn't bother to check for cancellation itself.  However,
236251881Speter     it needs a cancel_func and cancel_baton available to pass to
237251881Speter     long-running functions. */
238251881Speter  svn_cancel_func_t cancel_func;
239251881Speter  void *cancel_baton;
240251881Speter
241251881Speter  /* This editor will invoke a interactive conflict-resolution
242251881Speter     callback, if available. */
243251881Speter  svn_wc_conflict_resolver_func2_t conflict_func;
244251881Speter  void *conflict_baton;
245251881Speter
246251881Speter  /* Subtrees that were skipped during the edit, and therefore shouldn't
247251881Speter     have their revision/url info updated at the end.  If a path is a
248251881Speter     directory, its descendants will also be skipped.  The keys are paths
249251881Speter     relative to the working copy root and the values unspecified. */
250251881Speter  apr_hash_t *skipped_trees;
251251881Speter
252251881Speter  /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
253251881Speter     returned from fetch_dirents_func for that repos_relpath. These
254251881Speter     are used to avoid issue #3569 in specific update scenarios where a
255251881Speter     restricted depth is used. */
256251881Speter  apr_hash_t *dir_dirents;
257251881Speter
258251881Speter  /* Absolute path of the working copy root or NULL if not initialized yet */
259251881Speter  const char *wcroot_abspath;
260251881Speter
261251881Speter  apr_pool_t *pool;
262251881Speter};
263251881Speter
264251881Speter
265251881Speter/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
266251881Speter * updated.
267251881Speter *
268251881Speter * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
269251881Speter * LOCAL_ABSPATH.
270251881Speter */
271251881Speterstatic svn_error_t *
272251881Speterremember_skipped_tree(struct edit_baton *eb,
273251881Speter                      const char *local_abspath,
274251881Speter                      apr_pool_t *scratch_pool)
275251881Speter{
276251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
277251881Speter
278251881Speter  svn_hash_sets(eb->skipped_trees,
279251881Speter                apr_pstrdup(eb->pool,
280251881Speter                            svn_dirent_skip_ancestor(eb->wcroot_abspath,
281251881Speter                                                     local_abspath)),
282251881Speter                (void *)1);
283251881Speter
284251881Speter  return SVN_NO_ERROR;
285251881Speter}
286251881Speter
287251881Speter/* Per directory baton. Lives in its own subpool of the parent directory
288251881Speter   or of the edit baton if there is no parent directory */
289251881Speterstruct dir_baton
290251881Speter{
291251881Speter  /* Basename of this directory. */
292251881Speter  const char *name;
293251881Speter
294251881Speter  /* Absolute path of this directory */
295251881Speter  const char *local_abspath;
296251881Speter
297251881Speter  /* The repository relative path this directory will correspond to. */
298251881Speter  const char *new_relpath;
299251881Speter
300251881Speter  /* The revision of the directory before updating */
301251881Speter  svn_revnum_t old_revision;
302251881Speter
303251881Speter  /* The repos_relpath before updating/switching */
304251881Speter  const char *old_repos_relpath;
305251881Speter
306251881Speter  /* The global edit baton. */
307251881Speter  struct edit_baton *edit_baton;
308251881Speter
309251881Speter  /* Baton for this directory's parent, or NULL if this is the root
310251881Speter     directory. */
311251881Speter  struct dir_baton *parent_baton;
312251881Speter
313251881Speter  /* Set if updates to this directory are skipped */
314251881Speter  svn_boolean_t skip_this;
315251881Speter
316251881Speter  /* Set if there was a previous notification for this directory */
317251881Speter  svn_boolean_t already_notified;
318251881Speter
319251881Speter  /* Set if this directory is being added during this editor drive. */
320251881Speter  svn_boolean_t adding_dir;
321251881Speter
322251881Speter  /* Set on a node and its descendants are not present in the working copy
323251881Speter     but should still be updated (not skipped). These nodes should all be
324251881Speter     marked as deleted. */
325251881Speter  svn_boolean_t shadowed;
326251881Speter
327251881Speter  /* Set on a node when the existing node is obstructed, and the edit operation
328251881Speter     continues as semi-shadowed update */
329251881Speter  svn_boolean_t edit_obstructed;
330251881Speter
331251881Speter  /* The (new) changed_* information, cached to avoid retrieving it later */
332251881Speter  svn_revnum_t changed_rev;
333251881Speter  apr_time_t changed_date;
334251881Speter  const char *changed_author;
335251881Speter
336251881Speter  /* If not NULL, contains a mapping of const char* basenames of children that
337251881Speter     have been deleted to their svn_skel_t* tree conflicts.
338251881Speter     We store this hash to allow replacements to continue under a just
339251881Speter     installed tree conflict.
340251881Speter
341251881Speter     The add after the delete will then update the tree conflicts information
342251881Speter     and reinstall it. */
343251881Speter  apr_hash_t *deletion_conflicts;
344251881Speter
345251881Speter  /* A hash of file names (only the hash key matters) seen by add_file
346251881Speter     and not yet added to the database by close_file. */
347251881Speter  apr_hash_t *not_present_files;
348251881Speter
349251881Speter  /* Set if an unversioned dir of the same name already existed in
350251881Speter     this directory. */
351251881Speter  svn_boolean_t obstruction_found;
352251881Speter
353251881Speter  /* Set if a dir of the same name already exists and is
354251881Speter     scheduled for addition without history. */
355251881Speter  svn_boolean_t add_existed;
356251881Speter
357251881Speter  /* An array of svn_prop_t structures, representing all the property
358251881Speter     changes to be applied to this directory. */
359251881Speter  apr_array_header_t *propchanges;
360251881Speter
361251881Speter  /* A boolean indicating whether this node or one of its children has
362251881Speter     received any 'real' changes. Used to avoid tree conflicts for simple
363251881Speter     entryprop changes, like lock management */
364251881Speter  svn_boolean_t edited;
365251881Speter
366251881Speter  /* The tree conflict to install once the node is really edited */
367251881Speter  svn_skel_t *edit_conflict;
368251881Speter
369251881Speter  /* The bump information for this directory. */
370251881Speter  struct bump_dir_info *bump_info;
371251881Speter
372251881Speter  /* The depth of the directory in the wc (or inferred if added).  Not
373251881Speter     used for filtering; we have a separate wrapping editor for that. */
374251881Speter  svn_depth_t ambient_depth;
375251881Speter
376251881Speter  /* Was the directory marked as incomplete before the update?
377251881Speter     (In other words, are we resuming an interrupted update?)
378251881Speter
379251881Speter     If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
380251881Speter     and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
381251881Speter     we only receive the changes in/for children and properties.*/
382251881Speter  svn_boolean_t was_incomplete;
383251881Speter
384251881Speter  /* The pool in which this baton itself is allocated. */
385251881Speter  apr_pool_t *pool;
386251881Speter
387251881Speter  /* how many nodes are referring to baton? */
388251881Speter  int ref_count;
389251881Speter
390251881Speter};
391251881Speter
392251881Speter
393251881Speterstruct handler_baton
394251881Speter{
395251881Speter  svn_txdelta_window_handler_t apply_handler;
396251881Speter  void *apply_baton;
397251881Speter  apr_pool_t *pool;
398251881Speter  struct file_baton *fb;
399251881Speter
400251881Speter  /* Where we are assembling the new file. */
401251881Speter  const char *new_text_base_tmp_abspath;
402251881Speter
403251881Speter    /* The expected source checksum of the text source or NULL if no base
404251881Speter     checksum is available (MD5 if the server provides a checksum, SHA1 if
405251881Speter     the server doesn't) */
406251881Speter  svn_checksum_t *expected_source_checksum;
407251881Speter
408251881Speter  /* Why two checksums?
409251881Speter     The editor currently provides an md5 which we use to detect corruption
410251881Speter     during transmission.  We use the sha1 inside libsvn_wc both for pristine
411251881Speter     handling and corruption detection.  In the future, the editor will also
412251881Speter     provide a sha1, so we may not have to calculate both, but for the time
413251881Speter     being, that's the way it is. */
414251881Speter
415251881Speter  /* The calculated checksum of the text source or NULL if the acual
416251881Speter     checksum is not being calculated. The checksum kind is identical to the
417251881Speter     kind of expected_source_checksum. */
418251881Speter  svn_checksum_t *actual_source_checksum;
419251881Speter
420251881Speter  /* The stream used to calculate the source checksums */
421251881Speter  svn_stream_t *source_checksum_stream;
422251881Speter
423251881Speter  /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
424251881Speter     This is initialized to all zeroes when the baton is created, then
425251881Speter     populated with the MD5 digest of the resultant fulltext after the
426251881Speter     last window is handled by the handler returned from
427251881Speter     apply_textdelta(). */
428251881Speter  unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
429251881Speter
430251881Speter  /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
431251881Speter     eventually writing the pristine. */
432251881Speter  svn_checksum_t * new_text_base_sha1_checksum;
433251881Speter};
434251881Speter
435251881Speter
436251881Speter/* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
437251881Speter   not be set for automatic deletion, and the name will be returned in
438251881Speter   TMP_FILENAME.
439251881Speter
440251881Speter   This implementation creates a new empty file with a unique name.
441251881Speter
442251881Speter   ### This is inefficient for callers that just want an empty file to read
443251881Speter   ### from.  There could be (and there used to be) a permanent, shared
444251881Speter   ### empty file for this purpose.
445251881Speter
446251881Speter   ### This is inefficient for callers that just want to reserve a unique
447251881Speter   ### file name to create later.  A better way may not be readily available.
448251881Speter */
449251881Speterstatic svn_error_t *
450251881Speterget_empty_tmp_file(const char **tmp_filename,
451251881Speter                   svn_wc__db_t *db,
452251881Speter                   const char *wri_abspath,
453251881Speter                   apr_pool_t *result_pool,
454251881Speter                   apr_pool_t *scratch_pool)
455251881Speter{
456251881Speter  const char *temp_dir_abspath;
457251881Speter
458251881Speter  SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
459251881Speter                                         scratch_pool, scratch_pool));
460251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
461251881Speter                                   svn_io_file_del_none,
462251881Speter                                   scratch_pool, scratch_pool));
463251881Speter
464251881Speter  return SVN_NO_ERROR;
465251881Speter}
466251881Speter
467251881Speter/* An APR pool cleanup handler.  This runs the working queue for an
468251881Speter   editor baton. */
469251881Speterstatic apr_status_t
470251881Spetercleanup_edit_baton(void *edit_baton)
471251881Speter{
472251881Speter  struct edit_baton *eb = edit_baton;
473251881Speter  svn_error_t *err;
474251881Speter  apr_pool_t *pool = apr_pool_parent_get(eb->pool);
475251881Speter
476251881Speter  err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
477251881Speter                       NULL /* cancel_func */, NULL /* cancel_baton */,
478251881Speter                       pool);
479251881Speter
480251881Speter  if (err)
481251881Speter    {
482251881Speter      apr_status_t apr_err = err->apr_err;
483251881Speter      svn_error_clear(err);
484251881Speter      return apr_err;
485251881Speter    }
486251881Speter  return APR_SUCCESS;
487251881Speter}
488251881Speter
489251881Speter/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
490251881Speter   If PATH and PB are NULL, this is the root directory of the edit; in this
491251881Speter   case, make the new dir baton in a subpool of EB->pool.
492251881Speter   ADDING should be TRUE if we are adding this directory.  */
493251881Speterstatic svn_error_t *
494251881Spetermake_dir_baton(struct dir_baton **d_p,
495251881Speter               const char *path,
496251881Speter               struct edit_baton *eb,
497251881Speter               struct dir_baton *pb,
498251881Speter               svn_boolean_t adding,
499251881Speter               apr_pool_t *scratch_pool)
500251881Speter{
501251881Speter  apr_pool_t *dir_pool;
502251881Speter  struct dir_baton *d;
503251881Speter
504251881Speter  if (pb != NULL)
505251881Speter    dir_pool = svn_pool_create(pb->pool);
506251881Speter  else
507251881Speter    dir_pool = svn_pool_create(eb->pool);
508251881Speter
509251881Speter  SVN_ERR_ASSERT(path || (! pb));
510251881Speter
511251881Speter  /* Okay, no easy out, so allocate and initialize a dir baton. */
512251881Speter  d = apr_pcalloc(dir_pool, sizeof(*d));
513251881Speter
514251881Speter  /* Construct the PATH and baseNAME of this directory. */
515251881Speter  if (path)
516251881Speter    {
517251881Speter      d->name = svn_dirent_basename(path, dir_pool);
518251881Speter      SVN_ERR(path_join_under_root(&d->local_abspath,
519251881Speter                                   pb->local_abspath, d->name, dir_pool));
520251881Speter    }
521251881Speter  else
522251881Speter    {
523251881Speter      /* This is the root baton. */
524251881Speter      d->name = NULL;
525251881Speter      d->local_abspath = eb->anchor_abspath;
526251881Speter    }
527251881Speter
528251881Speter  /* Figure out the new_relpath for this directory. */
529251881Speter  if (eb->switch_relpath)
530251881Speter    {
531251881Speter      /* Handle switches... */
532251881Speter
533251881Speter      if (pb == NULL)
534251881Speter        {
535251881Speter          if (*eb->target_basename == '\0')
536251881Speter            {
537251881Speter              /* No parent baton and target_basename=="" means that we are
538251881Speter                 the target of the switch. Thus, our NEW_RELPATH will be
539251881Speter                 the SWITCH_RELPATH.  */
540251881Speter              d->new_relpath = eb->switch_relpath;
541251881Speter            }
542251881Speter          else
543251881Speter            {
544251881Speter              /* This node is NOT the target of the switch (one of our
545251881Speter                 children is the target); therefore, it must already exist.
546251881Speter                 Get its old REPOS_RELPATH, as it won't be changing.  */
547251881Speter              SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
548251881Speter                                                 eb->db, d->local_abspath,
549251881Speter                                                 dir_pool, scratch_pool));
550251881Speter            }
551251881Speter        }
552251881Speter      else
553251881Speter        {
554251881Speter          /* This directory is *not* the root (has a parent). If there is
555251881Speter             no grandparent, then we may have anchored at the parent,
556251881Speter             and self is the target. If we match the target, then set
557251881Speter             NEW_RELPATH to the SWITCH_RELPATH.
558251881Speter
559251881Speter             Otherwise, we simply extend NEW_RELPATH from the parent.  */
560251881Speter          if (pb->parent_baton == NULL
561251881Speter              && strcmp(eb->target_basename, d->name) == 0)
562251881Speter            d->new_relpath = eb->switch_relpath;
563251881Speter          else
564251881Speter            d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
565251881Speter                                              dir_pool);
566251881Speter        }
567251881Speter    }
568251881Speter  else  /* must be an update */
569251881Speter    {
570251881Speter      /* If we are adding the node, then simply extend the parent's
571251881Speter         relpath for our own.  */
572251881Speter      if (adding)
573251881Speter        {
574251881Speter          SVN_ERR_ASSERT(pb != NULL);
575251881Speter          d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
576251881Speter                                            dir_pool);
577251881Speter        }
578251881Speter      else
579251881Speter        {
580251881Speter          SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
581251881Speter                                             eb->db, d->local_abspath,
582251881Speter                                             dir_pool, scratch_pool));
583251881Speter          SVN_ERR_ASSERT(d->new_relpath);
584251881Speter        }
585251881Speter    }
586251881Speter
587251881Speter  d->edit_baton   = eb;
588251881Speter  d->parent_baton = pb;
589251881Speter  d->pool         = dir_pool;
590251881Speter  d->propchanges  = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
591251881Speter  d->obstruction_found = FALSE;
592251881Speter  d->add_existed  = FALSE;
593251881Speter  d->ref_count = 1;
594251881Speter  d->old_revision = SVN_INVALID_REVNUM;
595251881Speter  d->adding_dir   = adding;
596251881Speter  d->changed_rev  = SVN_INVALID_REVNUM;
597251881Speter  d->not_present_files = apr_hash_make(dir_pool);
598251881Speter
599251881Speter  /* Copy some flags from the parent baton */
600251881Speter  if (pb)
601251881Speter    {
602251881Speter      d->skip_this = pb->skip_this;
603251881Speter      d->shadowed = pb->shadowed || pb->edit_obstructed;
604251881Speter
605251881Speter      /* the parent's bump info has one more referer */
606251881Speter      pb->ref_count++;
607251881Speter    }
608251881Speter
609251881Speter  /* The caller of this function needs to fill these in. */
610251881Speter  d->ambient_depth = svn_depth_unknown;
611251881Speter  d->was_incomplete = FALSE;
612251881Speter
613251881Speter  *d_p = d;
614251881Speter  return SVN_NO_ERROR;
615251881Speter}
616251881Speter
617251881Speter
618251881Speter/* Forward declarations. */
619251881Speterstatic svn_error_t *
620251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted,
621251881Speter                           svn_boolean_t *ignored,
622251881Speter                           svn_wc__db_t *db,
623251881Speter                           const char *local_abspath,
624251881Speter                           apr_pool_t *scratch_pool);
625251881Speter
626251881Speter
627251881Speterstatic void
628251881Speterdo_notification(const struct edit_baton *eb,
629251881Speter                const char *local_abspath,
630251881Speter                svn_node_kind_t kind,
631251881Speter                svn_wc_notify_action_t action,
632251881Speter                apr_pool_t *scratch_pool)
633251881Speter{
634251881Speter  svn_wc_notify_t *notify;
635251881Speter
636251881Speter  if (eb->notify_func == NULL)
637251881Speter    return;
638251881Speter
639251881Speter  notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
640251881Speter  notify->kind = kind;
641251881Speter
642251881Speter  (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
643251881Speter}
644251881Speter
645251881Speter/* Decrement the directory's reference count. If it hits zero,
646251881Speter   then this directory is "done". This means it is safe to clear its pool.
647251881Speter
648251881Speter   In addition, when the directory is "done", we recurse to possible cleanup
649251881Speter   the parent directory.
650251881Speter*/
651251881Speterstatic svn_error_t *
652251881Spetermaybe_release_dir_info(struct dir_baton *db)
653251881Speter{
654251881Speter  db->ref_count--;
655251881Speter
656251881Speter  if (!db->ref_count)
657251881Speter    {
658251881Speter      struct dir_baton *pb = db->parent_baton;
659251881Speter
660251881Speter      svn_pool_destroy(db->pool);
661251881Speter
662251881Speter      if (pb)
663251881Speter        SVN_ERR(maybe_release_dir_info(pb));
664251881Speter    }
665251881Speter
666251881Speter  return SVN_NO_ERROR;
667251881Speter}
668251881Speter
669251881Speter/* Per file baton. Lives in its own subpool below the pool of the parent
670251881Speter   directory */
671251881Speterstruct file_baton
672251881Speter{
673251881Speter  /* Pool specific to this file_baton. */
674251881Speter  apr_pool_t *pool;
675251881Speter
676251881Speter  /* Name of this file (its entry in the directory). */
677251881Speter  const char *name;
678251881Speter
679251881Speter  /* Absolute path to this file */
680251881Speter  const char *local_abspath;
681251881Speter
682251881Speter  /* The repository relative path this file will correspond to. */
683251881Speter  const char *new_relpath;
684251881Speter
685251881Speter  /* The revision of the file before updating */
686251881Speter  svn_revnum_t old_revision;
687251881Speter
688251881Speter  /* The repos_relpath before updating/switching */
689251881Speter  const char *old_repos_relpath;
690251881Speter
691251881Speter  /* The global edit baton. */
692251881Speter  struct edit_baton *edit_baton;
693251881Speter
694251881Speter  /* The parent directory of this file. */
695251881Speter  struct dir_baton *dir_baton;
696251881Speter
697251881Speter  /* Set if updates to this directory are skipped */
698251881Speter  svn_boolean_t skip_this;
699251881Speter
700251881Speter  /* Set if there was a previous notification  */
701251881Speter  svn_boolean_t already_notified;
702251881Speter
703251881Speter  /* Set if this file is new. */
704251881Speter  svn_boolean_t adding_file;
705251881Speter
706251881Speter  /* Set if an unversioned file of the same name already existed in
707251881Speter     this directory. */
708251881Speter  svn_boolean_t obstruction_found;
709251881Speter
710251881Speter  /* Set if a file of the same name already exists and is
711251881Speter     scheduled for addition without history. */
712251881Speter  svn_boolean_t add_existed;
713251881Speter
714251881Speter  /* Set if this file is being added in the BASE layer, but is not-present
715251881Speter     in the working copy (replaced, deleted, etc.). */
716251881Speter  svn_boolean_t shadowed;
717251881Speter
718251881Speter  /* Set on a node when the existing node is obstructed, and the edit operation
719251881Speter     continues as semi-shadowed update */
720251881Speter  svn_boolean_t edit_obstructed;
721251881Speter
722251881Speter  /* The (new) changed_* information, cached to avoid retrieving it later */
723251881Speter  svn_revnum_t changed_rev;
724251881Speter  apr_time_t changed_date;
725251881Speter  const char *changed_author;
726251881Speter
727251881Speter  /* If there are file content changes, these are the checksums of the
728251881Speter     resulting new text base, which is in the pristine store, else NULL. */
729251881Speter  const svn_checksum_t *new_text_base_md5_checksum;
730251881Speter  const svn_checksum_t *new_text_base_sha1_checksum;
731251881Speter
732251881Speter  /* The checksum of the file before the update */
733251881Speter  const svn_checksum_t *original_checksum;
734251881Speter
735251881Speter  /* An array of svn_prop_t structures, representing all the property
736251881Speter     changes to be applied to this file.  Once a file baton is
737251881Speter     initialized, this is never NULL, but it may have zero elements.  */
738251881Speter  apr_array_header_t *propchanges;
739251881Speter
740251881Speter  /* For existing files, whether there are local modifications. FALSE for added
741251881Speter     files */
742251881Speter  svn_boolean_t local_prop_mods;
743251881Speter
744251881Speter  /* Bump information for the directory this file lives in */
745251881Speter  struct bump_dir_info *bump_info;
746251881Speter
747251881Speter  /* A boolean indicating whether this node or one of its children has
748251881Speter     received any 'real' changes. Used to avoid tree conflicts for simple
749251881Speter     entryprop changes, like lock management */
750251881Speter  svn_boolean_t edited;
751251881Speter
752251881Speter  /* The tree conflict to install once the node is really edited */
753251881Speter  svn_skel_t *edit_conflict;
754251881Speter};
755251881Speter
756251881Speter
757251881Speter/* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
758251881Speter * PATH is relative to the root of the edit. ADDING tells whether this file
759251881Speter * is being added. */
760251881Speterstatic svn_error_t *
761251881Spetermake_file_baton(struct file_baton **f_p,
762251881Speter                struct dir_baton *pb,
763251881Speter                const char *path,
764251881Speter                svn_boolean_t adding,
765251881Speter                apr_pool_t *scratch_pool)
766251881Speter{
767251881Speter  struct edit_baton *eb = pb->edit_baton;
768251881Speter  apr_pool_t *file_pool = svn_pool_create(pb->pool);
769251881Speter  struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
770251881Speter
771251881Speter  SVN_ERR_ASSERT(path);
772251881Speter
773251881Speter  /* Make the file's on-disk name. */
774251881Speter  f->name = svn_dirent_basename(path, file_pool);
775251881Speter  f->old_revision = SVN_INVALID_REVNUM;
776251881Speter  SVN_ERR(path_join_under_root(&f->local_abspath,
777251881Speter                               pb->local_abspath, f->name, file_pool));
778251881Speter
779251881Speter  /* Figure out the new URL for this file. */
780251881Speter  if (eb->switch_relpath)
781251881Speter    {
782251881Speter      /* Handle switches... */
783251881Speter
784251881Speter      /* This file has a parent directory. If there is
785251881Speter         no grandparent, then we may have anchored at the parent,
786251881Speter         and self is the target. If we match the target, then set
787251881Speter         NEW_RELPATH to the SWITCH_RELPATH.
788251881Speter
789251881Speter         Otherwise, we simply extend NEW_RELPATH from the parent.  */
790251881Speter      if (pb->parent_baton == NULL
791251881Speter          && strcmp(eb->target_basename, f->name) == 0)
792251881Speter        f->new_relpath = eb->switch_relpath;
793251881Speter      else
794251881Speter        f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
795251881Speter                                          file_pool);
796251881Speter    }
797251881Speter  else  /* must be an update */
798251881Speter    {
799251881Speter      if (adding)
800251881Speter        f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
801251881Speter      else
802251881Speter        {
803251881Speter          SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
804251881Speter                                             eb->db, f->local_abspath,
805251881Speter                                             file_pool, scratch_pool));
806251881Speter          SVN_ERR_ASSERT(f->new_relpath);
807251881Speter        }
808251881Speter    }
809251881Speter
810251881Speter  f->pool              = file_pool;
811251881Speter  f->edit_baton        = pb->edit_baton;
812251881Speter  f->propchanges       = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
813251881Speter  f->bump_info         = pb->bump_info;
814251881Speter  f->adding_file       = adding;
815251881Speter  f->obstruction_found = FALSE;
816251881Speter  f->add_existed       = FALSE;
817251881Speter  f->skip_this         = pb->skip_this;
818251881Speter  f->shadowed          = pb->shadowed || pb->edit_obstructed;
819251881Speter  f->dir_baton         = pb;
820251881Speter  f->changed_rev       = SVN_INVALID_REVNUM;
821251881Speter
822251881Speter  /* the directory has one more referer now */
823251881Speter  pb->ref_count++;
824251881Speter
825251881Speter  *f_p = f;
826251881Speter  return SVN_NO_ERROR;
827251881Speter}
828251881Speter
829251881Speter/* Complete a conflict skel by describing the update.
830251881Speter *
831251881Speter * LOCAL_KIND is the node kind of the tree conflict victim in the
832251881Speter * working copy.
833251881Speter *
834251881Speter * All temporary allocations are be made in SCRATCH_POOL, while allocations
835251881Speter * needed for the returned conflict struct are made in RESULT_POOL.
836251881Speter */
837251881Speterstatic svn_error_t *
838251881Spetercomplete_conflict(svn_skel_t *conflict,
839251881Speter                  const struct edit_baton *eb,
840251881Speter                  const char *local_abspath,
841251881Speter                  const char *old_repos_relpath,
842251881Speter                  svn_revnum_t old_revision,
843251881Speter                  const char *new_repos_relpath,
844251881Speter                  svn_node_kind_t local_kind,
845251881Speter                  svn_node_kind_t target_kind,
846251881Speter                  apr_pool_t *result_pool,
847251881Speter                  apr_pool_t *scratch_pool)
848251881Speter{
849251881Speter  svn_wc_conflict_version_t *original_version;
850251881Speter  svn_wc_conflict_version_t *target_version;
851251881Speter  svn_boolean_t is_complete;
852251881Speter
853251881Speter  if (!conflict)
854251881Speter    return SVN_NO_ERROR; /* Not conflicted */
855251881Speter
856251881Speter  SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
857251881Speter
858251881Speter  if (is_complete)
859251881Speter    return SVN_NO_ERROR; /* Already completed */
860251881Speter
861251881Speter  if (old_repos_relpath)
862251881Speter    original_version = svn_wc_conflict_version_create2(eb->repos_root,
863251881Speter                                                       eb->repos_uuid,
864251881Speter                                                       old_repos_relpath,
865251881Speter                                                       old_revision,
866251881Speter                                                       local_kind,
867251881Speter                                                       result_pool);
868251881Speter  else
869251881Speter    original_version = NULL;
870251881Speter
871251881Speter  if (new_repos_relpath)
872251881Speter    target_version = svn_wc_conflict_version_create2(eb->repos_root,
873251881Speter                                                        eb->repos_uuid,
874251881Speter                                                        new_repos_relpath,
875251881Speter                                                        *eb->target_revision,
876251881Speter                                                        target_kind,
877251881Speter                                                        result_pool);
878251881Speter  else
879251881Speter    target_version = NULL;
880251881Speter
881251881Speter  if (eb->switch_relpath)
882251881Speter    SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
883251881Speter                                                original_version,
884251881Speter                                                target_version,
885251881Speter                                                result_pool, scratch_pool));
886251881Speter  else
887251881Speter    SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
888251881Speter                                                original_version,
889251881Speter                                                target_version,
890251881Speter                                                result_pool, scratch_pool));
891251881Speter
892251881Speter  return SVN_NO_ERROR;
893251881Speter}
894251881Speter
895251881Speter
896251881Speter/* Called when a directory is really edited, to avoid marking a
897251881Speter   tree conflict on a node for a no-change edit */
898251881Speterstatic svn_error_t *
899251881Spetermark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
900251881Speter{
901251881Speter  if (db->edited)
902251881Speter    return SVN_NO_ERROR;
903251881Speter
904251881Speter  if (db->parent_baton)
905251881Speter    SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
906251881Speter
907251881Speter  db->edited = TRUE;
908251881Speter
909251881Speter  if (db->edit_conflict)
910251881Speter    {
911251881Speter      /* We have a (delayed) tree conflict to install */
912251881Speter
913251881Speter      SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
914251881Speter                                db->local_abspath,
915251881Speter                                db->old_repos_relpath, db->old_revision,
916251881Speter                                db->new_relpath,
917251881Speter                                svn_node_dir, svn_node_dir,
918251881Speter                                db->pool, scratch_pool));
919251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
920251881Speter                                          db->local_abspath,
921251881Speter                                          db->edit_conflict, NULL,
922251881Speter                                          scratch_pool));
923251881Speter
924251881Speter      do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
925251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
926251881Speter      db->already_notified = TRUE;
927253734Speter
928251881Speter    }
929251881Speter
930251881Speter  return SVN_NO_ERROR;
931251881Speter}
932251881Speter
933251881Speter/* Called when a file is really edited, to avoid marking a
934251881Speter   tree conflict on a node for a no-change edit */
935251881Speterstatic svn_error_t *
936251881Spetermark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
937251881Speter{
938251881Speter  if (fb->edited)
939251881Speter    return SVN_NO_ERROR;
940251881Speter
941251881Speter  SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
942251881Speter
943251881Speter  fb->edited = TRUE;
944251881Speter
945251881Speter  if (fb->edit_conflict)
946251881Speter    {
947251881Speter      /* We have a (delayed) tree conflict to install */
948251881Speter
949251881Speter      SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
950251881Speter                                fb->local_abspath, fb->old_repos_relpath,
951251881Speter                                fb->old_revision, fb->new_relpath,
952251881Speter                                svn_node_file, svn_node_file,
953251881Speter                                fb->pool, scratch_pool));
954251881Speter
955251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
956251881Speter                                          fb->local_abspath,
957251881Speter                                          fb->edit_conflict, NULL,
958251881Speter                                          scratch_pool));
959251881Speter
960251881Speter      do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
961251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
962251881Speter      fb->already_notified = TRUE;
963251881Speter    }
964251881Speter
965251881Speter  return SVN_NO_ERROR;
966251881Speter}
967251881Speter
968251881Speter
969251881Speter/* Handle the next delta window of the file described by BATON.  If it is
970251881Speter * the end (WINDOW == NULL), then check the checksum, store the text in the
971251881Speter * pristine store and write its details into BATON->fb->new_text_base_*. */
972251881Speterstatic svn_error_t *
973251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton)
974251881Speter{
975251881Speter  struct handler_baton *hb = baton;
976251881Speter  struct file_baton *fb = hb->fb;
977251881Speter  svn_wc__db_t *db = fb->edit_baton->db;
978251881Speter  svn_error_t *err;
979251881Speter
980251881Speter  /* Apply this window.  We may be done at that point.  */
981251881Speter  err = hb->apply_handler(window, hb->apply_baton);
982251881Speter  if (window != NULL && !err)
983251881Speter    return SVN_NO_ERROR;
984251881Speter
985251881Speter  if (hb->expected_source_checksum)
986251881Speter    {
987251881Speter      /* Close the stream to calculate HB->actual_source_md5_checksum. */
988251881Speter      svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
989251881Speter
990251881Speter      if (!err2)
991251881Speter        {
992251881Speter          SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
993251881Speter                        hb->actual_source_checksum->kind);
994251881Speter
995251881Speter          if (!svn_checksum_match(hb->expected_source_checksum,
996251881Speter                                  hb->actual_source_checksum))
997251881Speter            {
998251881Speter              err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
999251881Speter                        _("Checksum mismatch while updating '%s':\n"
1000251881Speter                          "   expected:  %s\n"
1001251881Speter                          "     actual:  %s\n"),
1002251881Speter                        svn_dirent_local_style(fb->local_abspath, hb->pool),
1003251881Speter                        svn_checksum_to_cstring(hb->expected_source_checksum,
1004251881Speter                                                hb->pool),
1005251881Speter                        svn_checksum_to_cstring(hb->actual_source_checksum,
1006251881Speter                                                hb->pool));
1007251881Speter            }
1008251881Speter        }
1009251881Speter
1010251881Speter      err = svn_error_compose_create(err, err2);
1011251881Speter    }
1012251881Speter
1013251881Speter  if (err)
1014251881Speter    {
1015257936Speter      /* We failed to apply the delta; clean up the temporary file if it
1016257936Speter         already created by lazy_open_target(). */
1017257936Speter      if (hb->new_text_base_tmp_abspath)
1018257936Speter        {
1019257936Speter          svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath,
1020257936Speter                                              TRUE, hb->pool));
1021257936Speter        }
1022251881Speter    }
1023251881Speter  else
1024251881Speter    {
1025251881Speter      /* Tell the file baton about the new text base's checksums. */
1026251881Speter      fb->new_text_base_md5_checksum =
1027251881Speter        svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1028251881Speter      fb->new_text_base_sha1_checksum =
1029251881Speter        svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1030251881Speter
1031251881Speter      /* Store the new pristine text in the pristine store now.  Later, in a
1032251881Speter         single transaction we will update the BASE_NODE to include a
1033251881Speter         reference to this pristine text's checksum. */
1034251881Speter      SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
1035251881Speter                                          fb->new_text_base_sha1_checksum,
1036251881Speter                                          fb->new_text_base_md5_checksum,
1037251881Speter                                          hb->pool));
1038251881Speter    }
1039251881Speter
1040251881Speter  svn_pool_destroy(hb->pool);
1041251881Speter
1042251881Speter  return err;
1043251881Speter}
1044251881Speter
1045251881Speter
1046251881Speter/* Find the last-change info within ENTRY_PROPS, and return then in the
1047251881Speter   CHANGED_* parameters. Each parameter will be initialized to its "none"
1048251881Speter   value, and will contain the relavent info if found.
1049251881Speter
1050251881Speter   CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1051251881Speter   used for some temporary allocations.
1052251881Speter*/
1053251881Speterstatic svn_error_t *
1054251881Speteraccumulate_last_change(svn_revnum_t *changed_rev,
1055251881Speter                       apr_time_t *changed_date,
1056251881Speter                       const char **changed_author,
1057251881Speter                       const apr_array_header_t *entry_props,
1058251881Speter                       apr_pool_t *result_pool,
1059251881Speter                       apr_pool_t *scratch_pool)
1060251881Speter{
1061251881Speter  int i;
1062251881Speter
1063251881Speter  *changed_rev = SVN_INVALID_REVNUM;
1064251881Speter  *changed_date = 0;
1065251881Speter  *changed_author = NULL;
1066251881Speter
1067251881Speter  for (i = 0; i < entry_props->nelts; ++i)
1068251881Speter    {
1069251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1070251881Speter
1071251881Speter      /* A prop value of NULL means the information was not
1072251881Speter         available.  We don't remove this field from the entries
1073251881Speter         file; we have convention just leave it empty.  So let's
1074251881Speter         just skip those entry props that have no values. */
1075251881Speter      if (! prop->value)
1076251881Speter        continue;
1077251881Speter
1078251881Speter      if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1079251881Speter        *changed_author = apr_pstrdup(result_pool, prop->value->data);
1080251881Speter      else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1081251881Speter        {
1082251881Speter          apr_int64_t rev;
1083251881Speter          SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1084251881Speter          *changed_rev = (svn_revnum_t)rev;
1085251881Speter        }
1086251881Speter      else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1087251881Speter        SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1088251881Speter                                      scratch_pool));
1089251881Speter
1090251881Speter      /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1091251881Speter         property here. */
1092251881Speter    }
1093251881Speter
1094251881Speter  return SVN_NO_ERROR;
1095251881Speter}
1096251881Speter
1097251881Speter
1098251881Speter/* Join ADD_PATH to BASE_PATH.  If ADD_PATH is absolute, or if any ".."
1099251881Speter * component of it resolves to a path above BASE_PATH, then return
1100251881Speter * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1101251881Speter *
1102251881Speter * This is to prevent the situation where the repository contains,
1103251881Speter * say, "..\nastyfile".  Although that's perfectly legal on some
1104251881Speter * systems, when checked out onto Win32 it would cause "nastyfile" to
1105251881Speter * be created in the parent of the current edit directory.
1106251881Speter *
1107251881Speter * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1108251881Speter */
1109251881Speterstatic svn_error_t *
1110251881Speterpath_join_under_root(const char **result_path,
1111251881Speter                     const char *base_path,
1112251881Speter                     const char *add_path,
1113251881Speter                     apr_pool_t *pool)
1114251881Speter{
1115251881Speter  svn_boolean_t under_root;
1116251881Speter
1117251881Speter  SVN_ERR(svn_dirent_is_under_root(&under_root,
1118251881Speter                                   result_path, base_path, add_path, pool));
1119251881Speter
1120251881Speter  if (! under_root)
1121251881Speter    {
1122251881Speter      return svn_error_createf(
1123251881Speter          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1124251881Speter          _("Path '%s' is not in the working copy"),
1125251881Speter          svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1126251881Speter                                 pool));
1127251881Speter    }
1128251881Speter
1129251881Speter  /* This catches issue #3288 */
1130251881Speter  if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1131251881Speter    {
1132251881Speter      return svn_error_createf(
1133251881Speter          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1134251881Speter          _("'%s' is not valid as filename in directory '%s'"),
1135251881Speter          svn_dirent_local_style(add_path, pool),
1136251881Speter          svn_dirent_local_style(base_path, pool));
1137251881Speter    }
1138251881Speter
1139251881Speter  return SVN_NO_ERROR;
1140251881Speter}
1141251881Speter
1142251881Speter
1143251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1144251881Speter
1145251881Speter/* An svn_delta_editor_t function. */
1146251881Speterstatic svn_error_t *
1147251881Speterset_target_revision(void *edit_baton,
1148251881Speter                    svn_revnum_t target_revision,
1149251881Speter                    apr_pool_t *pool)
1150251881Speter{
1151251881Speter  struct edit_baton *eb = edit_baton;
1152251881Speter
1153251881Speter  *(eb->target_revision) = target_revision;
1154251881Speter  return SVN_NO_ERROR;
1155251881Speter}
1156251881Speter
1157251881Speterstatic svn_error_t *
1158251881Spetercheck_tree_conflict(svn_skel_t **pconflict,
1159251881Speter                    struct edit_baton *eb,
1160251881Speter                    const char *local_abspath,
1161251881Speter                    svn_wc__db_status_t working_status,
1162251881Speter                    svn_boolean_t exists_in_repos,
1163251881Speter                    svn_node_kind_t expected_kind,
1164251881Speter                    svn_wc_conflict_action_t action,
1165251881Speter                    apr_pool_t *result_pool,
1166251881Speter                    apr_pool_t *scratch_pool);
1167251881Speter
1168251881Speter/* An svn_delta_editor_t function. */
1169251881Speterstatic svn_error_t *
1170251881Speteropen_root(void *edit_baton,
1171251881Speter          svn_revnum_t base_revision, /* This is ignored in co */
1172251881Speter          apr_pool_t *pool,
1173251881Speter          void **dir_baton)
1174251881Speter{
1175251881Speter  struct edit_baton *eb = edit_baton;
1176251881Speter  struct dir_baton *db;
1177251881Speter  svn_boolean_t already_conflicted, conflict_ignored;
1178251881Speter  svn_error_t *err;
1179251881Speter  svn_wc__db_status_t status;
1180251881Speter  svn_wc__db_status_t base_status;
1181251881Speter  svn_node_kind_t kind;
1182251881Speter  svn_boolean_t have_work;
1183251881Speter
1184251881Speter  /* Note that something interesting is actually happening in this
1185251881Speter     edit run. */
1186251881Speter  eb->root_opened = TRUE;
1187251881Speter
1188251881Speter  SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1189251881Speter  *dir_baton = db;
1190251881Speter
1191251881Speter  err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1192251881Speter                                   eb->db, db->local_abspath, pool);
1193251881Speter
1194251881Speter  if (err)
1195251881Speter    {
1196251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1197251881Speter        return svn_error_trace(err);
1198251881Speter
1199251881Speter      svn_error_clear(err);
1200251881Speter      already_conflicted = conflict_ignored = FALSE;
1201251881Speter    }
1202251881Speter  else if (already_conflicted)
1203251881Speter    {
1204251881Speter      /* Record a skip of both the anchor and target in the skipped tree
1205251881Speter         as the anchor itself might not be updated */
1206251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1207251881Speter      SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1208251881Speter
1209251881Speter      db->skip_this = TRUE;
1210251881Speter      db->already_notified = TRUE;
1211251881Speter
1212251881Speter      /* Notify that we skipped the target, while we actually skipped
1213251881Speter         the anchor */
1214251881Speter      do_notification(eb, eb->target_abspath, svn_node_unknown,
1215251881Speter                      svn_wc_notify_skip_conflicted, pool);
1216251881Speter
1217251881Speter      return SVN_NO_ERROR;
1218251881Speter    }
1219251881Speter
1220251881Speter
1221251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1222251881Speter                               &db->old_repos_relpath, NULL, NULL,
1223251881Speter                               &db->changed_rev, &db->changed_date,
1224251881Speter                               &db->changed_author, &db->ambient_depth,
1225251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1226251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1227251881Speter                               NULL, NULL, &have_work,
1228251881Speter                               eb->db, db->local_abspath,
1229251881Speter                               db->pool, pool));
1230251881Speter
1231251881Speter  if (conflict_ignored)
1232251881Speter    {
1233251881Speter      db->shadowed = TRUE;
1234251881Speter    }
1235251881Speter  else if (have_work)
1236251881Speter    {
1237251881Speter      const char *move_src_root_abspath;
1238251881Speter
1239251881Speter      SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
1240251881Speter                                       NULL, eb->db, db->local_abspath,
1241251881Speter                                       pool, pool));
1242251881Speter      if (move_src_root_abspath || *eb->target_basename == '\0')
1243251881Speter        SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1244251881Speter                                         &db->old_revision,
1245251881Speter                                         &db->old_repos_relpath, NULL, NULL,
1246251881Speter                                         &db->changed_rev, &db->changed_date,
1247251881Speter                                         &db->changed_author,
1248251881Speter                                         &db->ambient_depth,
1249251881Speter                                         NULL, NULL, NULL, NULL, NULL, NULL,
1250251881Speter                                         eb->db, db->local_abspath,
1251251881Speter                                         db->pool, pool));
1252251881Speter
1253251881Speter      if (move_src_root_abspath)
1254251881Speter        {
1255251881Speter          /* This is an update anchored inside a move. We need to
1256251881Speter             raise a move-edit tree-conflict on the move root to
1257251881Speter             update the move destination. */
1258251881Speter          svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1259251881Speter
1260251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1261251881Speter                    tree_conflict, eb->db, move_src_root_abspath,
1262251881Speter                    svn_wc_conflict_reason_moved_away,
1263251881Speter                    svn_wc_conflict_action_edit,
1264251881Speter                    move_src_root_abspath, pool, pool));
1265251881Speter
1266251881Speter          if (strcmp(db->local_abspath, move_src_root_abspath))
1267251881Speter            {
1268251881Speter              /* We are raising the tree-conflict on some parent of
1269251881Speter                 the edit root, we won't be handling that path again
1270251881Speter                 so raise the conflict now. */
1271251881Speter              SVN_ERR(complete_conflict(tree_conflict, eb,
1272251881Speter                                        move_src_root_abspath,
1273251881Speter                                        db->old_repos_relpath,
1274251881Speter                                        db->old_revision, db->new_relpath,
1275251881Speter                                        svn_node_dir, svn_node_dir,
1276251881Speter                                        pool, pool));
1277251881Speter              SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1278251881Speter                                                  move_src_root_abspath,
1279251881Speter                                                  tree_conflict,
1280251881Speter                                                  NULL, pool));
1281251881Speter              do_notification(eb, move_src_root_abspath, svn_node_dir,
1282251881Speter                              svn_wc_notify_tree_conflict, pool);
1283251881Speter            }
1284251881Speter          else
1285251881Speter            db->edit_conflict = tree_conflict;
1286251881Speter        }
1287251881Speter
1288251881Speter      db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1289251881Speter                              make sure it doesn't use the ACTUAL tree */
1290251881Speter    }
1291251881Speter  else
1292251881Speter    base_status = status;
1293251881Speter
1294251881Speter  if (*eb->target_basename == '\0')
1295251881Speter    {
1296251881Speter      /* For an update with a NULL target, this is equivalent to open_dir(): */
1297251881Speter
1298251881Speter      db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1299251881Speter
1300251881Speter      /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1301251881Speter                   open_directory() does.
1302251881Speter                   (or find a way to reuse that code here)
1303251881Speter
1304251881Speter         ### BH 2013: I don't think we need all of the detection here, as the
1305251881Speter                      user explicitly asked to update this node. So we don't
1306251881Speter                      have to tell that it is a local replacement/delete.
1307251881Speter       */
1308251881Speter
1309251881Speter      SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1310251881Speter                                                        db->local_abspath,
1311251881Speter                                                        db->new_relpath,
1312251881Speter                                                        *eb->target_revision,
1313251881Speter                                                        pool));
1314251881Speter    }
1315251881Speter
1316251881Speter  return SVN_NO_ERROR;
1317251881Speter}
1318251881Speter
1319251881Speter
1320251881Speter/* ===================================================================== */
1321251881Speter/* Checking for local modifications. */
1322251881Speter
1323251881Speter/* A baton for use with modcheck_found_entry(). */
1324251881Spetertypedef struct modcheck_baton_t {
1325251881Speter  svn_wc__db_t *db;         /* wc_db to access nodes */
1326251881Speter  svn_boolean_t found_mod;  /* whether a modification has been found */
1327251881Speter  svn_boolean_t found_not_delete;  /* Found a not-delete modification */
1328251881Speter} modcheck_baton_t;
1329251881Speter
1330251881Speter/* An implementation of svn_wc_status_func4_t. */
1331251881Speterstatic svn_error_t *
1332251881Spetermodcheck_callback(void *baton,
1333251881Speter                  const char *local_abspath,
1334251881Speter                  const svn_wc_status3_t *status,
1335251881Speter                  apr_pool_t *scratch_pool)
1336251881Speter{
1337251881Speter  modcheck_baton_t *mb = baton;
1338251881Speter
1339251881Speter  switch (status->node_status)
1340251881Speter    {
1341251881Speter      case svn_wc_status_normal:
1342251881Speter      case svn_wc_status_incomplete:
1343251881Speter      case svn_wc_status_ignored:
1344251881Speter      case svn_wc_status_none:
1345251881Speter      case svn_wc_status_unversioned:
1346251881Speter      case svn_wc_status_external:
1347251881Speter        break;
1348251881Speter
1349251881Speter      case svn_wc_status_deleted:
1350251881Speter        mb->found_mod = TRUE;
1351251881Speter        break;
1352251881Speter
1353251881Speter      case svn_wc_status_missing:
1354251881Speter      case svn_wc_status_obstructed:
1355251881Speter        mb->found_mod = TRUE;
1356251881Speter        mb->found_not_delete = TRUE;
1357251881Speter        /* Exit from the status walker: We know what we want to know */
1358251881Speter        return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1359251881Speter
1360251881Speter      default:
1361251881Speter      case svn_wc_status_added:
1362251881Speter      case svn_wc_status_replaced:
1363251881Speter      case svn_wc_status_modified:
1364251881Speter        mb->found_mod = TRUE;
1365251881Speter        mb->found_not_delete = TRUE;
1366251881Speter        /* Exit from the status walker: We know what we want to know */
1367251881Speter        return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1368251881Speter    }
1369251881Speter
1370251881Speter  return SVN_NO_ERROR;
1371251881Speter}
1372251881Speter
1373251881Speter
1374251881Speter/* Set *MODIFIED to true iff there are any local modifications within the
1375251881Speter * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
1376251881Speter * is set to true and all the local modifications were deletes then set
1377251881Speter * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise.  LOCAL_ABSPATH
1378251881Speter * may be a file or a directory. */
1379251881Spetersvn_error_t *
1380251881Spetersvn_wc__node_has_local_mods(svn_boolean_t *modified,
1381251881Speter                            svn_boolean_t *all_edits_are_deletes,
1382251881Speter                            svn_wc__db_t *db,
1383251881Speter                            const char *local_abspath,
1384251881Speter                            svn_cancel_func_t cancel_func,
1385251881Speter                            void *cancel_baton,
1386251881Speter                            apr_pool_t *scratch_pool)
1387251881Speter{
1388251881Speter  modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
1389251881Speter  svn_error_t *err;
1390251881Speter
1391251881Speter  modcheck_baton.db = db;
1392251881Speter
1393251881Speter  /* Walk the WC tree for status with depth infinity, looking for any local
1394251881Speter   * modifications. If it's a "sparse" directory, that's OK: there can be
1395251881Speter   * no local mods in the pieces that aren't present in the WC. */
1396251881Speter
1397251881Speter  err = svn_wc__internal_walk_status(db, local_abspath,
1398251881Speter                                     svn_depth_infinity,
1399251881Speter                                     FALSE, FALSE, FALSE, NULL,
1400251881Speter                                     modcheck_callback, &modcheck_baton,
1401251881Speter                                     cancel_func, cancel_baton,
1402251881Speter                                     scratch_pool);
1403251881Speter
1404251881Speter  if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
1405251881Speter    svn_error_clear(err);
1406251881Speter  else
1407251881Speter    SVN_ERR(err);
1408251881Speter
1409251881Speter  *modified = modcheck_baton.found_mod;
1410251881Speter  *all_edits_are_deletes = (modcheck_baton.found_mod
1411251881Speter                            && !modcheck_baton.found_not_delete);
1412251881Speter
1413251881Speter  return SVN_NO_ERROR;
1414251881Speter}
1415251881Speter
1416251881Speter/* Indicates an unset svn_wc_conflict_reason_t. */
1417251881Speter#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1418251881Speter
1419251881Speter/* Check whether the incoming change ACTION on FULL_PATH would conflict with
1420251881Speter * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1421251881Speter * LOCAL_ABSPATH as the victim.
1422251881Speter *
1423251881Speter * The edit baton EB gives information including whether the operation is
1424251881Speter * an update or a switch.
1425251881Speter *
1426251881Speter * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1427251881Speter * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1428251881Speter * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1429251881Speter *
1430251881Speter * If a tree conflict reason was found for the incoming action, the resulting
1431251881Speter * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1432251881Speter * while *PCONFLICT is always overwritten.
1433251881Speter *
1434251881Speter * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1435251881Speter * SCRATCH_POOL.
1436251881Speter */
1437251881Speterstatic svn_error_t *
1438251881Spetercheck_tree_conflict(svn_skel_t **pconflict,
1439251881Speter                    struct edit_baton *eb,
1440251881Speter                    const char *local_abspath,
1441251881Speter                    svn_wc__db_status_t working_status,
1442251881Speter                    svn_boolean_t exists_in_repos,
1443251881Speter                    svn_node_kind_t expected_kind,
1444251881Speter                    svn_wc_conflict_action_t action,
1445251881Speter                    apr_pool_t *result_pool,
1446251881Speter                    apr_pool_t *scratch_pool)
1447251881Speter{
1448251881Speter  svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1449251881Speter  svn_boolean_t modified = FALSE;
1450251881Speter  svn_boolean_t all_mods_are_deletes = FALSE;
1451251881Speter  const char *move_src_op_root_abspath = NULL;
1452251881Speter
1453251881Speter  *pconflict = NULL;
1454251881Speter
1455251881Speter  /* Find out if there are any local changes to this node that may
1456251881Speter   * be the "reason" of a tree-conflict with the incoming "action". */
1457251881Speter  switch (working_status)
1458251881Speter    {
1459251881Speter      case svn_wc__db_status_added:
1460251881Speter      case svn_wc__db_status_moved_here:
1461251881Speter      case svn_wc__db_status_copied:
1462251881Speter        if (!exists_in_repos)
1463251881Speter          {
1464251881Speter            /* The node is locally added, and it did not exist before.  This
1465251881Speter             * is an 'update', so the local add can only conflict with an
1466251881Speter             * incoming 'add'.  In fact, if we receive anything else than an
1467251881Speter             * svn_wc_conflict_action_add (which includes 'added',
1468251881Speter             * 'copied-here' and 'moved-here') during update on a node that
1469251881Speter             * did not exist before, then something is very wrong.
1470251881Speter             * Note that if there was no action on the node, this code
1471251881Speter             * would not have been called in the first place. */
1472251881Speter            SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1473251881Speter
1474251881Speter            /* Scan the addition in case our caller didn't. */
1475251881Speter            if (working_status == svn_wc__db_status_added)
1476251881Speter              SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1477251881Speter                                               NULL, NULL, NULL, NULL,
1478251881Speter                                               NULL, NULL,
1479251881Speter                                               eb->db, local_abspath,
1480251881Speter                                               scratch_pool, scratch_pool));
1481251881Speter
1482251881Speter            if (working_status == svn_wc__db_status_moved_here)
1483251881Speter              reason = svn_wc_conflict_reason_moved_here;
1484251881Speter            else
1485251881Speter              reason = svn_wc_conflict_reason_added;
1486251881Speter          }
1487251881Speter        else
1488251881Speter          {
1489251881Speter            /* The node is locally replaced but could also be moved-away. */
1490251881Speter            SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1491251881Speter                                             &move_src_op_root_abspath,
1492251881Speter                                             eb->db, local_abspath,
1493251881Speter                                             scratch_pool, scratch_pool));
1494251881Speter            if (move_src_op_root_abspath)
1495251881Speter              reason = svn_wc_conflict_reason_moved_away;
1496251881Speter            else
1497251881Speter              reason = svn_wc_conflict_reason_replaced;
1498251881Speter          }
1499251881Speter        break;
1500251881Speter
1501251881Speter
1502251881Speter      case svn_wc__db_status_deleted:
1503251881Speter        {
1504251881Speter          SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1505251881Speter                                           &move_src_op_root_abspath,
1506251881Speter                                           eb->db, local_abspath,
1507251881Speter                                           scratch_pool, scratch_pool));
1508251881Speter          if (move_src_op_root_abspath)
1509251881Speter            reason = svn_wc_conflict_reason_moved_away;
1510251881Speter          else
1511251881Speter            reason = svn_wc_conflict_reason_deleted;
1512251881Speter        }
1513251881Speter        break;
1514251881Speter
1515251881Speter      case svn_wc__db_status_incomplete:
1516251881Speter        /* We used svn_wc__db_read_info(), so 'incomplete' means
1517251881Speter         * - there is no node in the WORKING tree
1518251881Speter         * - a BASE node is known to exist
1519251881Speter         * So the node exists and is essentially 'normal'. We still need to
1520251881Speter         * check prop and text mods, and those checks will retrieve the
1521251881Speter         * missing information (hopefully). */
1522251881Speter      case svn_wc__db_status_normal:
1523251881Speter        if (action == svn_wc_conflict_action_edit)
1524251881Speter          {
1525251881Speter            /* An edit onto a local edit or onto *no* local changes is no
1526251881Speter             * tree-conflict. (It's possibly a text- or prop-conflict,
1527251881Speter             * but we don't handle those here.)
1528251881Speter             *
1529251881Speter             * Except when there is a local obstruction
1530251881Speter             */
1531251881Speter            if (exists_in_repos)
1532251881Speter              {
1533251881Speter                svn_node_kind_t disk_kind;
1534251881Speter
1535251881Speter                SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1536251881Speter                                          scratch_pool));
1537251881Speter
1538251881Speter                if (disk_kind != expected_kind && disk_kind != svn_node_none)
1539251881Speter                  {
1540251881Speter                    reason = svn_wc_conflict_reason_obstructed;
1541251881Speter                    break;
1542251881Speter                  }
1543251881Speter
1544251881Speter              }
1545251881Speter            return SVN_NO_ERROR;
1546251881Speter          }
1547251881Speter
1548251881Speter        /* Replace is handled as delete and then specifically in
1549251881Speter           add_directory() and add_file(), so we only expect deletes here */
1550251881Speter        SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1551251881Speter
1552251881Speter        /* Check if the update wants to delete or replace a locally
1553251881Speter         * modified node. */
1554251881Speter
1555251881Speter
1556251881Speter        /* Do a deep tree detection of local changes. The update editor will
1557251881Speter         * not visit the subdirectories of a directory that it wants to delete.
1558251881Speter         * Therefore, we need to start a separate crawl here. */
1559251881Speter
1560251881Speter        SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
1561251881Speter                                            eb->db, local_abspath,
1562251881Speter                                            eb->cancel_func, eb->cancel_baton,
1563251881Speter                                            scratch_pool));
1564251881Speter
1565251881Speter        if (modified)
1566251881Speter          {
1567251881Speter            if (all_mods_are_deletes)
1568251881Speter              reason = svn_wc_conflict_reason_deleted;
1569251881Speter            else
1570251881Speter              reason = svn_wc_conflict_reason_edited;
1571251881Speter          }
1572251881Speter        break;
1573251881Speter
1574251881Speter      case svn_wc__db_status_server_excluded:
1575251881Speter        /* Not allowed to view the node. Not allowed to report tree
1576251881Speter         * conflicts. */
1577251881Speter      case svn_wc__db_status_excluded:
1578251881Speter        /* Locally marked as excluded. No conflicts wanted. */
1579251881Speter      case svn_wc__db_status_not_present:
1580251881Speter        /* A committed delete (but parent not updated). The delete is
1581251881Speter           committed, so no conflict possible during update. */
1582251881Speter        return SVN_NO_ERROR;
1583251881Speter
1584251881Speter      case svn_wc__db_status_base_deleted:
1585251881Speter        /* An internal status. Should never show up here. */
1586251881Speter        SVN_ERR_MALFUNCTION();
1587251881Speter        break;
1588251881Speter
1589251881Speter    }
1590251881Speter
1591251881Speter  if (reason == SVN_WC_CONFLICT_REASON_NONE)
1592251881Speter    /* No conflict with the current action. */
1593251881Speter    return SVN_NO_ERROR;
1594251881Speter
1595251881Speter
1596251881Speter  /* Sanity checks. Note that if there was no action on the node, this function
1597251881Speter   * would not have been called in the first place.*/
1598251881Speter  if (reason == svn_wc_conflict_reason_edited
1599251881Speter      || reason == svn_wc_conflict_reason_obstructed
1600251881Speter      || reason == svn_wc_conflict_reason_deleted
1601251881Speter      || reason == svn_wc_conflict_reason_moved_away
1602251881Speter      || reason == svn_wc_conflict_reason_replaced)
1603251881Speter    {
1604251881Speter      /* When the node existed before (it was locally deleted, replaced or
1605251881Speter       * edited), then 'update' cannot add it "again". So it can only send
1606251881Speter       * _action_edit, _delete or _replace. */
1607251881Speter    if (action != svn_wc_conflict_action_edit
1608251881Speter        && action != svn_wc_conflict_action_delete
1609251881Speter        && action != svn_wc_conflict_action_replace)
1610251881Speter      return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1611251881Speter               _("Unexpected attempt to add a node at path '%s'"),
1612251881Speter               svn_dirent_local_style(local_abspath, scratch_pool));
1613251881Speter    }
1614251881Speter  else if (reason == svn_wc_conflict_reason_added ||
1615251881Speter           reason == svn_wc_conflict_reason_moved_here)
1616251881Speter    {
1617251881Speter      /* When the node did not exist before (it was locally added),
1618251881Speter       * then 'update' cannot want to modify it in any way.
1619251881Speter       * It can only send _action_add. */
1620251881Speter      if (action != svn_wc_conflict_action_add)
1621251881Speter        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1622251881Speter                 _("Unexpected attempt to edit, delete, or replace "
1623251881Speter                   "a node at path '%s'"),
1624251881Speter                 svn_dirent_local_style(local_abspath, scratch_pool));
1625251881Speter
1626251881Speter    }
1627251881Speter
1628251881Speter
1629251881Speter  /* A conflict was detected. Create a conflict skel to record it. */
1630251881Speter  *pconflict = svn_wc__conflict_skel_create(result_pool);
1631251881Speter
1632251881Speter  SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1633251881Speter                                                  eb->db, local_abspath,
1634251881Speter                                                  reason,
1635251881Speter                                                  action,
1636251881Speter                                                  move_src_op_root_abspath,
1637251881Speter                                                  result_pool, scratch_pool));
1638251881Speter
1639251881Speter  return SVN_NO_ERROR;
1640251881Speter}
1641251881Speter
1642251881Speter
1643251881Speter/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1644251881Speter * not a moved-away-edit conflict, set *CONFLICTED to TRUE.  Otherwise
1645251881Speter * set *CONFLICTED to FALSE.
1646251881Speter */
1647251881Speterstatic svn_error_t *
1648251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted,
1649251881Speter                           svn_boolean_t *ignored,
1650251881Speter                           svn_wc__db_t *db,
1651251881Speter                           const char *local_abspath,
1652251881Speter                           apr_pool_t *scratch_pool)
1653251881Speter{
1654251881Speter  const char *ancestor_abspath = local_abspath;
1655251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1656251881Speter
1657251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1658251881Speter
1659251881Speter  *conflicted = *ignored = FALSE;
1660251881Speter
1661251881Speter  while (TRUE)
1662251881Speter    {
1663251881Speter      svn_boolean_t is_wc_root;
1664251881Speter
1665251881Speter      svn_pool_clear(iterpool);
1666251881Speter
1667251881Speter      SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1668251881Speter                                              ancestor_abspath, TRUE,
1669251881Speter                                              scratch_pool));
1670251881Speter      if (*conflicted || *ignored)
1671251881Speter        break;
1672251881Speter
1673251881Speter      SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1674251881Speter                                   iterpool));
1675251881Speter      if (is_wc_root)
1676251881Speter        break;
1677251881Speter
1678251881Speter      ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1679251881Speter    }
1680251881Speter
1681251881Speter  svn_pool_destroy(iterpool);
1682251881Speter
1683251881Speter  return SVN_NO_ERROR;
1684251881Speter}
1685251881Speter
1686251881Speter/* Temporary helper until the new conflict handling is in place */
1687251881Speterstatic svn_error_t *
1688251881Speternode_already_conflicted(svn_boolean_t *conflicted,
1689251881Speter                        svn_boolean_t *conflict_ignored,
1690251881Speter                        svn_wc__db_t *db,
1691251881Speter                        const char *local_abspath,
1692251881Speter                        apr_pool_t *scratch_pool)
1693251881Speter{
1694251881Speter  SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1695251881Speter                                          local_abspath, FALSE,
1696251881Speter                                          scratch_pool));
1697251881Speter
1698251881Speter  return SVN_NO_ERROR;
1699251881Speter}
1700251881Speter
1701251881Speter
1702251881Speter/* An svn_delta_editor_t function. */
1703251881Speterstatic svn_error_t *
1704251881Speterdelete_entry(const char *path,
1705251881Speter             svn_revnum_t revision,
1706251881Speter             void *parent_baton,
1707251881Speter             apr_pool_t *pool)
1708251881Speter{
1709251881Speter  struct dir_baton *pb = parent_baton;
1710251881Speter  struct edit_baton *eb = pb->edit_baton;
1711251881Speter  const char *base = svn_relpath_basename(path, NULL);
1712251881Speter  const char *local_abspath;
1713251881Speter  const char *repos_relpath;
1714251881Speter  svn_node_kind_t kind, base_kind;
1715251881Speter  svn_revnum_t old_revision;
1716251881Speter  svn_boolean_t conflicted;
1717251881Speter  svn_boolean_t have_work;
1718251881Speter  svn_skel_t *tree_conflict = NULL;
1719251881Speter  svn_wc__db_status_t status;
1720251881Speter  svn_wc__db_status_t base_status;
1721251881Speter  apr_pool_t *scratch_pool;
1722251881Speter  svn_boolean_t deleting_target;
1723251881Speter  svn_boolean_t deleting_switched;
1724251881Speter  svn_boolean_t keep_as_working = FALSE;
1725251881Speter  svn_boolean_t queue_deletes = TRUE;
1726251881Speter
1727251881Speter  if (pb->skip_this)
1728251881Speter    return SVN_NO_ERROR;
1729251881Speter
1730251881Speter  scratch_pool = svn_pool_create(pb->pool);
1731251881Speter
1732251881Speter  SVN_ERR(mark_directory_edited(pb, scratch_pool));
1733251881Speter
1734251881Speter  SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1735251881Speter                               scratch_pool));
1736251881Speter
1737251881Speter  deleting_target =  (strcmp(local_abspath, eb->target_abspath) == 0);
1738251881Speter
1739251881Speter  /* Detect obstructing working copies */
1740251881Speter  {
1741251881Speter    svn_boolean_t is_root;
1742251881Speter
1743251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1744251881Speter                                 scratch_pool));
1745251881Speter
1746251881Speter    if (is_root)
1747251881Speter      {
1748251881Speter        /* Just skip this node; a future update will handle it */
1749251881Speter        SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1750251881Speter        do_notification(eb, local_abspath, svn_node_unknown,
1751251881Speter                        svn_wc_notify_update_skip_obstruction, scratch_pool);
1752251881Speter
1753251881Speter        svn_pool_destroy(scratch_pool);
1754251881Speter
1755251881Speter        return SVN_NO_ERROR;
1756251881Speter      }
1757251881Speter  }
1758251881Speter
1759251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1760251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1761251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1762251881Speter                               &conflicted, NULL, NULL, NULL,
1763251881Speter                               NULL, NULL, &have_work,
1764251881Speter                               eb->db, local_abspath,
1765251881Speter                               scratch_pool, scratch_pool));
1766251881Speter
1767251881Speter  if (!have_work)
1768251881Speter    {
1769251881Speter      base_status = status;
1770251881Speter      base_kind = kind;
1771251881Speter    }
1772251881Speter  else
1773251881Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
1774251881Speter                                     &repos_relpath,
1775251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1776251881Speter                                     NULL, NULL, NULL, NULL, NULL,
1777251881Speter                                     eb->db, local_abspath,
1778251881Speter                                     scratch_pool, scratch_pool));
1779251881Speter
1780251881Speter  if (pb->old_repos_relpath && repos_relpath)
1781251881Speter    {
1782251881Speter      const char *expected_name;
1783251881Speter
1784251881Speter      expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1785251881Speter                                                repos_relpath);
1786251881Speter
1787251881Speter      deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1788251881Speter    }
1789251881Speter  else
1790251881Speter    deleting_switched = FALSE;
1791251881Speter
1792251881Speter  /* Is this path a conflict victim? */
1793251881Speter  if (pb->shadowed)
1794251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
1795251881Speter  else if (conflicted)
1796251881Speter    SVN_ERR(node_already_conflicted(&conflicted, NULL,
1797251881Speter                                    eb->db, local_abspath, scratch_pool));
1798251881Speter  if (conflicted)
1799251881Speter    {
1800251881Speter      SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1801251881Speter
1802251881Speter      do_notification(eb, local_abspath, svn_node_unknown,
1803251881Speter                      svn_wc_notify_skip_conflicted,
1804251881Speter                      scratch_pool);
1805251881Speter
1806251881Speter      svn_pool_destroy(scratch_pool);
1807251881Speter
1808251881Speter      return SVN_NO_ERROR;
1809251881Speter    }
1810251881Speter
1811251881Speter
1812251881Speter  /* Receive the remote removal of excluded/server-excluded/not present node.
1813251881Speter     Do not notify, but perform the change even when the node is shadowed */
1814251881Speter  if (base_status == svn_wc__db_status_not_present
1815251881Speter      || base_status == svn_wc__db_status_excluded
1816251881Speter      || base_status == svn_wc__db_status_server_excluded)
1817251881Speter    {
1818251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1819251881Speter                                     FALSE /* keep_as_working */,
1820251881Speter                                     FALSE /* queue_deletes */,
1821253734Speter                                     FALSE /* remove_locks */,
1822251881Speter                                     SVN_INVALID_REVNUM /* not_present_rev */,
1823251881Speter                                     NULL, NULL,
1824251881Speter                                     scratch_pool));
1825251881Speter
1826251881Speter      if (deleting_target)
1827251881Speter        eb->target_deleted = TRUE;
1828251881Speter
1829251881Speter      svn_pool_destroy(scratch_pool);
1830251881Speter
1831251881Speter      return SVN_NO_ERROR;
1832251881Speter    }
1833251881Speter
1834251881Speter  /* Is this path the victim of a newly-discovered tree conflict?  If so,
1835251881Speter   * remember it and notify the client. Then (if it was existing and
1836251881Speter   * modified), re-schedule the node to be added back again, as a (modified)
1837251881Speter   * copy of the previous base version.  */
1838251881Speter
1839251881Speter  /* Check for conflicts only when we haven't already recorded
1840251881Speter   * a tree-conflict on a parent node. */
1841251881Speter  if (!pb->shadowed && !pb->edit_obstructed)
1842251881Speter    {
1843251881Speter      SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1844251881Speter                                  status, TRUE,
1845251881Speter                                  (kind == svn_node_dir)
1846251881Speter                                        ? svn_node_dir
1847251881Speter                                        : svn_node_file,
1848251881Speter                                  svn_wc_conflict_action_delete,
1849251881Speter                                  pb->pool, scratch_pool));
1850251881Speter    }
1851251881Speter  else
1852251881Speter    queue_deletes = FALSE; /* There is no in-wc representation */
1853251881Speter
1854251881Speter  if (tree_conflict != NULL)
1855251881Speter    {
1856251881Speter      svn_wc_conflict_reason_t reason;
1857251881Speter      /* When we raise a tree conflict on a node, we don't want to mark the
1858251881Speter       * node as skipped, to allow a replacement to continue doing at least
1859251881Speter       * a bit of its work (possibly adding a not present node, for the
1860251881Speter       * next update) */
1861251881Speter      if (!pb->deletion_conflicts)
1862251881Speter        pb->deletion_conflicts = apr_hash_make(pb->pool);
1863251881Speter
1864251881Speter      svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1865251881Speter                    tree_conflict);
1866251881Speter
1867251881Speter      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
1868251881Speter                                                  eb->db, local_abspath,
1869251881Speter                                                  tree_conflict,
1870251881Speter                                                  scratch_pool, scratch_pool));
1871251881Speter
1872251881Speter      if (reason == svn_wc_conflict_reason_edited
1873251881Speter          || reason == svn_wc_conflict_reason_obstructed)
1874251881Speter        {
1875251881Speter          /* The item exists locally and has some sort of local mod.
1876251881Speter           * It no longer exists in the repository at its target URL@REV.
1877251881Speter           *
1878251881Speter           * To prepare the "accept mine" resolution for the tree conflict,
1879251881Speter           * we must schedule the existing content for re-addition as a copy
1880251881Speter           * of what it was, but with its local modifications preserved. */
1881251881Speter          keep_as_working = TRUE;
1882251881Speter
1883251881Speter          /* Fall through to remove the BASE_NODEs properly, with potentially
1884251881Speter             keeping a not-present marker */
1885251881Speter        }
1886251881Speter      else if (reason == svn_wc_conflict_reason_deleted
1887251881Speter               || reason == svn_wc_conflict_reason_moved_away
1888251881Speter               || reason == svn_wc_conflict_reason_replaced)
1889251881Speter        {
1890251881Speter          /* The item does not exist locally because it was already shadowed.
1891251881Speter           * We must complete the deletion, leaving the tree conflict info
1892251881Speter           * as the only difference from a normal deletion. */
1893251881Speter
1894251881Speter          /* Fall through to the normal "delete" code path. */
1895251881Speter        }
1896251881Speter      else
1897251881Speter        SVN_ERR_MALFUNCTION();  /* other reasons are not expected here */
1898251881Speter    }
1899251881Speter
1900251881Speter  SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1901251881Speter                            old_revision, NULL,
1902251881Speter                            (kind == svn_node_dir)
1903251881Speter                                ? svn_node_dir
1904251881Speter                                : svn_node_file,
1905251881Speter                            svn_node_none,
1906251881Speter                            pb->pool, scratch_pool));
1907251881Speter
1908251881Speter  /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1909251881Speter     nodes based on that from disk, but leave any WORKING_NODEs on disk.
1910251881Speter
1911251881Speter     Local modifications are already turned into copies at this point.
1912251881Speter
1913251881Speter     If the thing being deleted is the *target* of this update, then
1914251881Speter     we need to recreate a 'deleted' entry, so that the parent can give
1915251881Speter     accurate reports about itself in the future. */
1916251881Speter  if (! deleting_target && ! deleting_switched)
1917251881Speter    {
1918251881Speter      /* Delete, and do not leave a not-present node.  */
1919251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1920253734Speter                                     keep_as_working, queue_deletes, FALSE,
1921251881Speter                                     SVN_INVALID_REVNUM /* not_present_rev */,
1922251881Speter                                     tree_conflict, NULL,
1923251881Speter                                     scratch_pool));
1924251881Speter    }
1925251881Speter  else
1926251881Speter    {
1927251881Speter      /* Delete, leaving a not-present node.  */
1928251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1929253734Speter                                     keep_as_working, queue_deletes, FALSE,
1930251881Speter                                     *eb->target_revision,
1931251881Speter                                     tree_conflict, NULL,
1932251881Speter                                     scratch_pool));
1933251881Speter      if (deleting_target)
1934251881Speter        eb->target_deleted = TRUE;
1935251881Speter      else
1936251881Speter        {
1937251881Speter          /* Don't remove the not-present marker at the final bump */
1938251881Speter          SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1939251881Speter        }
1940251881Speter    }
1941251881Speter
1942251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1943251881Speter                         eb->cancel_func, eb->cancel_baton,
1944251881Speter                         scratch_pool));
1945251881Speter
1946251881Speter  /* Notify. */
1947251881Speter  if (tree_conflict)
1948253734Speter    {
1949253734Speter      if (eb->conflict_func)
1950253734Speter        SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1951253734Speter                                                 tree_conflict,
1952253734Speter                                                 NULL /* merge_options */,
1953253734Speter                                                 eb->conflict_func,
1954253734Speter                                                 eb->conflict_baton,
1955253734Speter                                                 eb->cancel_func,
1956253734Speter                                                 eb->cancel_baton,
1957253734Speter                                                 scratch_pool));
1958253734Speter      do_notification(eb, local_abspath, svn_node_unknown,
1959253734Speter                      svn_wc_notify_tree_conflict, scratch_pool);
1960253734Speter    }
1961251881Speter  else
1962251881Speter    {
1963251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1964251881Speter      svn_node_kind_t node_kind;
1965251881Speter
1966251881Speter      if (pb->shadowed || pb->edit_obstructed)
1967251881Speter        action = svn_wc_notify_update_shadowed_delete;
1968251881Speter
1969251881Speter      if (kind == svn_node_dir)
1970251881Speter        node_kind = svn_node_dir;
1971251881Speter      else
1972251881Speter        node_kind = svn_node_file;
1973251881Speter
1974251881Speter      do_notification(eb, local_abspath, node_kind, action, scratch_pool);
1975251881Speter    }
1976251881Speter
1977251881Speter  svn_pool_destroy(scratch_pool);
1978251881Speter
1979251881Speter  return SVN_NO_ERROR;
1980251881Speter}
1981251881Speter
1982251881Speter/* An svn_delta_editor_t function. */
1983251881Speterstatic svn_error_t *
1984251881Speteradd_directory(const char *path,
1985251881Speter              void *parent_baton,
1986251881Speter              const char *copyfrom_path,
1987251881Speter              svn_revnum_t copyfrom_rev,
1988251881Speter              apr_pool_t *pool,
1989251881Speter              void **child_baton)
1990251881Speter{
1991251881Speter  struct dir_baton *pb = parent_baton;
1992251881Speter  struct edit_baton *eb = pb->edit_baton;
1993251881Speter  struct dir_baton *db;
1994251881Speter  svn_node_kind_t kind;
1995251881Speter  svn_wc__db_status_t status;
1996251881Speter  svn_node_kind_t wc_kind;
1997251881Speter  svn_boolean_t conflicted;
1998251881Speter  svn_boolean_t conflict_ignored = FALSE;
1999251881Speter  svn_boolean_t versioned_locally_and_present;
2000251881Speter  svn_skel_t *tree_conflict = NULL;
2001251881Speter  svn_error_t *err;
2002251881Speter
2003251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
2004251881Speter
2005251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
2006251881Speter  *child_baton = db;
2007251881Speter
2008251881Speter  if (db->skip_this)
2009251881Speter    return SVN_NO_ERROR;
2010251881Speter
2011251881Speter  SVN_ERR(mark_directory_edited(db, pool));
2012251881Speter
2013251881Speter  if (strcmp(eb->target_abspath, db->local_abspath) == 0)
2014251881Speter    {
2015251881Speter      /* The target of the edit is being added, give it the requested
2016251881Speter         depth of the edit (but convert svn_depth_unknown to
2017251881Speter         svn_depth_infinity). */
2018251881Speter      db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
2019251881Speter        ? svn_depth_infinity : eb->requested_depth;
2020251881Speter    }
2021251881Speter  else if (eb->requested_depth == svn_depth_immediates
2022251881Speter           || (eb->requested_depth == svn_depth_unknown
2023251881Speter               && pb->ambient_depth == svn_depth_immediates))
2024251881Speter    {
2025251881Speter      db->ambient_depth = svn_depth_empty;
2026251881Speter    }
2027251881Speter  else
2028251881Speter    {
2029251881Speter      db->ambient_depth = svn_depth_infinity;
2030251881Speter    }
2031251881Speter
2032251881Speter  /* It may not be named the same as the administrative directory. */
2033251881Speter  if (svn_wc_is_adm_dir(db->name, pool))
2034251881Speter    return svn_error_createf(
2035251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
2036251881Speter       _("Failed to add directory '%s': object of the same name as the "
2037251881Speter         "administrative directory"),
2038251881Speter       svn_dirent_local_style(db->local_abspath, pool));
2039251881Speter
2040251881Speter  SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
2041251881Speter
2042251881Speter  err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
2043251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2044251881Speter                             NULL, NULL, NULL, NULL, NULL,
2045251881Speter                             &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
2046251881Speter                             eb->db, db->local_abspath, db->pool, db->pool);
2047251881Speter  if (err)
2048251881Speter    {
2049251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2050251881Speter        return svn_error_trace(err);
2051251881Speter
2052251881Speter      svn_error_clear(err);
2053251881Speter      wc_kind = svn_node_unknown;
2054251881Speter      status = svn_wc__db_status_normal;
2055251881Speter      conflicted = FALSE;
2056251881Speter
2057251881Speter      versioned_locally_and_present = FALSE;
2058251881Speter    }
2059251881Speter  else if (wc_kind == svn_node_dir
2060251881Speter           && status == svn_wc__db_status_normal)
2061251881Speter    {
2062251881Speter      /* !! We found the root of a separate working copy obstructing the wc !!
2063251881Speter
2064251881Speter         If the directory would be part of our own working copy then
2065251881Speter         we wouldn't have been called as an add_directory().
2066251881Speter
2067251881Speter         The only thing we can do is add a not-present node, to allow
2068251881Speter         a future update to bring in the new files when the problem is
2069251881Speter         resolved.  Note that svn_wc__db_base_add_not_present_node()
2070251881Speter         explicitly adds the node into the parent's node database. */
2071251881Speter
2072251881Speter      SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2073251881Speter                                                   db->new_relpath,
2074251881Speter                                                   eb->repos_root,
2075251881Speter                                                   eb->repos_uuid,
2076251881Speter                                                   *eb->target_revision,
2077251881Speter                                                   svn_node_file,
2078251881Speter                                                   NULL, NULL,
2079251881Speter                                                   pool));
2080251881Speter
2081251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2082251881Speter      db->skip_this = TRUE;
2083251881Speter      db->already_notified = TRUE;
2084251881Speter
2085251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2086251881Speter                      svn_wc_notify_update_skip_obstruction, pool);
2087251881Speter
2088251881Speter      return SVN_NO_ERROR;
2089251881Speter    }
2090251881Speter  else if (status == svn_wc__db_status_normal
2091251881Speter           && (wc_kind == svn_node_file
2092251881Speter               || wc_kind == svn_node_symlink))
2093251881Speter    {
2094251881Speter      /* We found a file external occupating the place we need in BASE.
2095251881Speter
2096251881Speter         We can't add a not-present node in this case as that would overwrite
2097251881Speter         the file external. Luckily the file external itself stops us from
2098251881Speter         forgetting a child of this parent directory like an obstructing
2099251881Speter         working copy would.
2100251881Speter
2101251881Speter         The reason we get here is that the adm crawler doesn't report
2102251881Speter         file externals.
2103251881Speter      */
2104251881Speter
2105251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2106251881Speter      db->skip_this = TRUE;
2107251881Speter      db->already_notified = TRUE;
2108251881Speter
2109251881Speter      do_notification(eb, db->local_abspath, svn_node_file,
2110251881Speter                      svn_wc_notify_update_skip_obstruction, pool);
2111251881Speter
2112251881Speter      return SVN_NO_ERROR;
2113251881Speter    }
2114251881Speter  else if (wc_kind == svn_node_unknown)
2115251881Speter    versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
2116251881Speter  else
2117251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
2118251881Speter
2119251881Speter  /* Is this path a conflict victim? */
2120251881Speter  if (conflicted)
2121251881Speter    {
2122251881Speter      if (pb->deletion_conflicts)
2123251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2124251881Speter
2125251881Speter      if (tree_conflict)
2126251881Speter        {
2127251881Speter          svn_wc_conflict_reason_t reason;
2128251881Speter          /* So this deletion wasn't just a deletion, it is actually a
2129251881Speter             replacement. Let's install a better tree conflict. */
2130251881Speter
2131251881Speter          /* ### Should store the conflict in DB to allow reinstalling
2132251881Speter             ### with theoretically more data in close_directory() */
2133251881Speter
2134251881Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2135251881Speter                                                      eb->db,
2136251881Speter                                                      db->local_abspath,
2137251881Speter                                                      tree_conflict,
2138251881Speter                                                      db->pool, db->pool));
2139251881Speter
2140251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2141251881Speter
2142251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2143251881Speter                                        tree_conflict,
2144251881Speter                                        eb->db, db->local_abspath,
2145251881Speter                                        reason, svn_wc_conflict_action_replace,
2146251881Speter                                        NULL,
2147251881Speter                                        db->pool, db->pool));
2148251881Speter
2149251881Speter          /* And now stop checking for conflicts here and just perform
2150251881Speter             a shadowed update */
2151251881Speter          db->edit_conflict = tree_conflict; /* Cache for close_directory */
2152251881Speter          tree_conflict = NULL; /* No direct notification */
2153251881Speter          db->shadowed = TRUE; /* Just continue */
2154251881Speter          conflicted = FALSE; /* No skip */
2155251881Speter        }
2156251881Speter      else
2157251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2158251881Speter                                        eb->db, db->local_abspath, pool));
2159251881Speter    }
2160251881Speter
2161251881Speter  /* Now the "usual" behaviour if already conflicted. Skip it. */
2162251881Speter  if (conflicted)
2163251881Speter    {
2164251881Speter      /* Record this conflict so that its descendants are skipped silently. */
2165251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2166251881Speter
2167251881Speter      db->skip_this = TRUE;
2168251881Speter      db->already_notified = TRUE;
2169251881Speter
2170251881Speter      /* We skip this node, but once the update completes the parent node will
2171251881Speter         be updated to the new revision. So a future recursive update of the
2172251881Speter         parent will not bring in this new node as the revision of the parent
2173251881Speter         describes to the repository that all children are available.
2174251881Speter
2175251881Speter         To resolve this problem, we add a not-present node to allow bringing
2176251881Speter         the node in once this conflict is resolved.
2177251881Speter
2178251881Speter         Note that we can safely assume that no present base node exists,
2179251881Speter         because then we would not have received an add_directory.
2180251881Speter       */
2181251881Speter      SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2182251881Speter                                                   db->new_relpath,
2183251881Speter                                                   eb->repos_root,
2184251881Speter                                                   eb->repos_uuid,
2185251881Speter                                                   *eb->target_revision,
2186251881Speter                                                   svn_node_dir,
2187251881Speter                                                   NULL, NULL,
2188251881Speter                                                   pool));
2189251881Speter
2190251881Speter      /* ### TODO: Also print victim_path in the skip msg. */
2191251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2192251881Speter                      svn_wc_notify_skip_conflicted, pool);
2193251881Speter      return SVN_NO_ERROR;
2194251881Speter    }
2195251881Speter  else if (conflict_ignored)
2196251881Speter    {
2197251881Speter      db->shadowed = TRUE;
2198251881Speter    }
2199251881Speter
2200251881Speter  if (db->shadowed)
2201251881Speter    {
2202251881Speter      /* Nothing to check; does not and will not exist in working copy */
2203251881Speter    }
2204251881Speter  else if (versioned_locally_and_present)
2205251881Speter    {
2206251881Speter      /* What to do with a versioned or schedule-add dir:
2207251881Speter
2208251881Speter         A dir already added without history is OK.  Set add_existed
2209251881Speter         so that user notification is delayed until after any prop
2210251881Speter         conflicts have been found.
2211251881Speter
2212251881Speter         An existing versioned dir is an error.  In the future we may
2213251881Speter         relax this restriction and simply update such dirs.
2214251881Speter
2215251881Speter         A dir added with history is a tree conflict. */
2216251881Speter
2217251881Speter      svn_boolean_t local_is_non_dir;
2218251881Speter      svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2219251881Speter
2220251881Speter      /* Is the local add a copy? */
2221251881Speter      if (status == svn_wc__db_status_added)
2222251881Speter        SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2223251881Speter                                         NULL, NULL, NULL, NULL,
2224251881Speter                                         eb->db, db->local_abspath,
2225251881Speter                                         pool, pool));
2226251881Speter
2227251881Speter
2228251881Speter      /* Is there *something* that is not a dir? */
2229251881Speter      local_is_non_dir = (wc_kind != svn_node_dir
2230251881Speter                          && status != svn_wc__db_status_deleted);
2231251881Speter
2232251881Speter      /* Do tree conflict checking if
2233251881Speter       *  - if there is a local copy.
2234251881Speter       *  - if this is a switch operation
2235251881Speter       *  - the node kinds mismatch
2236251881Speter       *
2237251881Speter       * During switch, local adds at the same path as incoming adds get
2238251881Speter       * "lost" in that switching back to the original will no longer have the
2239251881Speter       * local add. So switch always alerts the user with a tree conflict. */
2240251881Speter      if (!eb->adds_as_modification
2241251881Speter          || local_is_non_dir
2242251881Speter          || add_status != svn_wc__db_status_added)
2243251881Speter        {
2244251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2245251881Speter                                      db->local_abspath,
2246251881Speter                                      status, FALSE, svn_node_none,
2247251881Speter                                      svn_wc_conflict_action_add,
2248251881Speter                                      pool, pool));
2249251881Speter        }
2250251881Speter
2251251881Speter      if (tree_conflict == NULL)
2252251881Speter        db->add_existed = TRUE; /* Take over WORKING */
2253251881Speter      else
2254251881Speter        db->shadowed = TRUE; /* Only update BASE */
2255251881Speter    }
2256251881Speter  else if (kind != svn_node_none)
2257251881Speter    {
2258251881Speter      /* There's an unversioned node at this path. */
2259251881Speter      db->obstruction_found = TRUE;
2260251881Speter
2261251881Speter      /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2262251881Speter       * if unversioned obstructions are allowed. */
2263251881Speter      if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2264251881Speter        {
2265251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2266251881Speter          db->shadowed = TRUE;
2267251881Speter
2268251881Speter          /* Mark a conflict */
2269251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2270251881Speter
2271251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2272251881Speter                                        tree_conflict,
2273251881Speter                                        eb->db, db->local_abspath,
2274251881Speter                                        svn_wc_conflict_reason_unversioned,
2275251881Speter                                        svn_wc_conflict_action_add, NULL,
2276251881Speter                                        db->pool, pool));
2277251881Speter          db->edit_conflict = tree_conflict;
2278251881Speter        }
2279251881Speter    }
2280251881Speter
2281251881Speter  if (tree_conflict)
2282251881Speter    SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2283251881Speter                              db->old_repos_relpath, db->old_revision,
2284251881Speter                              db->new_relpath,
2285251881Speter                              wc_kind,
2286251881Speter                              svn_node_dir,
2287251881Speter                              db->pool, pool));
2288251881Speter
2289251881Speter  SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2290251881Speter                                     eb->db, db->local_abspath,
2291251881Speter                                     db->new_relpath,
2292251881Speter                                     eb->repos_root,
2293251881Speter                                     eb->repos_uuid,
2294251881Speter                                     *eb->target_revision,
2295251881Speter                                     db->ambient_depth,
2296251881Speter                                     (db->shadowed && db->obstruction_found),
2297251881Speter                                     (! db->shadowed
2298251881Speter                                      && status == svn_wc__db_status_added),
2299251881Speter                                     tree_conflict, NULL,
2300251881Speter                                     pool));
2301251881Speter
2302251881Speter  /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2303251881Speter     updating the DB */
2304251881Speter  if (!db->shadowed)
2305251881Speter    SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2306251881Speter
2307251881Speter  if (tree_conflict != NULL)
2308251881Speter    {
2309253734Speter      if (eb->conflict_func)
2310253734Speter        SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2311253734Speter                                                 tree_conflict,
2312253734Speter                                                 NULL /* merge_options */,
2313253734Speter                                                 eb->conflict_func,
2314253734Speter                                                 eb->conflict_baton,
2315253734Speter                                                 eb->cancel_func,
2316253734Speter                                                 eb->cancel_baton,
2317253734Speter                                                 pool));
2318253734Speter
2319251881Speter      db->already_notified = TRUE;
2320251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2321251881Speter                      svn_wc_notify_tree_conflict, pool);
2322251881Speter    }
2323251881Speter
2324251881Speter
2325251881Speter  /* If this add was obstructed by dir scheduled for addition without
2326251881Speter     history let close_directory() handle the notification because there
2327251881Speter     might be properties to deal with.  If PATH was added inside a locally
2328251881Speter     deleted tree, then suppress notification, a tree conflict was already
2329251881Speter     issued. */
2330251881Speter  if (eb->notify_func && !db->already_notified && !db->add_existed)
2331251881Speter    {
2332251881Speter      svn_wc_notify_action_t action;
2333251881Speter
2334251881Speter      if (db->shadowed)
2335251881Speter        action = svn_wc_notify_update_shadowed_add;
2336251881Speter      else if (db->obstruction_found || db->add_existed)
2337251881Speter        action = svn_wc_notify_exists;
2338251881Speter      else
2339251881Speter        action = svn_wc_notify_update_add;
2340251881Speter
2341251881Speter      db->already_notified = TRUE;
2342251881Speter
2343251881Speter      do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2344251881Speter    }
2345251881Speter
2346251881Speter  return SVN_NO_ERROR;
2347251881Speter}
2348251881Speter
2349251881Speter/* An svn_delta_editor_t function. */
2350251881Speterstatic svn_error_t *
2351251881Speteropen_directory(const char *path,
2352251881Speter               void *parent_baton,
2353251881Speter               svn_revnum_t base_revision,
2354251881Speter               apr_pool_t *pool,
2355251881Speter               void **child_baton)
2356251881Speter{
2357251881Speter  struct dir_baton *db, *pb = parent_baton;
2358251881Speter  struct edit_baton *eb = pb->edit_baton;
2359251881Speter  svn_boolean_t have_work;
2360251881Speter  svn_boolean_t conflicted;
2361251881Speter  svn_boolean_t conflict_ignored = FALSE;
2362251881Speter  svn_skel_t *tree_conflict = NULL;
2363251881Speter  svn_wc__db_status_t status, base_status;
2364251881Speter  svn_node_kind_t wc_kind;
2365251881Speter
2366251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2367251881Speter  *child_baton = db;
2368251881Speter
2369251881Speter  if (db->skip_this)
2370251881Speter    return SVN_NO_ERROR;
2371251881Speter
2372251881Speter  /* Detect obstructing working copies */
2373251881Speter  {
2374251881Speter    svn_boolean_t is_root;
2375251881Speter
2376251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2377251881Speter                                 pool));
2378251881Speter
2379251881Speter    if (is_root)
2380251881Speter      {
2381251881Speter        /* Just skip this node; a future update will handle it */
2382251881Speter        SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2383251881Speter        db->skip_this = TRUE;
2384251881Speter        db->already_notified = TRUE;
2385251881Speter
2386251881Speter        do_notification(eb, db->local_abspath, svn_node_dir,
2387251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
2388251881Speter
2389251881Speter        return SVN_NO_ERROR;
2390251881Speter      }
2391251881Speter  }
2392251881Speter
2393251881Speter  /* We should have a write lock on every directory touched.  */
2394251881Speter  SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2395251881Speter
2396251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2397251881Speter                               &db->old_repos_relpath, NULL, NULL,
2398251881Speter                               &db->changed_rev, &db->changed_date,
2399251881Speter                               &db->changed_author, &db->ambient_depth,
2400251881Speter                               NULL, NULL, NULL, NULL,
2401251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
2402251881Speter                               &conflicted, NULL, NULL, NULL,
2403251881Speter                               NULL, NULL, &have_work,
2404251881Speter                               eb->db, db->local_abspath,
2405251881Speter                               db->pool, pool));
2406251881Speter
2407251881Speter  if (!have_work)
2408251881Speter    base_status = status;
2409251881Speter  else
2410251881Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2411251881Speter                                     &db->old_repos_relpath, NULL, NULL,
2412251881Speter                                     &db->changed_rev, &db->changed_date,
2413251881Speter                                     &db->changed_author, &db->ambient_depth,
2414251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL,
2415251881Speter                                     eb->db, db->local_abspath,
2416251881Speter                                     db->pool, pool));
2417251881Speter
2418251881Speter  db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2419251881Speter
2420251881Speter  /* Is this path a conflict victim? */
2421251881Speter  if (db->shadowed)
2422251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
2423251881Speter  else if (conflicted)
2424251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2425251881Speter                                    eb->db, db->local_abspath, pool));
2426251881Speter  if (conflicted)
2427251881Speter    {
2428251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2429251881Speter
2430251881Speter      db->skip_this = TRUE;
2431251881Speter      db->already_notified = TRUE;
2432251881Speter
2433251881Speter      do_notification(eb, db->local_abspath, svn_node_unknown,
2434251881Speter                      svn_wc_notify_skip_conflicted, pool);
2435251881Speter
2436251881Speter      return SVN_NO_ERROR;
2437251881Speter    }
2438251881Speter  else if (conflict_ignored)
2439251881Speter    {
2440251881Speter      db->shadowed = TRUE;
2441251881Speter    }
2442251881Speter
2443251881Speter  /* Is this path a fresh tree conflict victim?  If so, skip the tree
2444251881Speter     with one notification. */
2445251881Speter
2446251881Speter  /* Check for conflicts only when we haven't already recorded
2447251881Speter   * a tree-conflict on a parent node. */
2448251881Speter  if (!db->shadowed)
2449251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2450251881Speter                                status, TRUE, svn_node_dir,
2451251881Speter                                svn_wc_conflict_action_edit,
2452251881Speter                                db->pool, pool));
2453251881Speter
2454251881Speter  /* Remember the roots of any locally deleted trees. */
2455251881Speter  if (tree_conflict != NULL)
2456251881Speter    {
2457251881Speter      svn_wc_conflict_reason_t reason;
2458251881Speter      db->edit_conflict = tree_conflict;
2459251881Speter      /* Other modifications wouldn't be a tree conflict */
2460251881Speter
2461251881Speter      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2462251881Speter                                                  eb->db, db->local_abspath,
2463251881Speter                                                  tree_conflict,
2464251881Speter                                                  db->pool, db->pool));
2465251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2466251881Speter                     || reason == svn_wc_conflict_reason_moved_away
2467251881Speter                     || reason == svn_wc_conflict_reason_replaced
2468251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
2469251881Speter
2470251881Speter      /* Continue updating BASE */
2471251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
2472251881Speter        db->edit_obstructed = TRUE;
2473251881Speter      else
2474251881Speter        db->shadowed = TRUE;
2475251881Speter    }
2476251881Speter
2477251881Speter  /* Mark directory as being at target_revision and URL, but incomplete. */
2478251881Speter  SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2479251881Speter                                                    db->new_relpath,
2480251881Speter                                                    *eb->target_revision,
2481251881Speter                                                    pool));
2482251881Speter
2483251881Speter  return SVN_NO_ERROR;
2484251881Speter}
2485251881Speter
2486251881Speter
2487251881Speter/* An svn_delta_editor_t function. */
2488251881Speterstatic svn_error_t *
2489251881Speterchange_dir_prop(void *dir_baton,
2490251881Speter                const char *name,
2491251881Speter                const svn_string_t *value,
2492251881Speter                apr_pool_t *pool)
2493251881Speter{
2494251881Speter  svn_prop_t *propchange;
2495251881Speter  struct dir_baton *db = dir_baton;
2496251881Speter
2497251881Speter  if (db->skip_this)
2498251881Speter    return SVN_NO_ERROR;
2499251881Speter
2500251881Speter  propchange = apr_array_push(db->propchanges);
2501251881Speter  propchange->name = apr_pstrdup(db->pool, name);
2502251881Speter  propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2503251881Speter
2504251881Speter  if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2505251881Speter    SVN_ERR(mark_directory_edited(db, pool));
2506251881Speter
2507251881Speter  return SVN_NO_ERROR;
2508251881Speter}
2509251881Speter
2510251881Speter/* If any of the svn_prop_t objects in PROPCHANGES represents a change
2511251881Speter   to the SVN_PROP_EXTERNALS property, return that change, else return
2512251881Speter   null.  If PROPCHANGES contains more than one such change, return
2513251881Speter   the first. */
2514251881Speterstatic const svn_prop_t *
2515251881Speterexternals_prop_changed(const apr_array_header_t *propchanges)
2516251881Speter{
2517251881Speter  int i;
2518251881Speter
2519251881Speter  for (i = 0; i < propchanges->nelts; i++)
2520251881Speter    {
2521251881Speter      const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2522251881Speter      if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2523251881Speter        return p;
2524251881Speter    }
2525251881Speter
2526251881Speter  return NULL;
2527251881Speter}
2528251881Speter
2529251881Speter
2530251881Speter
2531251881Speter/* An svn_delta_editor_t function. */
2532251881Speterstatic svn_error_t *
2533251881Speterclose_directory(void *dir_baton,
2534251881Speter                apr_pool_t *pool)
2535251881Speter{
2536251881Speter  struct dir_baton *db = dir_baton;
2537251881Speter  struct edit_baton *eb = db->edit_baton;
2538251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2539251881Speter  apr_array_header_t *entry_prop_changes;
2540251881Speter  apr_array_header_t *dav_prop_changes;
2541251881Speter  apr_array_header_t *regular_prop_changes;
2542251881Speter  apr_hash_t *base_props;
2543251881Speter  apr_hash_t *actual_props;
2544251881Speter  apr_hash_t *new_base_props = NULL;
2545251881Speter  apr_hash_t *new_actual_props = NULL;
2546251881Speter  svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2547251881Speter  apr_time_t new_changed_date = 0;
2548251881Speter  const char *new_changed_author = NULL;
2549251881Speter  apr_pool_t *scratch_pool = db->pool;
2550251881Speter  svn_skel_t *all_work_items = NULL;
2551251881Speter  svn_skel_t *conflict_skel = NULL;
2552251881Speter
2553251881Speter  /* Skip if we're in a conflicted tree. */
2554251881Speter  if (db->skip_this)
2555251881Speter    {
2556251881Speter      /* Allow the parent to complete its update. */
2557251881Speter      SVN_ERR(maybe_release_dir_info(db));
2558251881Speter
2559251881Speter      return SVN_NO_ERROR;
2560251881Speter    }
2561251881Speter
2562251881Speter  if (db->edited)
2563251881Speter    conflict_skel = db->edit_conflict;
2564251881Speter
2565251881Speter  SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2566251881Speter                               &dav_prop_changes, &regular_prop_changes, pool));
2567251881Speter
2568251881Speter  /* Fetch the existing properties.  */
2569251881Speter  if ((!db->adding_dir || db->add_existed)
2570251881Speter      && !db->shadowed)
2571251881Speter    {
2572251881Speter      SVN_ERR(svn_wc__get_actual_props(&actual_props,
2573251881Speter                                       eb->db, db->local_abspath,
2574251881Speter                                       scratch_pool, scratch_pool));
2575251881Speter    }
2576251881Speter  else
2577251881Speter    actual_props = apr_hash_make(pool);
2578251881Speter
2579251881Speter  if (db->add_existed)
2580251881Speter    {
2581251881Speter      /* This node already exists. Grab the current pristine properties. */
2582251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2583251881Speter                                             eb->db, db->local_abspath,
2584251881Speter                                             scratch_pool, scratch_pool));
2585251881Speter    }
2586251881Speter  else if (!db->adding_dir)
2587251881Speter    {
2588251881Speter      /* Get the BASE properties for proper merging. */
2589251881Speter      SVN_ERR(svn_wc__db_base_get_props(&base_props,
2590251881Speter                                        eb->db, db->local_abspath,
2591251881Speter                                        scratch_pool, scratch_pool));
2592251881Speter    }
2593251881Speter  else
2594251881Speter    base_props = apr_hash_make(pool);
2595251881Speter
2596251881Speter  /* An incomplete directory might have props which were supposed to be
2597251881Speter     deleted but weren't.  Because the server sent us all the props we're
2598251881Speter     supposed to have, any previous base props not in this list must be
2599251881Speter     deleted (issue #1672). */
2600251881Speter  if (db->was_incomplete)
2601251881Speter    {
2602251881Speter      int i;
2603251881Speter      apr_hash_t *props_to_delete;
2604251881Speter      apr_hash_index_t *hi;
2605251881Speter
2606251881Speter      /* In a copy of the BASE props, remove every property that we see an
2607251881Speter         incoming change for. The remaining unmentioned properties are those
2608251881Speter         which need to be deleted.  */
2609251881Speter      props_to_delete = apr_hash_copy(pool, base_props);
2610251881Speter      for (i = 0; i < regular_prop_changes->nelts; i++)
2611251881Speter        {
2612251881Speter          const svn_prop_t *prop;
2613251881Speter          prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2614251881Speter          svn_hash_sets(props_to_delete, prop->name, NULL);
2615251881Speter        }
2616251881Speter
2617251881Speter      /* Add these props to the incoming propchanges (in
2618251881Speter       * regular_prop_changes).  */
2619251881Speter      for (hi = apr_hash_first(pool, props_to_delete);
2620251881Speter           hi != NULL;
2621251881Speter           hi = apr_hash_next(hi))
2622251881Speter        {
2623251881Speter          const char *propname = svn__apr_hash_index_key(hi);
2624251881Speter          svn_prop_t *prop = apr_array_push(regular_prop_changes);
2625251881Speter
2626251881Speter          /* Record a deletion for PROPNAME.  */
2627251881Speter          prop->name = propname;
2628251881Speter          prop->value = NULL;
2629251881Speter        }
2630251881Speter    }
2631251881Speter
2632251881Speter  /* If this directory has property changes stored up, now is the time
2633251881Speter     to deal with them. */
2634251881Speter  if (regular_prop_changes->nelts)
2635251881Speter    {
2636251881Speter      /* If recording traversal info, then see if the
2637251881Speter         SVN_PROP_EXTERNALS property on this directory changed,
2638251881Speter         and record before and after for the change. */
2639251881Speter      if (eb->external_func)
2640251881Speter        {
2641251881Speter          const svn_prop_t *change
2642251881Speter            = externals_prop_changed(regular_prop_changes);
2643251881Speter
2644251881Speter          if (change)
2645251881Speter            {
2646251881Speter              const svn_string_t *new_val_s = change->value;
2647251881Speter              const svn_string_t *old_val_s;
2648251881Speter
2649251881Speter              old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2650251881Speter
2651251881Speter              if ((new_val_s == NULL) && (old_val_s == NULL))
2652251881Speter                ; /* No value before, no value after... so do nothing. */
2653251881Speter              else if (new_val_s && old_val_s
2654251881Speter                       && (svn_string_compare(old_val_s, new_val_s)))
2655251881Speter                ; /* Value did not change... so do nothing. */
2656251881Speter              else if (old_val_s || new_val_s)
2657251881Speter                /* something changed, record the change */
2658251881Speter                {
2659251881Speter                  SVN_ERR((eb->external_func)(
2660251881Speter                                       eb->external_baton,
2661251881Speter                                       db->local_abspath,
2662251881Speter                                       old_val_s,
2663251881Speter                                       new_val_s,
2664251881Speter                                       db->ambient_depth,
2665251881Speter                                       db->pool));
2666251881Speter                }
2667251881Speter            }
2668251881Speter        }
2669251881Speter
2670251881Speter      if (db->shadowed)
2671251881Speter        {
2672251881Speter          /* We don't have a relevant actual row, but we need actual properties
2673251881Speter             to allow property merging without conflicts. */
2674251881Speter          if (db->adding_dir)
2675251881Speter            actual_props = apr_hash_make(scratch_pool);
2676251881Speter          else
2677251881Speter            actual_props = base_props;
2678251881Speter        }
2679251881Speter
2680251881Speter      /* Merge pending properties. */
2681251881Speter      new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2682251881Speter                                       db->pool);
2683251881Speter      SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2684251881Speter                                    &prop_state,
2685251881Speter                                    &new_actual_props,
2686251881Speter                                    eb->db,
2687251881Speter                                    db->local_abspath,
2688251881Speter                                    NULL /* use baseprops */,
2689251881Speter                                    base_props,
2690251881Speter                                    actual_props,
2691251881Speter                                    regular_prop_changes,
2692251881Speter                                    db->pool,
2693251881Speter                                    scratch_pool),
2694251881Speter                _("Couldn't do property merge"));
2695251881Speter      /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2696251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2697251881Speter    }
2698251881Speter
2699251881Speter  SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2700251881Speter                                 &new_changed_author, entry_prop_changes,
2701251881Speter                                 scratch_pool, scratch_pool));
2702251881Speter
2703251881Speter  /* Check if we should add some not-present markers before marking the
2704251881Speter     directory complete (Issue #3569) */
2705251881Speter  {
2706251881Speter    apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2707251881Speter
2708251881Speter    if (new_children != NULL)
2709251881Speter      {
2710251881Speter        apr_hash_index_t *hi;
2711251881Speter        apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2712251881Speter
2713251881Speter        for (hi = apr_hash_first(scratch_pool, new_children);
2714251881Speter             hi;
2715251881Speter             hi = apr_hash_next(hi))
2716251881Speter          {
2717251881Speter            const char *child_name;
2718251881Speter            const char *child_abspath;
2719251881Speter            const char *child_relpath;
2720251881Speter            const svn_dirent_t *dirent;
2721251881Speter            svn_wc__db_status_t status;
2722251881Speter            svn_node_kind_t child_kind;
2723251881Speter            svn_error_t *err;
2724251881Speter
2725251881Speter            svn_pool_clear(iterpool);
2726251881Speter
2727251881Speter            child_name = svn__apr_hash_index_key(hi);
2728251881Speter            child_abspath = svn_dirent_join(db->local_abspath, child_name,
2729251881Speter                                            iterpool);
2730251881Speter
2731251881Speter            dirent = svn__apr_hash_index_val(hi);
2732251881Speter            child_kind = (dirent->kind == svn_node_dir)
2733251881Speter                                        ? svn_node_dir
2734251881Speter                                        : svn_node_file;
2735251881Speter
2736251881Speter            if (db->ambient_depth < svn_depth_immediates
2737251881Speter                && child_kind == svn_node_dir)
2738251881Speter              continue; /* We don't need the subdirs */
2739251881Speter
2740251881Speter            /* ### We just check if there is some node in BASE at this path */
2741251881Speter            err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2742251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
2743251881Speter                                           NULL, NULL, NULL, NULL, NULL,
2744251881Speter                                           eb->db, child_abspath,
2745251881Speter                                           iterpool, iterpool);
2746251881Speter
2747251881Speter            if (!err)
2748251881Speter              {
2749251881Speter                svn_boolean_t is_wcroot;
2750251881Speter                SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2751251881Speter                                             iterpool));
2752251881Speter
2753251881Speter                if (!is_wcroot)
2754251881Speter                  continue; /* Everything ok... Nothing to do here */
2755251881Speter                /* Fall through to allow recovering later */
2756251881Speter              }
2757251881Speter            else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2758251881Speter              return svn_error_trace(err);
2759251881Speter
2760251881Speter            svn_error_clear(err);
2761251881Speter
2762251881Speter            child_relpath = svn_relpath_join(db->new_relpath, child_name,
2763251881Speter                                             iterpool);
2764251881Speter
2765251881Speter            SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2766251881Speter                                                         child_abspath,
2767251881Speter                                                         child_relpath,
2768251881Speter                                                         eb->repos_root,
2769251881Speter                                                         eb->repos_uuid,
2770251881Speter                                                         *eb->target_revision,
2771251881Speter                                                         child_kind,
2772251881Speter                                                         NULL, NULL,
2773251881Speter                                                         iterpool));
2774251881Speter          }
2775251881Speter
2776251881Speter        svn_pool_destroy(iterpool);
2777251881Speter      }
2778251881Speter  }
2779251881Speter
2780251881Speter  if (apr_hash_count(db->not_present_files))
2781251881Speter    {
2782251881Speter      apr_hash_index_t *hi;
2783251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2784251881Speter
2785251881Speter      /* This should call some new function (which could also be used
2786251881Speter         for new_children above) to add all the names in single
2787251881Speter         transaction, but I can't even trigger it.  I've tried
2788251881Speter         ra_local, ra_svn, ra_neon, ra_serf and they all call
2789251881Speter         close_file before close_dir. */
2790251881Speter      for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2791251881Speter           hi;
2792251881Speter           hi = apr_hash_next(hi))
2793251881Speter        {
2794251881Speter          const char *child = svn__apr_hash_index_key(hi);
2795251881Speter          const char *child_abspath, *child_relpath;
2796251881Speter
2797251881Speter          svn_pool_clear(iterpool);
2798251881Speter
2799251881Speter          child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2800251881Speter          child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2801251881Speter
2802251881Speter          SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2803251881Speter                                                       child_abspath,
2804251881Speter                                                       child_relpath,
2805251881Speter                                                       eb->repos_root,
2806251881Speter                                                       eb->repos_uuid,
2807251881Speter                                                       *eb->target_revision,
2808251881Speter                                                       svn_node_file,
2809251881Speter                                                       NULL, NULL,
2810251881Speter                                                       iterpool));
2811251881Speter        }
2812251881Speter      svn_pool_destroy(iterpool);
2813251881Speter    }
2814251881Speter
2815251881Speter  /* If this directory is merely an anchor for a targeted child, then we
2816251881Speter     should not be updating the node at all.  */
2817251881Speter  if (db->parent_baton == NULL
2818251881Speter      && *eb->target_basename != '\0')
2819251881Speter    {
2820251881Speter      /* And we should not have received any changes!  */
2821251881Speter      SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2822251881Speter      /* ... which also implies NEW_CHANGED_* are not set,
2823251881Speter         and NEW_BASE_PROPS == NULL.  */
2824251881Speter    }
2825251881Speter  else
2826251881Speter    {
2827251881Speter      apr_hash_t *props;
2828251881Speter      apr_array_header_t *iprops = NULL;
2829251881Speter
2830251881Speter      /* ### we know a base node already exists. it was created in
2831251881Speter         ### open_directory or add_directory.  let's just preserve the
2832251881Speter         ### existing DEPTH value, and possibly CHANGED_*.  */
2833251881Speter      /* If we received any changed_* values, then use them.  */
2834251881Speter      if (SVN_IS_VALID_REVNUM(new_changed_rev))
2835251881Speter        db->changed_rev = new_changed_rev;
2836251881Speter      if (new_changed_date != 0)
2837251881Speter        db->changed_date = new_changed_date;
2838251881Speter      if (new_changed_author != NULL)
2839251881Speter        db->changed_author = new_changed_author;
2840251881Speter
2841251881Speter      /* If no depth is set yet, set to infinity. */
2842251881Speter      if (db->ambient_depth == svn_depth_unknown)
2843251881Speter        db->ambient_depth = svn_depth_infinity;
2844251881Speter
2845251881Speter      if (eb->depth_is_sticky
2846251881Speter          && db->ambient_depth != eb->requested_depth)
2847251881Speter        {
2848251881Speter          /* After a depth upgrade the entry must reflect the new depth.
2849251881Speter             Upgrading to infinity changes the depth of *all* directories,
2850251881Speter             upgrading to something else only changes the target. */
2851251881Speter
2852251881Speter          if (eb->requested_depth == svn_depth_infinity
2853251881Speter              || (strcmp(db->local_abspath, eb->target_abspath) == 0
2854251881Speter                  && eb->requested_depth > db->ambient_depth))
2855251881Speter            {
2856251881Speter              db->ambient_depth = eb->requested_depth;
2857251881Speter            }
2858251881Speter        }
2859251881Speter
2860251881Speter      /* Do we have new properties to install? Or shall we simply retain
2861251881Speter         the prior set of properties? If we're installing new properties,
2862251881Speter         then we also want to write them to an old-style props file.  */
2863251881Speter      props = new_base_props;
2864251881Speter      if (props == NULL)
2865251881Speter        props = base_props;
2866251881Speter
2867251881Speter      if (conflict_skel)
2868251881Speter        {
2869251881Speter          svn_skel_t *work_item;
2870251881Speter
2871251881Speter          SVN_ERR(complete_conflict(conflict_skel,
2872251881Speter                                    db->edit_baton,
2873251881Speter                                    db->local_abspath,
2874251881Speter                                    db->old_repos_relpath,
2875251881Speter                                    db->old_revision,
2876251881Speter                                    db->new_relpath,
2877251881Speter                                    svn_node_dir, svn_node_dir,
2878251881Speter                                    db->pool, scratch_pool));
2879251881Speter
2880251881Speter          SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2881251881Speter                                                  eb->db, db->local_abspath,
2882251881Speter                                                  conflict_skel,
2883251881Speter                                                  scratch_pool, scratch_pool));
2884251881Speter
2885251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2886251881Speter                                            scratch_pool);
2887251881Speter        }
2888251881Speter
2889251881Speter      /* Any inherited props to be set set for this base node? */
2890251881Speter      if (eb->wcroot_iprops)
2891251881Speter        {
2892251881Speter          iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2893251881Speter
2894251881Speter          /* close_edit may also update iprops for switched nodes, catching
2895251881Speter             those for which close_directory is never called (e.g. a switch
2896251881Speter             with no changes).  So as a minor optimization we remove any
2897251881Speter             iprops from the hash so as not to set them again in
2898251881Speter             close_edit. */
2899251881Speter          if (iprops)
2900251881Speter            svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2901251881Speter        }
2902251881Speter
2903251881Speter      /* Update the BASE data for the directory and mark the directory
2904251881Speter         complete */
2905251881Speter      SVN_ERR(svn_wc__db_base_add_directory(
2906251881Speter                eb->db, db->local_abspath,
2907251881Speter                eb->wcroot_abspath,
2908251881Speter                db->new_relpath,
2909251881Speter                eb->repos_root, eb->repos_uuid,
2910251881Speter                *eb->target_revision,
2911251881Speter                props,
2912251881Speter                db->changed_rev, db->changed_date, db->changed_author,
2913251881Speter                NULL /* children */,
2914251881Speter                db->ambient_depth,
2915251881Speter                (dav_prop_changes->nelts > 0)
2916251881Speter                    ? svn_prop_array_to_hash(dav_prop_changes, pool)
2917251881Speter                    : NULL,
2918251881Speter                conflict_skel,
2919251881Speter                (! db->shadowed) && new_base_props != NULL,
2920251881Speter                new_actual_props,
2921251881Speter                iprops, all_work_items,
2922251881Speter                scratch_pool));
2923251881Speter    }
2924251881Speter
2925251881Speter  /* Process all of the queued work items for this directory.  */
2926251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2927251881Speter                         eb->cancel_func, eb->cancel_baton,
2928251881Speter                         scratch_pool));
2929251881Speter
2930251881Speter  if (conflict_skel && eb->conflict_func)
2931251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2932251881Speter                                             conflict_skel,
2933251881Speter                                             NULL /* merge_options */,
2934251881Speter                                             eb->conflict_func,
2935251881Speter                                             eb->conflict_baton,
2936251881Speter                                             eb->cancel_func,
2937253734Speter                                             eb->cancel_baton,
2938251881Speter                                             scratch_pool));
2939251881Speter
2940251881Speter  /* Notify of any prop changes on this directory -- but do nothing if
2941251881Speter     it's an added or skipped directory, because notification has already
2942251881Speter     happened in that case - unless the add was obstructed by a dir
2943251881Speter     scheduled for addition without history, in which case we handle
2944251881Speter     notification here). */
2945251881Speter  if (!db->already_notified && eb->notify_func && db->edited)
2946251881Speter    {
2947251881Speter      svn_wc_notify_t *notify;
2948251881Speter      svn_wc_notify_action_t action;
2949251881Speter
2950251881Speter      if (db->shadowed || db->edit_obstructed)
2951251881Speter        action = svn_wc_notify_update_shadowed_update;
2952251881Speter      else if (db->obstruction_found || db->add_existed)
2953251881Speter        action = svn_wc_notify_exists;
2954251881Speter      else
2955251881Speter        action = svn_wc_notify_update_update;
2956251881Speter
2957251881Speter      notify = svn_wc_create_notify(db->local_abspath, action, pool);
2958251881Speter      notify->kind = svn_node_dir;
2959251881Speter      notify->prop_state = prop_state;
2960251881Speter      notify->revision = *eb->target_revision;
2961251881Speter      notify->old_revision = db->old_revision;
2962251881Speter
2963251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
2964251881Speter    }
2965251881Speter
2966251881Speter  /* We're done with this directory, so remove one reference from the
2967251881Speter     bump information. */
2968251881Speter  SVN_ERR(maybe_release_dir_info(db));
2969251881Speter
2970251881Speter  return SVN_NO_ERROR;
2971251881Speter}
2972251881Speter
2973251881Speter
2974251881Speter/* Common code for 'absent_file' and 'absent_directory'. */
2975251881Speterstatic svn_error_t *
2976251881Speterabsent_node(const char *path,
2977251881Speter            svn_node_kind_t absent_kind,
2978251881Speter            void *parent_baton,
2979251881Speter            apr_pool_t *pool)
2980251881Speter{
2981251881Speter  struct dir_baton *pb = parent_baton;
2982251881Speter  struct edit_baton *eb = pb->edit_baton;
2983251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
2984251881Speter  const char *name = svn_dirent_basename(path, NULL);
2985251881Speter  const char *local_abspath;
2986251881Speter  svn_error_t *err;
2987251881Speter  svn_wc__db_status_t status;
2988251881Speter  svn_node_kind_t kind;
2989251881Speter
2990251881Speter  if (pb->skip_this)
2991251881Speter    return SVN_NO_ERROR;
2992251881Speter
2993251881Speter  SVN_ERR(mark_directory_edited(pb, scratch_pool));
2994251881Speter
2995251881Speter  local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2996251881Speter
2997251881Speter  /* If an item by this name is scheduled for addition that's a
2998251881Speter     genuine tree-conflict.  */
2999251881Speter  err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
3000251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3001251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3002251881Speter                             NULL, NULL, NULL, NULL,
3003251881Speter                             eb->db, local_abspath,
3004251881Speter                             scratch_pool, scratch_pool);
3005251881Speter
3006251881Speter  if (err)
3007251881Speter    {
3008251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3009251881Speter        return svn_error_trace(err);
3010251881Speter
3011251881Speter      svn_error_clear(err);
3012251881Speter      status = svn_wc__db_status_not_present;
3013251881Speter      kind = svn_node_unknown;
3014251881Speter    }
3015251881Speter
3016257936Speter  if (status == svn_wc__db_status_normal)
3017251881Speter    {
3018257936Speter      svn_boolean_t wcroot;
3019257936Speter      /* We found an obstructing working copy or a file external! */
3020251881Speter
3021257936Speter      SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
3022257936Speter                                   scratch_pool));
3023251881Speter
3024257936Speter      if (wcroot)
3025257936Speter        {
3026257936Speter          /*
3027257936Speter             We have an obstructing working copy; possibly a directory external
3028257936Speter
3029257936Speter             We can do two things now:
3030257936Speter             1) notify the user, record a skip, etc.
3031257936Speter             2) Just record the absent node in BASE in the parent
3032257936Speter                working copy.
3033257936Speter
3034257936Speter             As option 2 happens to be exactly what we do anyway, fall through.
3035257936Speter           */
3036257936Speter        }
3037257936Speter      else
3038257936Speter        {
3039257936Speter          /* The server asks us to replace a file external
3040257936Speter             (Existing BASE node; not reported by the working copy crawler or
3041257936Speter              there would have been a delete_entry() call.
3042257936Speter
3043257936Speter             There is no way we can store this state in the working copy as
3044257936Speter             the BASE layer is already filled.
3045257936Speter
3046257936Speter             We could error out, but that is not helping anybody; the user is not
3047257936Speter             even seeing with what the file external would be replaced, so let's
3048257936Speter             report a skip and continue the update.
3049257936Speter           */
3050257936Speter
3051257936Speter          if (eb->notify_func)
3052257936Speter            {
3053257936Speter              svn_wc_notify_t *notify;
3054257936Speter              notify = svn_wc_create_notify(
3055257936Speter                                    local_abspath,
3056257936Speter                                    svn_wc_notify_update_skip_obstruction,
3057257936Speter                                    scratch_pool);
3058257936Speter
3059257936Speter              eb->notify_func(eb->notify_baton, notify, scratch_pool);
3060257936Speter            }
3061257936Speter
3062257936Speter          svn_pool_destroy(scratch_pool);
3063257936Speter          return SVN_NO_ERROR;
3064257936Speter        }
3065251881Speter    }
3066251881Speter  else if (status == svn_wc__db_status_not_present
3067251881Speter           || status == svn_wc__db_status_server_excluded
3068251881Speter           || status == svn_wc__db_status_excluded)
3069251881Speter    {
3070251881Speter      /* The BASE node is not actually there, so we can safely turn it into
3071251881Speter         an absent node */
3072251881Speter    }
3073251881Speter  else
3074251881Speter    {
3075251881Speter      /* We have a local addition. If this would be a BASE node it would have
3076251881Speter         been deleted before we get here. (Which might have turned it into
3077251881Speter         a copy).
3078251881Speter
3079251881Speter         ### This should be recorded as a tree conflict and the update
3080251881Speter         ### can just continue, as we can just record the absent status
3081251881Speter         ### in BASE.
3082251881Speter       */
3083251881Speter      SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3084251881Speter
3085251881Speter      return svn_error_createf(
3086251881Speter         SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3087251881Speter         _("Failed to mark '%s' absent: item of the same name is already "
3088251881Speter           "scheduled for addition"),
3089251881Speter         svn_dirent_local_style(local_abspath, pool));
3090251881Speter    }
3091251881Speter
3092251881Speter  {
3093251881Speter    const char *repos_relpath;
3094251881Speter    repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3095251881Speter
3096251881Speter    /* Insert an excluded node below the parent node to note that this child
3097251881Speter       is absent. (This puts it in the parent db if the child is obstructed) */
3098251881Speter    SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3099251881Speter                                              repos_relpath, eb->repos_root,
3100251881Speter                                              eb->repos_uuid,
3101251881Speter                                              *(eb->target_revision),
3102251881Speter                                              absent_kind,
3103251881Speter                                              svn_wc__db_status_server_excluded,
3104251881Speter                                              NULL, NULL,
3105251881Speter                                              scratch_pool));
3106251881Speter  }
3107251881Speter
3108251881Speter  svn_pool_destroy(scratch_pool);
3109251881Speter
3110251881Speter  return SVN_NO_ERROR;
3111251881Speter}
3112251881Speter
3113251881Speter
3114251881Speter/* An svn_delta_editor_t function. */
3115251881Speterstatic svn_error_t *
3116251881Speterabsent_file(const char *path,
3117251881Speter            void *parent_baton,
3118251881Speter            apr_pool_t *pool)
3119251881Speter{
3120251881Speter  return absent_node(path, svn_node_file, parent_baton, pool);
3121251881Speter}
3122251881Speter
3123251881Speter
3124251881Speter/* An svn_delta_editor_t function. */
3125251881Speterstatic svn_error_t *
3126251881Speterabsent_directory(const char *path,
3127251881Speter                 void *parent_baton,
3128251881Speter                 apr_pool_t *pool)
3129251881Speter{
3130251881Speter  return absent_node(path, svn_node_dir, parent_baton, pool);
3131251881Speter}
3132251881Speter
3133251881Speter
3134251881Speter/* An svn_delta_editor_t function. */
3135251881Speterstatic svn_error_t *
3136251881Speteradd_file(const char *path,
3137251881Speter         void *parent_baton,
3138251881Speter         const char *copyfrom_path,
3139251881Speter         svn_revnum_t copyfrom_rev,
3140251881Speter         apr_pool_t *pool,
3141251881Speter         void **file_baton)
3142251881Speter{
3143251881Speter  struct dir_baton *pb = parent_baton;
3144251881Speter  struct edit_baton *eb = pb->edit_baton;
3145251881Speter  struct file_baton *fb;
3146251881Speter  svn_node_kind_t kind = svn_node_none;
3147251881Speter  svn_node_kind_t wc_kind = svn_node_unknown;
3148251881Speter  svn_wc__db_status_t status = svn_wc__db_status_normal;
3149251881Speter  apr_pool_t *scratch_pool;
3150251881Speter  svn_boolean_t conflicted = FALSE;
3151251881Speter  svn_boolean_t conflict_ignored = FALSE;
3152251881Speter  svn_boolean_t versioned_locally_and_present = FALSE;
3153251881Speter  svn_skel_t *tree_conflict = NULL;
3154251881Speter  svn_error_t *err = SVN_NO_ERROR;
3155251881Speter
3156251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3157251881Speter
3158251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3159251881Speter  *file_baton = fb;
3160251881Speter
3161251881Speter  if (fb->skip_this)
3162251881Speter    return SVN_NO_ERROR;
3163251881Speter
3164251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3165251881Speter
3166251881Speter  /* The file_pool can stick around for a *long* time, so we want to
3167251881Speter     use a subpool for any temporary allocations. */
3168251881Speter  scratch_pool = svn_pool_create(pool);
3169251881Speter
3170251881Speter
3171251881Speter  /* It may not be named the same as the administrative directory. */
3172251881Speter  if (svn_wc_is_adm_dir(fb->name, pool))
3173251881Speter    return svn_error_createf(
3174251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3175251881Speter       _("Failed to add file '%s': object of the same name as the "
3176251881Speter         "administrative directory"),
3177251881Speter       svn_dirent_local_style(fb->local_abspath, pool));
3178251881Speter
3179251881Speter  if (!eb->clean_checkout)
3180251881Speter    {
3181251881Speter      SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3182251881Speter
3183251881Speter      err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3184251881Speter                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3185251881Speter                                NULL, NULL, NULL, NULL, NULL,
3186251881Speter                                &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3187251881Speter                                eb->db, fb->local_abspath,
3188251881Speter                                scratch_pool, scratch_pool);
3189251881Speter    }
3190251881Speter
3191251881Speter  if (err)
3192251881Speter    {
3193251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194251881Speter        return svn_error_trace(err);
3195251881Speter
3196251881Speter      svn_error_clear(err);
3197251881Speter      wc_kind = svn_node_unknown;
3198251881Speter      conflicted = FALSE;
3199251881Speter
3200251881Speter      versioned_locally_and_present = FALSE;
3201251881Speter    }
3202251881Speter  else if (wc_kind == svn_node_dir
3203251881Speter           && status == svn_wc__db_status_normal)
3204251881Speter    {
3205251881Speter      /* !! We found the root of a separate working copy obstructing the wc !!
3206251881Speter
3207251881Speter         If the directory would be part of our own working copy then
3208251881Speter         we wouldn't have been called as an add_file().
3209251881Speter
3210251881Speter         The only thing we can do is add a not-present node, to allow
3211251881Speter         a future update to bring in the new files when the problem is
3212251881Speter         resolved. */
3213251881Speter      svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3214251881Speter                    (void *)1);
3215251881Speter
3216251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3217251881Speter      fb->skip_this = TRUE;
3218251881Speter      fb->already_notified = TRUE;
3219251881Speter
3220251881Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3221251881Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
3222251881Speter
3223251881Speter      svn_pool_destroy(scratch_pool);
3224251881Speter
3225251881Speter      return SVN_NO_ERROR;
3226251881Speter    }
3227251881Speter  else if (status == svn_wc__db_status_normal
3228251881Speter           && (wc_kind == svn_node_file
3229251881Speter               || wc_kind == svn_node_symlink))
3230251881Speter    {
3231251881Speter      /* We found a file external occupating the place we need in BASE.
3232251881Speter
3233251881Speter         We can't add a not-present node in this case as that would overwrite
3234251881Speter         the file external. Luckily the file external itself stops us from
3235251881Speter         forgetting a child of this parent directory like an obstructing
3236251881Speter         working copy would.
3237251881Speter
3238251881Speter         The reason we get here is that the adm crawler doesn't report
3239251881Speter         file externals.
3240251881Speter      */
3241251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3242251881Speter      fb->skip_this = TRUE;
3243251881Speter      fb->already_notified = TRUE;
3244251881Speter
3245251881Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3246251881Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
3247251881Speter
3248251881Speter      svn_pool_destroy(scratch_pool);
3249251881Speter
3250251881Speter      return SVN_NO_ERROR;
3251251881Speter    }
3252251881Speter  else if (wc_kind == svn_node_unknown)
3253251881Speter    versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3254251881Speter  else
3255251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
3256251881Speter
3257251881Speter
3258251881Speter  /* Is this path a conflict victim? */
3259251881Speter  if (fb->shadowed)
3260251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3261251881Speter  else if (conflicted)
3262251881Speter    {
3263251881Speter      if (pb->deletion_conflicts)
3264251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3265251881Speter
3266251881Speter      if (tree_conflict)
3267251881Speter        {
3268251881Speter          svn_wc_conflict_reason_t reason;
3269251881Speter          /* So this deletion wasn't just a deletion, it is actually a
3270251881Speter             replacement. Let's install a better tree conflict. */
3271251881Speter
3272251881Speter          /* ### Should store the conflict in DB to allow reinstalling
3273251881Speter             ### with theoretically more data in close_directory() */
3274251881Speter
3275251881Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3276251881Speter                                                      eb->db,
3277251881Speter                                                      fb->local_abspath,
3278251881Speter                                                      tree_conflict,
3279251881Speter                                                      fb->pool, fb->pool));
3280251881Speter
3281251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3282251881Speter
3283251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3284251881Speter                                        tree_conflict,
3285251881Speter                                        eb->db, fb->local_abspath,
3286251881Speter                                        reason, svn_wc_conflict_action_replace,
3287251881Speter                                        NULL,
3288251881Speter                                        fb->pool, fb->pool));
3289251881Speter
3290251881Speter          /* And now stop checking for conflicts here and just perform
3291251881Speter             a shadowed update */
3292251881Speter          fb->edit_conflict = tree_conflict; /* Cache for close_file */
3293251881Speter          tree_conflict = NULL; /* No direct notification */
3294251881Speter          fb->shadowed = TRUE; /* Just continue */
3295251881Speter          conflicted = FALSE; /* No skip */
3296251881Speter        }
3297251881Speter      else
3298251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3299251881Speter                                        eb->db, fb->local_abspath, pool));
3300251881Speter    }
3301251881Speter
3302251881Speter  /* Now the usual conflict handling: skip. */
3303251881Speter  if (conflicted)
3304251881Speter    {
3305251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3306251881Speter
3307251881Speter      fb->skip_this = TRUE;
3308251881Speter      fb->already_notified = TRUE;
3309251881Speter
3310251881Speter      /* We skip this node, but once the update completes the parent node will
3311251881Speter         be updated to the new revision. So a future recursive update of the
3312251881Speter         parent will not bring in this new node as the revision of the parent
3313251881Speter         describes to the repository that all children are available.
3314251881Speter
3315251881Speter         To resolve this problem, we add a not-present node to allow bringing
3316251881Speter         the node in once this conflict is resolved.
3317251881Speter
3318251881Speter         Note that we can safely assume that no present base node exists,
3319251881Speter         because then we would not have received an add_file.
3320251881Speter       */
3321251881Speter      svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3322251881Speter                    (void *)1);
3323251881Speter
3324251881Speter      do_notification(eb, fb->local_abspath, svn_node_unknown,
3325251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3326251881Speter
3327251881Speter      svn_pool_destroy(scratch_pool);
3328251881Speter
3329251881Speter      return SVN_NO_ERROR;
3330251881Speter    }
3331251881Speter  else if (conflict_ignored)
3332251881Speter    {
3333251881Speter      fb->shadowed = TRUE;
3334251881Speter    }
3335251881Speter
3336251881Speter  if (fb->shadowed)
3337251881Speter    {
3338251881Speter      /* Nothing to check; does not and will not exist in working copy */
3339251881Speter    }
3340251881Speter  else if (versioned_locally_and_present)
3341251881Speter    {
3342251881Speter      /* What to do with a versioned or schedule-add file:
3343251881Speter
3344251881Speter         If the UUID doesn't match the parent's, or the URL isn't a child of
3345251881Speter         the parent dir's URL, it's an error.
3346251881Speter
3347251881Speter         Set add_existed so that user notification is delayed until after any
3348251881Speter         text or prop conflicts have been found.
3349251881Speter
3350251881Speter         Whether the incoming add is a symlink or a file will only be known in
3351251881Speter         close_file(), when the props are known. So with a locally added file
3352251881Speter         or symlink, let close_file() check for a tree conflict.
3353251881Speter
3354251881Speter         We will never see missing files here, because these would be
3355251881Speter         re-added during the crawler phase. */
3356251881Speter      svn_boolean_t local_is_file;
3357251881Speter
3358251881Speter      /* Is the local node a copy or move */
3359251881Speter      if (status == svn_wc__db_status_added)
3360251881Speter        SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3361251881Speter                                         NULL, NULL, NULL,
3362251881Speter                                         eb->db, fb->local_abspath,
3363251881Speter                                         scratch_pool, scratch_pool));
3364251881Speter
3365251881Speter      /* Is there something that is a file? */
3366251881Speter      local_is_file = (wc_kind == svn_node_file
3367251881Speter                       || wc_kind == svn_node_symlink);
3368251881Speter
3369251881Speter      /* Do tree conflict checking if
3370251881Speter       *  - if there is a local copy.
3371251881Speter       *  - if this is a switch operation
3372251881Speter       *  - the node kinds mismatch
3373251881Speter       *
3374251881Speter       * During switch, local adds at the same path as incoming adds get
3375251881Speter       * "lost" in that switching back to the original will no longer have the
3376251881Speter       * local add. So switch always alerts the user with a tree conflict. */
3377251881Speter      if (!eb->adds_as_modification
3378251881Speter          || !local_is_file
3379251881Speter          || status != svn_wc__db_status_added)
3380251881Speter        {
3381251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3382251881Speter                                      fb->local_abspath,
3383251881Speter                                      status, FALSE, svn_node_none,
3384251881Speter                                      svn_wc_conflict_action_add,
3385251881Speter                                      scratch_pool, scratch_pool));
3386251881Speter        }
3387251881Speter
3388251881Speter      if (tree_conflict == NULL)
3389251881Speter        fb->add_existed = TRUE; /* Take over WORKING */
3390251881Speter      else
3391251881Speter        fb->shadowed = TRUE; /* Only update BASE */
3392251881Speter
3393251881Speter    }
3394251881Speter  else if (kind != svn_node_none)
3395251881Speter    {
3396251881Speter      /* There's an unversioned node at this path. */
3397251881Speter      fb->obstruction_found = TRUE;
3398251881Speter
3399251881Speter      /* Unversioned, obstructing files are handled by text merge/conflict,
3400251881Speter       * if unversioned obstructions are allowed. */
3401251881Speter      if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3402251881Speter        {
3403251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3404251881Speter          fb->shadowed = TRUE;
3405251881Speter
3406251881Speter          /* Mark a conflict */
3407251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3408251881Speter
3409251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3410251881Speter                                        tree_conflict,
3411251881Speter                                        eb->db, fb->local_abspath,
3412251881Speter                                        svn_wc_conflict_reason_unversioned,
3413251881Speter                                        svn_wc_conflict_action_add,
3414251881Speter                                        NULL,
3415251881Speter                                        fb->pool, scratch_pool));
3416251881Speter        }
3417251881Speter    }
3418251881Speter
3419251881Speter  /* When this is not the update target add a not-present BASE node now,
3420251881Speter     to allow marking the parent directory complete in its close_edit() call.
3421251881Speter     This resolves issues when that occurs before the close_file(). */
3422251881Speter  if (pb->parent_baton
3423251881Speter      || *eb->target_basename == '\0'
3424251881Speter      || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3425251881Speter    {
3426251881Speter      svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3427251881Speter                    (void *)1);
3428251881Speter    }
3429251881Speter
3430251881Speter  if (tree_conflict != NULL)
3431251881Speter    {
3432251881Speter      SVN_ERR(complete_conflict(tree_conflict,
3433251881Speter                                fb->edit_baton,
3434251881Speter                                fb->local_abspath,
3435251881Speter                                fb->old_repos_relpath,
3436251881Speter                                fb->old_revision,
3437251881Speter                                fb->new_relpath,
3438251881Speter                                wc_kind,
3439251881Speter                                svn_node_file,
3440251881Speter                                fb->pool, scratch_pool));
3441251881Speter
3442251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3443251881Speter                                          fb->local_abspath,
3444251881Speter                                          tree_conflict, NULL,
3445251881Speter                                          scratch_pool));
3446251881Speter
3447253734Speter      if (eb->conflict_func)
3448253734Speter        SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3449253734Speter                                                 tree_conflict,
3450253734Speter                                                 NULL /* merge_options */,
3451253734Speter                                                 eb->conflict_func,
3452253734Speter                                                 eb->conflict_baton,
3453253734Speter                                                 eb->cancel_func,
3454253734Speter                                                 eb->cancel_baton,
3455253734Speter                                                 scratch_pool));
3456253734Speter
3457251881Speter      fb->already_notified = TRUE;
3458251881Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3459251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
3460251881Speter    }
3461251881Speter
3462251881Speter  svn_pool_destroy(scratch_pool);
3463251881Speter
3464251881Speter  return SVN_NO_ERROR;
3465251881Speter}
3466251881Speter
3467251881Speter
3468251881Speter/* An svn_delta_editor_t function. */
3469251881Speterstatic svn_error_t *
3470251881Speteropen_file(const char *path,
3471251881Speter          void *parent_baton,
3472251881Speter          svn_revnum_t base_revision,
3473251881Speter          apr_pool_t *pool,
3474251881Speter          void **file_baton)
3475251881Speter{
3476251881Speter  struct dir_baton *pb = parent_baton;
3477251881Speter  struct edit_baton *eb = pb->edit_baton;
3478251881Speter  struct file_baton *fb;
3479251881Speter  svn_boolean_t conflicted;
3480251881Speter  svn_boolean_t conflict_ignored = FALSE;
3481251881Speter  svn_boolean_t have_work;
3482251881Speter  svn_wc__db_status_t status;
3483251881Speter  svn_node_kind_t wc_kind;
3484251881Speter  svn_skel_t *tree_conflict = NULL;
3485251881Speter
3486251881Speter  /* the file_pool can stick around for a *long* time, so we want to use
3487251881Speter     a subpool for any temporary allocations. */
3488251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3489251881Speter
3490251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3491251881Speter  *file_baton = fb;
3492251881Speter
3493251881Speter  if (fb->skip_this)
3494251881Speter    return SVN_NO_ERROR;
3495251881Speter
3496251881Speter  /* Detect obstructing working copies */
3497251881Speter  {
3498251881Speter    svn_boolean_t is_root;
3499251881Speter
3500251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3501251881Speter                                 pool));
3502251881Speter
3503251881Speter    if (is_root)
3504251881Speter      {
3505251881Speter        /* Just skip this node; a future update will handle it */
3506251881Speter        SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3507251881Speter        fb->skip_this = TRUE;
3508251881Speter        fb->already_notified = TRUE;
3509251881Speter
3510251881Speter        do_notification(eb, fb->local_abspath, svn_node_file,
3511251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
3512251881Speter
3513251881Speter        return SVN_NO_ERROR;
3514251881Speter      }
3515251881Speter  }
3516251881Speter
3517251881Speter  /* Sanity check. */
3518251881Speter
3519251881Speter  /* If replacing, make sure the .svn entry already exists. */
3520251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3521251881Speter                               &fb->old_repos_relpath, NULL, NULL,
3522251881Speter                               &fb->changed_rev, &fb->changed_date,
3523251881Speter                               &fb->changed_author, NULL,
3524251881Speter                               &fb->original_checksum, NULL, NULL, NULL,
3525251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
3526251881Speter                               &conflicted, NULL, NULL, &fb->local_prop_mods,
3527251881Speter                               NULL, NULL, &have_work,
3528251881Speter                               eb->db, fb->local_abspath,
3529251881Speter                               fb->pool, scratch_pool));
3530251881Speter
3531251881Speter  if (have_work)
3532251881Speter    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3533251881Speter                                     &fb->old_repos_relpath, NULL, NULL,
3534251881Speter                                     &fb->changed_rev, &fb->changed_date,
3535251881Speter                                     &fb->changed_author, NULL,
3536251881Speter                                     &fb->original_checksum, NULL, NULL,
3537251881Speter                                     NULL, NULL, NULL,
3538251881Speter                                     eb->db, fb->local_abspath,
3539251881Speter                                     fb->pool, scratch_pool));
3540251881Speter
3541251881Speter  /* Is this path a conflict victim? */
3542251881Speter  if (fb->shadowed)
3543251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3544251881Speter  else if (conflicted)
3545251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3546251881Speter                                    eb->db, fb->local_abspath, pool));
3547251881Speter  if (conflicted)
3548251881Speter    {
3549251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3550251881Speter
3551251881Speter      fb->skip_this = TRUE;
3552251881Speter      fb->already_notified = TRUE;
3553251881Speter
3554251881Speter      do_notification(eb, fb->local_abspath, svn_node_unknown,
3555251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3556251881Speter
3557251881Speter      svn_pool_destroy(scratch_pool);
3558251881Speter
3559251881Speter      return SVN_NO_ERROR;
3560251881Speter    }
3561251881Speter  else if (conflict_ignored)
3562251881Speter    {
3563251881Speter      fb->shadowed = TRUE;
3564251881Speter    }
3565251881Speter
3566251881Speter  /* Check for conflicts only when we haven't already recorded
3567251881Speter   * a tree-conflict on a parent node. */
3568251881Speter  if (!fb->shadowed)
3569251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3570251881Speter                                status, TRUE, svn_node_file,
3571251881Speter                                svn_wc_conflict_action_edit,
3572251881Speter                                fb->pool, scratch_pool));
3573251881Speter
3574251881Speter  /* Is this path the victim of a newly-discovered tree conflict? */
3575251881Speter  if (tree_conflict != NULL)
3576251881Speter    {
3577251881Speter      svn_wc_conflict_reason_t reason;
3578251881Speter      fb->edit_conflict = tree_conflict;
3579251881Speter      /* Other modifications wouldn't be a tree conflict */
3580251881Speter
3581251881Speter      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3582251881Speter                                                  eb->db, fb->local_abspath,
3583251881Speter                                                  tree_conflict,
3584251881Speter                                                  scratch_pool, scratch_pool));
3585251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3586251881Speter                     || reason == svn_wc_conflict_reason_moved_away
3587251881Speter                     || reason == svn_wc_conflict_reason_replaced
3588251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
3589251881Speter
3590251881Speter      /* Continue updating BASE */
3591251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
3592251881Speter        fb->edit_obstructed = TRUE;
3593251881Speter      else
3594251881Speter        fb->shadowed = TRUE;
3595251881Speter    }
3596251881Speter
3597251881Speter  svn_pool_destroy(scratch_pool);
3598251881Speter
3599251881Speter  return SVN_NO_ERROR;
3600251881Speter}
3601251881Speter
3602251881Speter/* Implements svn_stream_lazyopen_func_t. */
3603251881Speterstatic svn_error_t *
3604251881Speterlazy_open_source(svn_stream_t **stream,
3605251881Speter                 void *baton,
3606251881Speter                 apr_pool_t *result_pool,
3607251881Speter                 apr_pool_t *scratch_pool)
3608251881Speter{
3609251881Speter  struct file_baton *fb = baton;
3610251881Speter
3611251881Speter  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3612251881Speter                                   fb->local_abspath,
3613251881Speter                                   fb->original_checksum,
3614251881Speter                                   result_pool, scratch_pool));
3615251881Speter
3616251881Speter
3617251881Speter  return SVN_NO_ERROR;
3618251881Speter}
3619251881Speter
3620251881Speterstruct lazy_target_baton {
3621251881Speter  struct file_baton *fb;
3622251881Speter  struct handler_baton *hb;
3623251881Speter  struct edit_baton *eb;
3624251881Speter};
3625251881Speter
3626251881Speter/* Implements svn_stream_lazyopen_func_t. */
3627251881Speterstatic svn_error_t *
3628251881Speterlazy_open_target(svn_stream_t **stream,
3629251881Speter                 void *baton,
3630251881Speter                 apr_pool_t *result_pool,
3631251881Speter                 apr_pool_t *scratch_pool)
3632251881Speter{
3633251881Speter  struct lazy_target_baton *tb = baton;
3634251881Speter
3635251881Speter  SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3636251881Speter                                     NULL, &tb->hb->new_text_base_sha1_checksum,
3637251881Speter                                     tb->fb->edit_baton->db,
3638251881Speter                                     tb->eb->wcroot_abspath,
3639251881Speter                                     result_pool, scratch_pool));
3640251881Speter
3641251881Speter  return SVN_NO_ERROR;
3642251881Speter}
3643251881Speter
3644251881Speter/* An svn_delta_editor_t function. */
3645251881Speterstatic svn_error_t *
3646251881Speterapply_textdelta(void *file_baton,
3647251881Speter                const char *expected_checksum,
3648251881Speter                apr_pool_t *pool,
3649251881Speter                svn_txdelta_window_handler_t *handler,
3650251881Speter                void **handler_baton)
3651251881Speter{
3652251881Speter  struct file_baton *fb = file_baton;
3653251881Speter  apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3654251881Speter  struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3655251881Speter  struct edit_baton *eb = fb->edit_baton;
3656251881Speter  const svn_checksum_t *recorded_base_checksum;
3657251881Speter  svn_checksum_t *expected_base_checksum;
3658251881Speter  svn_stream_t *source;
3659251881Speter  struct lazy_target_baton *tb;
3660251881Speter  svn_stream_t *target;
3661251881Speter
3662251881Speter  if (fb->skip_this)
3663251881Speter    {
3664251881Speter      *handler = svn_delta_noop_window_handler;
3665251881Speter      *handler_baton = NULL;
3666251881Speter      return SVN_NO_ERROR;
3667251881Speter    }
3668251881Speter
3669251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3670251881Speter
3671251881Speter  /* Parse checksum or sets expected_base_checksum to NULL */
3672251881Speter  SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3673251881Speter                                 expected_checksum, pool));
3674251881Speter
3675251881Speter  /* Before applying incoming svndiff data to text base, make sure
3676251881Speter     text base hasn't been corrupted, and that its checksum
3677251881Speter     matches the expected base checksum. */
3678251881Speter
3679251881Speter  /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3680251881Speter     check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3681251881Speter     for replaced nodes because we didn't store the checksum of the "revert
3682251881Speter     base".  In WC-NG, we do and we can.) */
3683251881Speter  recorded_base_checksum = fb->original_checksum;
3684251881Speter
3685251881Speter  /* If we have a checksum that we want to compare to a MD5 checksum,
3686251881Speter     ensure that it is a MD5 checksum */
3687251881Speter  if (recorded_base_checksum
3688251881Speter      && expected_base_checksum
3689251881Speter      && recorded_base_checksum->kind != svn_checksum_md5)
3690251881Speter    SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3691251881Speter                                        eb->db, eb->wcroot_abspath,
3692251881Speter                                        recorded_base_checksum, pool, pool));
3693251881Speter
3694251881Speter
3695251881Speter  if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3696251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3697251881Speter                     _("Checksum mismatch for '%s':\n"
3698251881Speter                       "   expected:  %s\n"
3699251881Speter                       "   recorded:  %s\n"),
3700251881Speter                     svn_dirent_local_style(fb->local_abspath, pool),
3701251881Speter                     svn_checksum_to_cstring_display(expected_base_checksum,
3702251881Speter                                                     pool),
3703251881Speter                     svn_checksum_to_cstring_display(recorded_base_checksum,
3704251881Speter                                                     pool));
3705251881Speter
3706251881Speter  /* Open the text base for reading, unless this is an added file. */
3707251881Speter
3708251881Speter  /*
3709251881Speter     kff todo: what we really need to do here is:
3710251881Speter
3711251881Speter     1. See if there's a file or dir by this name already here.
3712251881Speter     2. See if it's under revision control.
3713251881Speter     3. If both are true, open text-base.
3714251881Speter     4. If only 1 is true, bail, because we can't go destroying user's
3715251881Speter        files (or as an alternative to bailing, move it to some tmp
3716251881Speter        name and somehow tell the user, but communicating with the
3717251881Speter        user without erroring is a whole callback system we haven't
3718251881Speter        finished inventing yet.)
3719251881Speter  */
3720251881Speter
3721251881Speter  if (! fb->adding_file)
3722251881Speter    {
3723251881Speter      SVN_ERR_ASSERT(!fb->original_checksum
3724251881Speter                     || fb->original_checksum->kind == svn_checksum_sha1);
3725251881Speter
3726251881Speter      source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3727251881Speter                                          handler_pool);
3728251881Speter    }
3729251881Speter  else
3730251881Speter    {
3731251881Speter      source = svn_stream_empty(handler_pool);
3732251881Speter    }
3733251881Speter
3734251881Speter  /* If we don't have a recorded checksum, use the ra provided checksum */
3735251881Speter  if (!recorded_base_checksum)
3736251881Speter    recorded_base_checksum = expected_base_checksum;
3737251881Speter
3738251881Speter  /* Checksum the text base while applying deltas */
3739251881Speter  if (recorded_base_checksum)
3740251881Speter    {
3741251881Speter      hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3742251881Speter                                                      handler_pool);
3743251881Speter
3744251881Speter      /* Wrap stream and store reference to allow calculating the
3745251881Speter         checksum. */
3746251881Speter      source = svn_stream_checksummed2(source,
3747251881Speter                                       &hb->actual_source_checksum,
3748251881Speter                                       NULL, recorded_base_checksum->kind,
3749251881Speter                                       TRUE, handler_pool);
3750251881Speter      hb->source_checksum_stream = source;
3751251881Speter    }
3752251881Speter
3753251881Speter  tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3754251881Speter  tb->hb = hb;
3755251881Speter  tb->fb = fb;
3756251881Speter  tb->eb = eb;
3757251881Speter  target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3758251881Speter
3759251881Speter  /* Prepare to apply the delta.  */
3760251881Speter  svn_txdelta_apply(source, target,
3761251881Speter                    hb->new_text_base_md5_digest,
3762251881Speter                    hb->new_text_base_tmp_abspath /* error_info */,
3763251881Speter                    handler_pool,
3764251881Speter                    &hb->apply_handler, &hb->apply_baton);
3765251881Speter
3766251881Speter  hb->pool = handler_pool;
3767251881Speter  hb->fb = fb;
3768251881Speter
3769251881Speter  /* We're all set.  */
3770251881Speter  *handler_baton = hb;
3771251881Speter  *handler = window_handler;
3772251881Speter
3773251881Speter  return SVN_NO_ERROR;
3774251881Speter}
3775251881Speter
3776251881Speter
3777251881Speter/* An svn_delta_editor_t function. */
3778251881Speterstatic svn_error_t *
3779251881Speterchange_file_prop(void *file_baton,
3780251881Speter                 const char *name,
3781251881Speter                 const svn_string_t *value,
3782251881Speter                 apr_pool_t *scratch_pool)
3783251881Speter{
3784251881Speter  struct file_baton *fb = file_baton;
3785251881Speter  svn_prop_t *propchange;
3786251881Speter
3787251881Speter  if (fb->skip_this)
3788251881Speter    return SVN_NO_ERROR;
3789251881Speter
3790251881Speter  /* Push a new propchange to the file baton's array of propchanges */
3791251881Speter  propchange = apr_array_push(fb->propchanges);
3792251881Speter  propchange->name = apr_pstrdup(fb->pool, name);
3793251881Speter  propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3794251881Speter
3795251881Speter  if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3796251881Speter    SVN_ERR(mark_file_edited(fb, scratch_pool));
3797251881Speter
3798251881Speter  if (! fb->shadowed
3799251881Speter      && strcmp(name, SVN_PROP_SPECIAL) == 0)
3800251881Speter    {
3801251881Speter      struct edit_baton *eb = fb->edit_baton;
3802251881Speter      svn_boolean_t modified = FALSE;
3803251881Speter      svn_boolean_t becomes_symlink;
3804251881Speter      svn_boolean_t was_symlink;
3805251881Speter
3806251881Speter      /* Let's see if we have a change as in some scenarios servers report
3807251881Speter         non-changes of properties. */
3808251881Speter      becomes_symlink = (value != NULL);
3809251881Speter
3810251881Speter      if (fb->adding_file)
3811251881Speter        was_symlink = becomes_symlink; /* No change */
3812251881Speter      else
3813251881Speter        {
3814251881Speter          apr_hash_t *props;
3815251881Speter
3816251881Speter          /* We read the server-props, not the ACTUAL props here as we just
3817251881Speter             want to see if this is really an incoming prop change. */
3818251881Speter          SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3819251881Speter                                            fb->local_abspath,
3820251881Speter                                            scratch_pool, scratch_pool));
3821251881Speter
3822251881Speter          was_symlink = ((props
3823251881Speter                              && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3824251881Speter                              ? svn_tristate_true
3825251881Speter                              : svn_tristate_false);
3826251881Speter        }
3827251881Speter
3828251881Speter      if (was_symlink != becomes_symlink)
3829251881Speter        {
3830251881Speter          /* If the local node was not modified, we continue as usual, if
3831251881Speter             modified we want a tree conflict just like how we would handle
3832251881Speter             it when receiving a delete + add (aka "replace") */
3833251881Speter          if (fb->local_prop_mods)
3834251881Speter            modified = TRUE;
3835251881Speter          else
3836251881Speter            SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3837251881Speter                                                     fb->local_abspath,
3838251881Speter                                                     FALSE, scratch_pool));
3839251881Speter        }
3840251881Speter
3841251881Speter      if (modified)
3842251881Speter        {
3843251881Speter          if (!fb->edit_conflict)
3844251881Speter            fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3845251881Speter
3846251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3847251881Speter                                     fb->edit_conflict,
3848251881Speter                                     eb->db, fb->local_abspath,
3849251881Speter                                     svn_wc_conflict_reason_edited,
3850251881Speter                                     svn_wc_conflict_action_replace,
3851251881Speter                                     NULL,
3852251881Speter                                     fb->pool, scratch_pool));
3853251881Speter
3854251881Speter          SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3855251881Speter                                    fb->local_abspath, fb->old_repos_relpath,
3856251881Speter                                    fb->old_revision, fb->new_relpath,
3857251881Speter                                    svn_node_file, svn_node_file,
3858251881Speter                                    fb->pool, scratch_pool));
3859251881Speter
3860251881Speter          /* Create a copy of the existing (pre update) BASE node in WORKING,
3861251881Speter             mark a tree conflict and handle the rest of the update as
3862251881Speter             shadowed */
3863251881Speter          SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3864251881Speter                                          fb->edit_conflict, NULL,
3865251881Speter                                          scratch_pool));
3866251881Speter
3867251881Speter          do_notification(eb, fb->local_abspath, svn_node_file,
3868251881Speter                          svn_wc_notify_tree_conflict, scratch_pool);
3869251881Speter
3870251881Speter          /* Ok, we introduced a replacement, so we can now handle the rest
3871251881Speter             as a normal shadowed update */
3872251881Speter          fb->shadowed = TRUE;
3873251881Speter          fb->add_existed = FALSE;
3874251881Speter          fb->already_notified = TRUE;
3875251881Speter      }
3876251881Speter    }
3877251881Speter
3878251881Speter  return SVN_NO_ERROR;
3879251881Speter}
3880251881Speter
3881251881Speter/* Perform the actual merge of file changes between an original file,
3882251881Speter   identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3883251881Speter   identified by NEW_CHECKSUM.
3884251881Speter
3885251881Speter   Merge the result into LOCAL_ABSPATH, which is part of the working copy
3886251881Speter   identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3887251881Speter   the intermediate files.
3888251881Speter
3889251881Speter   The rest of the arguments are passed to svn_wc__internal_merge().
3890251881Speter */
3891251881Spetersvn_error_t *
3892251881Spetersvn_wc__perform_file_merge(svn_skel_t **work_items,
3893251881Speter                           svn_skel_t **conflict_skel,
3894251881Speter                           svn_boolean_t *found_conflict,
3895251881Speter                           svn_wc__db_t *db,
3896251881Speter                           const char *local_abspath,
3897251881Speter                           const char *wri_abspath,
3898251881Speter                           const svn_checksum_t *new_checksum,
3899251881Speter                           const svn_checksum_t *original_checksum,
3900251881Speter                           apr_hash_t *old_actual_props,
3901251881Speter                           const apr_array_header_t *ext_patterns,
3902251881Speter                           svn_revnum_t old_revision,
3903251881Speter                           svn_revnum_t target_revision,
3904251881Speter                           const apr_array_header_t *propchanges,
3905251881Speter                           const char *diff3_cmd,
3906251881Speter                           svn_cancel_func_t cancel_func,
3907251881Speter                           void *cancel_baton,
3908251881Speter                           apr_pool_t *result_pool,
3909251881Speter                           apr_pool_t *scratch_pool)
3910251881Speter{
3911251881Speter  /* Actual file exists and has local mods:
3912251881Speter     Now we need to let loose svn_wc__internal_merge() to merge
3913251881Speter     the textual changes into the working file. */
3914251881Speter  const char *oldrev_str, *newrev_str, *mine_str;
3915251881Speter  const char *merge_left;
3916251881Speter  svn_boolean_t delete_left = FALSE;
3917251881Speter  const char *path_ext = "";
3918251881Speter  const char *new_text_base_tmp_abspath;
3919251881Speter  enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3920251881Speter  svn_skel_t *work_item;
3921251881Speter
3922251881Speter  *work_items = NULL;
3923251881Speter
3924251881Speter  SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3925251881Speter                                       db, wri_abspath, new_checksum,
3926251881Speter                                       scratch_pool, scratch_pool));
3927251881Speter
3928251881Speter  /* If we have any file extensions we're supposed to
3929251881Speter     preserve in generated conflict file names, then find
3930251881Speter     this path's extension.  But then, if it isn't one of
3931251881Speter     the ones we want to keep in conflict filenames,
3932251881Speter     pretend it doesn't have an extension at all. */
3933251881Speter  if (ext_patterns && ext_patterns->nelts)
3934251881Speter    {
3935251881Speter      svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3936251881Speter      if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3937251881Speter        path_ext = "";
3938251881Speter    }
3939251881Speter
3940251881Speter  /* old_revision can be invalid when the conflict is against a
3941251881Speter     local addition */
3942251881Speter  if (!SVN_IS_VALID_REVNUM(old_revision))
3943251881Speter    old_revision = 0;
3944251881Speter
3945251881Speter  oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3946251881Speter                            old_revision,
3947251881Speter                            *path_ext ? "." : "",
3948251881Speter                            *path_ext ? path_ext : "");
3949251881Speter
3950251881Speter  newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3951251881Speter                            target_revision,
3952251881Speter                            *path_ext ? "." : "",
3953251881Speter                            *path_ext ? path_ext : "");
3954251881Speter  mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3955251881Speter                          *path_ext ? "." : "",
3956251881Speter                          *path_ext ? path_ext : "");
3957251881Speter
3958251881Speter  if (! original_checksum)
3959251881Speter    {
3960251881Speter      SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3961251881Speter                                 result_pool, scratch_pool));
3962251881Speter      delete_left = TRUE;
3963251881Speter    }
3964251881Speter  else
3965251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3966251881Speter                                         original_checksum,
3967251881Speter                                         result_pool, scratch_pool));
3968251881Speter
3969251881Speter  /* Merge the changes from the old textbase to the new
3970251881Speter     textbase into the file we're updating.
3971251881Speter     Remember that this function wants full paths! */
3972251881Speter  SVN_ERR(svn_wc__internal_merge(&work_item,
3973251881Speter                                 conflict_skel,
3974251881Speter                                 &merge_outcome,
3975251881Speter                                 db,
3976251881Speter                                 merge_left,
3977251881Speter                                 new_text_base_tmp_abspath,
3978251881Speter                                 local_abspath,
3979251881Speter                                 wri_abspath,
3980251881Speter                                 oldrev_str, newrev_str, mine_str,
3981251881Speter                                 old_actual_props,
3982251881Speter                                 FALSE /* dry_run */,
3983251881Speter                                 diff3_cmd, NULL, propchanges,
3984251881Speter                                 cancel_func, cancel_baton,
3985251881Speter                                 result_pool, scratch_pool));
3986251881Speter
3987251881Speter  *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3988251881Speter  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3989251881Speter
3990251881Speter  /* If we created a temporary left merge file, get rid of it. */
3991251881Speter  if (delete_left)
3992251881Speter    {
3993251881Speter      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3994251881Speter                                           merge_left,
3995251881Speter                                           result_pool, scratch_pool));
3996251881Speter      *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3997251881Speter    }
3998251881Speter
3999251881Speter  return SVN_NO_ERROR;
4000251881Speter}
4001251881Speter
4002251881Speter/* This is the small planet.  It has the complex responsibility of
4003251881Speter * "integrating" a new revision of a file into a working copy.
4004251881Speter *
4005251881Speter * Given a file_baton FB for a file either already under version control, or
4006251881Speter * prepared (see below) to join version control, fully install a
4007251881Speter * new revision of the file.
4008251881Speter *
4009251881Speter * ### transitional: installation of the working file will be handled
4010251881Speter * ### by the *INSTALL_PRISTINE flag.
4011251881Speter *
4012251881Speter * By "install", we mean: create a new text-base and prop-base, merge
4013251881Speter * any textual and property changes into the working file, and finally
4014251881Speter * update all metadata so that the working copy believes it has a new
4015251881Speter * working revision of the file.  All of this work includes being
4016251881Speter * sensitive to eol translation, keyword substitution, and performing
4017251881Speter * all actions accumulated the parent directory's work queue.
4018251881Speter *
4019251881Speter * Set *CONTENT_STATE to the state of the contents after the
4020251881Speter * installation.
4021251881Speter *
4022251881Speter * Return values are allocated in RESULT_POOL and temporary allocations
4023251881Speter * are performed in SCRATCH_POOL.
4024251881Speter */
4025251881Speterstatic svn_error_t *
4026251881Spetermerge_file(svn_skel_t **work_items,
4027251881Speter           svn_skel_t **conflict_skel,
4028251881Speter           svn_boolean_t *install_pristine,
4029251881Speter           const char **install_from,
4030251881Speter           svn_wc_notify_state_t *content_state,
4031251881Speter           struct file_baton *fb,
4032251881Speter           apr_hash_t *actual_props,
4033251881Speter           apr_time_t last_changed_date,
4034251881Speter           apr_pool_t *result_pool,
4035251881Speter           apr_pool_t *scratch_pool)
4036251881Speter{
4037251881Speter  struct edit_baton *eb = fb->edit_baton;
4038251881Speter  struct dir_baton *pb = fb->dir_baton;
4039251881Speter  svn_boolean_t is_locally_modified;
4040251881Speter  svn_boolean_t found_text_conflict = FALSE;
4041251881Speter
4042251881Speter  SVN_ERR_ASSERT(! fb->shadowed
4043251881Speter                 && ! fb->obstruction_found
4044251881Speter                 && ! fb->edit_obstructed);
4045251881Speter
4046251881Speter  /*
4047251881Speter     When this function is called on file F, we assume the following
4048251881Speter     things are true:
4049251881Speter
4050251881Speter         - The new pristine text of F is present in the pristine store
4051251881Speter           iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4052251881Speter
4053251881Speter         - The WC metadata still reflects the old version of F.
4054251881Speter           (We can still access the old pristine base text of F.)
4055251881Speter
4056251881Speter     The goal is to update the local working copy of F to reflect
4057251881Speter     the changes received from the repository, preserving any local
4058251881Speter     modifications.
4059251881Speter  */
4060251881Speter
4061251881Speter  *work_items = NULL;
4062251881Speter  *install_pristine = FALSE;
4063251881Speter  *install_from = NULL;
4064251881Speter
4065251881Speter  /* Start by splitting the file path, getting an access baton for the parent,
4066251881Speter     and an entry for the file if any. */
4067251881Speter
4068251881Speter  /* Has the user made local mods to the working file?
4069251881Speter     Note that this compares to the current pristine file, which is
4070251881Speter     different from fb->old_text_base_path if we have a replaced-with-history
4071251881Speter     file.  However, in the case we had an obstruction, we check against the
4072251881Speter     new text base.
4073251881Speter   */
4074251881Speter  if (fb->adding_file && !fb->add_existed)
4075251881Speter    {
4076251881Speter      is_locally_modified = FALSE; /* There is no file: Don't check */
4077251881Speter    }
4078251881Speter  else
4079251881Speter    {
4080251881Speter      /* The working file is not an obstruction.
4081251881Speter         So: is the file modified, relative to its ORIGINAL pristine?
4082251881Speter
4083251881Speter         This function sets is_locally_modified to FALSE for
4084251881Speter         files that do not exist and for directories. */
4085251881Speter
4086251881Speter      SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4087251881Speter                                               eb->db, fb->local_abspath,
4088251881Speter                                               FALSE /* exact_comparison */,
4089251881Speter                                               scratch_pool));
4090251881Speter    }
4091251881Speter
4092251881Speter  /* For 'textual' merging, we use the following system:
4093251881Speter
4094251881Speter     When a file is modified and we have a new BASE:
4095251881Speter      - For text files
4096251881Speter          * svn_wc_merge uses diff3
4097251881Speter          * possibly makes backups and marks files as conflicted.
4098251881Speter
4099251881Speter      - For binary files
4100251881Speter          * svn_wc_merge makes backups and marks files as conflicted.
4101251881Speter
4102251881Speter     If a file is not modified and we have a new BASE:
4103251881Speter       * Install from pristine.
4104251881Speter
4105251881Speter     If we have property changes related to magic properties or if the
4106251881Speter     svn:keywords property is set:
4107251881Speter       * Retranslate from the working file.
4108251881Speter   */
4109251881Speter  if (! is_locally_modified
4110251881Speter      && fb->new_text_base_sha1_checksum)
4111251881Speter    {
4112251881Speter          /* If there are no local mods, who cares whether it's a text
4113251881Speter             or binary file!  Just write a log command to overwrite
4114251881Speter             any working file with the new text-base.  If newline
4115251881Speter             conversion or keyword substitution is activated, this
4116251881Speter             will happen as well during the copy.
4117251881Speter             For replaced files, though, we want to merge in the changes
4118251881Speter             even if the file is not modified compared to the (non-revert)
4119251881Speter             text-base. */
4120251881Speter
4121251881Speter      *install_pristine = TRUE;
4122251881Speter    }
4123251881Speter  else if (fb->new_text_base_sha1_checksum)
4124251881Speter    {
4125251881Speter      /* Actual file exists and has local mods:
4126251881Speter         Now we need to let loose svn_wc__merge_internal() to merge
4127251881Speter         the textual changes into the working file. */
4128251881Speter      SVN_ERR(svn_wc__perform_file_merge(work_items,
4129251881Speter                                         conflict_skel,
4130251881Speter                                         &found_text_conflict,
4131251881Speter                                         eb->db,
4132251881Speter                                         fb->local_abspath,
4133251881Speter                                         pb->local_abspath,
4134251881Speter                                         fb->new_text_base_sha1_checksum,
4135251881Speter                                         fb->add_existed
4136251881Speter                                                  ? NULL
4137251881Speter                                                  : fb->original_checksum,
4138251881Speter                                         actual_props,
4139251881Speter                                         eb->ext_patterns,
4140251881Speter                                         fb->old_revision,
4141251881Speter                                         *eb->target_revision,
4142251881Speter                                         fb->propchanges,
4143251881Speter                                         eb->diff3_cmd,
4144251881Speter                                         eb->cancel_func, eb->cancel_baton,
4145251881Speter                                         result_pool, scratch_pool));
4146251881Speter    } /* end: working file exists and has mods */
4147251881Speter  else
4148251881Speter    {
4149251881Speter      /* There is no new text base, but let's see if the working file needs
4150251881Speter         to be updated for any other reason. */
4151251881Speter
4152251881Speter      apr_hash_t *keywords;
4153251881Speter
4154251881Speter      /* Determine if any of the propchanges are the "magic" ones that
4155251881Speter         might require changing the working file. */
4156251881Speter      svn_boolean_t magic_props_changed;
4157251881Speter
4158251881Speter      magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4159251881Speter
4160251881Speter      SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4161251881Speter                                         &keywords,
4162251881Speter                                         NULL,
4163251881Speter                                         eb->db, fb->local_abspath,
4164251881Speter                                         actual_props, TRUE,
4165251881Speter                                         scratch_pool, scratch_pool));
4166251881Speter      if (magic_props_changed || keywords)
4167251881Speter        {
4168251881Speter          /* Special edge-case: it's possible that this file installation
4169251881Speter             only involves propchanges, but that some of those props still
4170251881Speter             require a retranslation of the working file.
4171251881Speter
4172251881Speter             OR that the file doesn't involve propchanges which by themselves
4173251881Speter             require retranslation, but receiving a change bumps the revision
4174251881Speter             number which requires re-expansion of keywords... */
4175251881Speter
4176251881Speter          if (is_locally_modified)
4177251881Speter            {
4178251881Speter              const char *tmptext;
4179251881Speter
4180251881Speter              /* Copy and DEtranslate the working file to a temp text-base.
4181251881Speter                 Note that detranslation is done according to the old props. */
4182251881Speter              SVN_ERR(svn_wc__internal_translated_file(
4183251881Speter                        &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4184251881Speter                        SVN_WC_TRANSLATE_TO_NF
4185251881Speter                          | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4186251881Speter                        eb->cancel_func, eb->cancel_baton,
4187251881Speter                        result_pool, scratch_pool));
4188251881Speter
4189251881Speter              /* We always want to reinstall the working file if the magic
4190251881Speter                 properties have changed, or there are any keywords present.
4191251881Speter                 Note that TMPTEXT might actually refer to the working file
4192251881Speter                 itself (the above function skips a detranslate when not
4193251881Speter                 required). This is acceptable, as we will (re)translate
4194251881Speter                 according to the new properties into a temporary file (from
4195251881Speter                 the working file), and then rename the temp into place. Magic!
4196251881Speter               */
4197251881Speter              *install_pristine = TRUE;
4198251881Speter              *install_from = tmptext;
4199251881Speter            }
4200251881Speter          else
4201251881Speter            {
4202251881Speter              /* Use our existing 'copy' from the pristine store instead
4203251881Speter                 of making a new copy. This way we can use the standard code
4204251881Speter                 to update the recorded size and modification time.
4205251881Speter                 (Issue #3842) */
4206251881Speter              *install_pristine = TRUE;
4207251881Speter            }
4208251881Speter        }
4209251881Speter    }
4210251881Speter
4211251881Speter  /* Set the returned content state. */
4212251881Speter
4213251881Speter  if (found_text_conflict)
4214251881Speter    *content_state = svn_wc_notify_state_conflicted;
4215251881Speter  else if (fb->new_text_base_sha1_checksum)
4216251881Speter    {
4217251881Speter      if (is_locally_modified)
4218251881Speter        *content_state = svn_wc_notify_state_merged;
4219251881Speter      else
4220251881Speter        *content_state = svn_wc_notify_state_changed;
4221251881Speter    }
4222251881Speter  else
4223251881Speter    *content_state = svn_wc_notify_state_unchanged;
4224251881Speter
4225251881Speter  return SVN_NO_ERROR;
4226251881Speter}
4227251881Speter
4228251881Speter
4229251881Speter/* An svn_delta_editor_t function. */
4230251881Speter/* Mostly a wrapper around merge_file. */
4231251881Speterstatic svn_error_t *
4232251881Speterclose_file(void *file_baton,
4233251881Speter           const char *expected_md5_digest,
4234251881Speter           apr_pool_t *pool)
4235251881Speter{
4236251881Speter  struct file_baton *fb = file_baton;
4237251881Speter  struct dir_baton *pdb = fb->dir_baton;
4238251881Speter  struct edit_baton *eb = fb->edit_baton;
4239251881Speter  svn_wc_notify_state_t content_state, prop_state;
4240251881Speter  svn_wc_notify_lock_state_t lock_state;
4241251881Speter  svn_checksum_t *expected_md5_checksum = NULL;
4242251881Speter  apr_hash_t *new_base_props = NULL;
4243251881Speter  apr_hash_t *new_actual_props = NULL;
4244251881Speter  apr_array_header_t *entry_prop_changes;
4245251881Speter  apr_array_header_t *dav_prop_changes;
4246251881Speter  apr_array_header_t *regular_prop_changes;
4247251881Speter  apr_hash_t *current_base_props = NULL;
4248251881Speter  apr_hash_t *current_actual_props = NULL;
4249251881Speter  apr_hash_t *local_actual_props = NULL;
4250251881Speter  svn_skel_t *all_work_items = NULL;
4251251881Speter  svn_skel_t *conflict_skel = NULL;
4252251881Speter  svn_skel_t *work_item;
4253251881Speter  apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4254251881Speter  svn_boolean_t keep_recorded_info = FALSE;
4255251881Speter  const svn_checksum_t *new_checksum;
4256251881Speter  apr_array_header_t *iprops = NULL;
4257251881Speter
4258251881Speter  if (fb->skip_this)
4259251881Speter    {
4260251881Speter      svn_pool_destroy(fb->pool);
4261251881Speter      SVN_ERR(maybe_release_dir_info(pdb));
4262251881Speter      return SVN_NO_ERROR;
4263251881Speter    }
4264251881Speter
4265251881Speter  if (fb->edited)
4266251881Speter    conflict_skel = fb->edit_conflict;
4267251881Speter
4268251881Speter  if (expected_md5_digest)
4269251881Speter    SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4270251881Speter                                   expected_md5_digest, scratch_pool));
4271251881Speter
4272251881Speter  if (fb->new_text_base_md5_checksum && expected_md5_checksum
4273251881Speter      && !svn_checksum_match(expected_md5_checksum,
4274251881Speter                             fb->new_text_base_md5_checksum))
4275251881Speter    return svn_error_trace(
4276251881Speter                svn_checksum_mismatch_err(expected_md5_checksum,
4277251881Speter                                          fb->new_text_base_md5_checksum,
4278251881Speter                                          scratch_pool,
4279251881Speter                                          _("Checksum mismatch for '%s'"),
4280251881Speter                                          svn_dirent_local_style(
4281251881Speter                                                fb->local_abspath, pool)));
4282251881Speter
4283251881Speter  /* Gather the changes for each kind of property.  */
4284251881Speter  SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4285251881Speter                               &dav_prop_changes, &regular_prop_changes,
4286251881Speter                               scratch_pool));
4287251881Speter
4288251881Speter  /* Extract the changed_* and lock state information.  */
4289251881Speter  {
4290251881Speter    svn_revnum_t new_changed_rev;
4291251881Speter    apr_time_t new_changed_date;
4292251881Speter    const char *new_changed_author;
4293251881Speter
4294251881Speter    SVN_ERR(accumulate_last_change(&new_changed_rev,
4295251881Speter                                   &new_changed_date,
4296251881Speter                                   &new_changed_author,
4297251881Speter                                   entry_prop_changes,
4298251881Speter                                   scratch_pool, scratch_pool));
4299251881Speter
4300251881Speter    if (SVN_IS_VALID_REVNUM(new_changed_rev))
4301251881Speter      fb->changed_rev = new_changed_rev;
4302251881Speter    if (new_changed_date != 0)
4303251881Speter      fb->changed_date = new_changed_date;
4304251881Speter    if (new_changed_author != NULL)
4305251881Speter      fb->changed_author = new_changed_author;
4306251881Speter  }
4307251881Speter
4308251881Speter  /* Determine whether the file has become unlocked.  */
4309251881Speter  {
4310251881Speter    int i;
4311251881Speter
4312251881Speter    lock_state = svn_wc_notify_lock_state_unchanged;
4313251881Speter
4314251881Speter    for (i = 0; i < entry_prop_changes->nelts; ++i)
4315251881Speter      {
4316251881Speter        const svn_prop_t *prop
4317251881Speter          = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4318251881Speter
4319251881Speter        /* If we see a change to the LOCK_TOKEN entry prop, then the only
4320251881Speter           possible change is its REMOVAL. Thus, the lock has been removed,
4321251881Speter           and we should likewise remove our cached copy of it.  */
4322251881Speter        if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4323251881Speter          {
4324251881Speter            /* If we lose the lock, but not because we are switching to
4325251881Speter               another url, remove the state lock from the wc */
4326251881Speter            if (! eb->switch_relpath
4327251881Speter                || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4328251881Speter              {
4329251881Speter                SVN_ERR_ASSERT(prop->value == NULL);
4330251881Speter                SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4331251881Speter                                               scratch_pool));
4332251881Speter
4333251881Speter                lock_state = svn_wc_notify_lock_state_unlocked;
4334251881Speter              }
4335251881Speter            break;
4336251881Speter          }
4337251881Speter      }
4338251881Speter  }
4339251881Speter
4340251881Speter  /* Install all kinds of properties.  It is important to do this before
4341251881Speter     any file content merging, since that process might expand keywords, in
4342251881Speter     which case we want the new entryprops to be in place. */
4343251881Speter
4344251881Speter  /* Write log commands to merge REGULAR_PROPS into the existing
4345251881Speter     properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4346251881Speter     the result of the regular prop merge.
4347251881Speter
4348251881Speter     BASE_PROPS and WORKING_PROPS are hashes of the base and
4349251881Speter     working props of the file; if NULL they are read from the wc.  */
4350251881Speter
4351251881Speter  /* ### some of this feels like voodoo... */
4352251881Speter
4353251881Speter  if ((!fb->adding_file || fb->add_existed)
4354251881Speter      && !fb->shadowed)
4355251881Speter    SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4356251881Speter                                     eb->db, fb->local_abspath,
4357251881Speter                                     scratch_pool, scratch_pool));
4358251881Speter  if (local_actual_props == NULL)
4359251881Speter    local_actual_props = apr_hash_make(scratch_pool);
4360251881Speter
4361251881Speter  if (fb->add_existed)
4362251881Speter    {
4363251881Speter      /* This node already exists. Grab the current pristine properties. */
4364251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4365251881Speter                                             eb->db, fb->local_abspath,
4366251881Speter                                             scratch_pool, scratch_pool));
4367251881Speter      current_actual_props = local_actual_props;
4368251881Speter    }
4369251881Speter  else if (!fb->adding_file)
4370251881Speter    {
4371251881Speter      /* Get the BASE properties for proper merging. */
4372251881Speter      SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4373251881Speter                                        eb->db, fb->local_abspath,
4374251881Speter                                        scratch_pool, scratch_pool));
4375251881Speter      current_actual_props = local_actual_props;
4376251881Speter    }
4377251881Speter
4378251881Speter  /* Note: even if the node existed before, it may not have
4379251881Speter     pristine props (e.g a local-add)  */
4380251881Speter  if (current_base_props == NULL)
4381251881Speter    current_base_props = apr_hash_make(scratch_pool);
4382251881Speter
4383251881Speter  /* And new nodes need an empty set of ACTUAL props.  */
4384251881Speter  if (current_actual_props == NULL)
4385251881Speter    current_actual_props = apr_hash_make(scratch_pool);
4386251881Speter
4387251881Speter  prop_state = svn_wc_notify_state_unknown;
4388251881Speter
4389251881Speter  if (! fb->shadowed)
4390251881Speter    {
4391251881Speter      svn_boolean_t install_pristine;
4392251881Speter      const char *install_from = NULL;
4393251881Speter
4394251881Speter      /* Merge the 'regular' props into the existing working proplist. */
4395251881Speter      /* This will merge the old and new props into a new prop db, and
4396251881Speter         write <cp> commands to the logfile to install the merged
4397251881Speter         props.  */
4398251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4399251881Speter                                       scratch_pool);
4400251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4401251881Speter                                  &prop_state,
4402251881Speter                                  &new_actual_props,
4403251881Speter                                  eb->db,
4404251881Speter                                  fb->local_abspath,
4405251881Speter                                  NULL /* server_baseprops (update, not merge)  */,
4406251881Speter                                  current_base_props,
4407251881Speter                                  current_actual_props,
4408251881Speter                                  regular_prop_changes, /* propchanges */
4409251881Speter                                  scratch_pool,
4410251881Speter                                  scratch_pool));
4411251881Speter      /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4412251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4413251881Speter
4414251881Speter      /* Merge the text. This will queue some additional work.  */
4415251881Speter      if (!fb->obstruction_found && !fb->edit_obstructed)
4416251881Speter        {
4417251881Speter          svn_error_t *err;
4418251881Speter          err = merge_file(&work_item, &conflict_skel,
4419251881Speter                           &install_pristine, &install_from,
4420251881Speter                           &content_state, fb, current_actual_props,
4421251881Speter                           fb->changed_date, scratch_pool, scratch_pool);
4422251881Speter
4423251881Speter          if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4424251881Speter            {
4425251881Speter              if (eb->notify_func)
4426251881Speter                {
4427251881Speter                  svn_wc_notify_t *notify =svn_wc_create_notify(
4428251881Speter                                fb->local_abspath,
4429251881Speter                                svn_wc_notify_update_skip_access_denied,
4430251881Speter                                scratch_pool);
4431251881Speter
4432251881Speter                  notify->kind = svn_node_file;
4433251881Speter                  notify->err = err;
4434251881Speter
4435251881Speter                  eb->notify_func(eb->notify_baton, notify, scratch_pool);
4436251881Speter                }
4437251881Speter              svn_error_clear(err);
4438251881Speter
4439251881Speter              SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4440251881Speter                                            scratch_pool));
4441251881Speter              fb->skip_this = TRUE;
4442251881Speter
4443251881Speter              svn_pool_destroy(fb->pool);
4444251881Speter              SVN_ERR(maybe_release_dir_info(pdb));
4445251881Speter              return SVN_NO_ERROR;
4446251881Speter            }
4447251881Speter          else
4448251881Speter            SVN_ERR(err);
4449251881Speter
4450251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4451251881Speter                                            scratch_pool);
4452251881Speter        }
4453251881Speter      else
4454251881Speter        {
4455251881Speter          install_pristine = FALSE;
4456251881Speter          if (fb->new_text_base_sha1_checksum)
4457251881Speter            content_state = svn_wc_notify_state_changed;
4458251881Speter          else
4459251881Speter            content_state = svn_wc_notify_state_unchanged;
4460251881Speter        }
4461251881Speter
4462251881Speter      if (install_pristine)
4463251881Speter        {
4464251881Speter          svn_boolean_t record_fileinfo;
4465251881Speter
4466251881Speter          /* If we are installing from the pristine contents, then go ahead and
4467251881Speter             record the fileinfo. That will be the "proper" values. Installing
4468251881Speter             from some random file means the fileinfo does NOT correspond to
4469251881Speter             the pristine (in which case, the fileinfo will be cleared for
4470251881Speter             safety's sake).  */
4471251881Speter          record_fileinfo = (install_from == NULL);
4472251881Speter
4473251881Speter          SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4474251881Speter                                                eb->db,
4475251881Speter                                                fb->local_abspath,
4476251881Speter                                                install_from,
4477251881Speter                                                eb->use_commit_times,
4478251881Speter                                                record_fileinfo,
4479251881Speter                                                scratch_pool, scratch_pool));
4480251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4481251881Speter                                            scratch_pool);
4482251881Speter        }
4483251881Speter      else if (lock_state == svn_wc_notify_lock_state_unlocked
4484251881Speter               && !fb->obstruction_found)
4485251881Speter        {
4486251881Speter          /* If a lock was removed and we didn't update the text contents, we
4487251881Speter             might need to set the file read-only.
4488251881Speter
4489251881Speter             Note: this will also update the executable flag, but ... meh.  */
4490251881Speter          SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4491251881Speter                                                   fb->local_abspath,
4492251881Speter                                                   scratch_pool, scratch_pool));
4493251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4494251881Speter                                            scratch_pool);
4495251881Speter        }
4496251881Speter
4497251881Speter      if (! install_pristine
4498251881Speter          && (content_state == svn_wc_notify_state_unchanged))
4499251881Speter        {
4500251881Speter          /* It is safe to keep the current recorded timestamp and size */
4501251881Speter          keep_recorded_info = TRUE;
4502251881Speter        }
4503251881Speter
4504251881Speter      /* Clean up any temporary files.  */
4505251881Speter
4506251881Speter      /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4507251881Speter         working file.  */
4508251881Speter      if (install_from != NULL
4509251881Speter          && strcmp(install_from, fb->local_abspath) != 0)
4510251881Speter        {
4511251881Speter          SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4512251881Speter                                               fb->local_abspath, install_from,
4513251881Speter                                               scratch_pool, scratch_pool));
4514251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4515251881Speter                                            scratch_pool);
4516251881Speter        }
4517251881Speter    }
4518251881Speter  else
4519251881Speter    {
4520251881Speter      /* Adding or updating a BASE node under a locally added node. */
4521251881Speter      apr_hash_t *fake_actual_props;
4522251881Speter
4523251881Speter      if (fb->adding_file)
4524251881Speter        fake_actual_props = apr_hash_make(scratch_pool);
4525251881Speter      else
4526251881Speter        fake_actual_props = current_base_props;
4527251881Speter
4528251881Speter      /* Store the incoming props (sent as propchanges) in new_base_props
4529251881Speter         and create a set of new actual props to use for notifications */
4530251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4531251881Speter                                       scratch_pool);
4532251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4533251881Speter                                  &prop_state,
4534251881Speter                                  &new_actual_props,
4535251881Speter                                  eb->db,
4536251881Speter                                  fb->local_abspath,
4537251881Speter                                  NULL /* server_baseprops (not merging) */,
4538251881Speter                                  current_base_props /* pristine_props */,
4539251881Speter                                  fake_actual_props /* actual_props */,
4540251881Speter                                  regular_prop_changes, /* propchanges */
4541251881Speter                                  scratch_pool,
4542251881Speter                                  scratch_pool));
4543251881Speter
4544251881Speter      if (fb->new_text_base_sha1_checksum)
4545251881Speter        content_state = svn_wc_notify_state_changed;
4546251881Speter      else
4547251881Speter        content_state = svn_wc_notify_state_unchanged;
4548251881Speter    }
4549251881Speter
4550251881Speter  /* Insert/replace the BASE node with all of the new metadata.  */
4551251881Speter
4552251881Speter  /* Set the 'checksum' column of the file's BASE_NODE row to
4553251881Speter   * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4554251881Speter   * checksum is already in the pristine store. */
4555251881Speter  new_checksum = fb->new_text_base_sha1_checksum;
4556251881Speter
4557251881Speter  /* If we don't have a NEW checksum, then the base must not have changed.
4558251881Speter     Just carry over the old checksum.  */
4559251881Speter  if (new_checksum == NULL)
4560251881Speter    new_checksum = fb->original_checksum;
4561251881Speter
4562251881Speter  if (conflict_skel)
4563251881Speter    {
4564251881Speter      SVN_ERR(complete_conflict(conflict_skel,
4565251881Speter                                fb->edit_baton,
4566251881Speter                                fb->local_abspath,
4567251881Speter                                fb->old_repos_relpath,
4568251881Speter                                fb->old_revision,
4569251881Speter                                fb->new_relpath,
4570251881Speter                                svn_node_file, svn_node_file,
4571251881Speter                                fb->pool, scratch_pool));
4572251881Speter
4573251881Speter      SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4574251881Speter                                              eb->db, fb->local_abspath,
4575251881Speter                                              conflict_skel,
4576251881Speter                                              scratch_pool, scratch_pool));
4577251881Speter
4578251881Speter      all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4579251881Speter                                        scratch_pool);
4580251881Speter    }
4581251881Speter
4582251881Speter  /* Any inherited props to be set set for this base node? */
4583251881Speter  if (eb->wcroot_iprops)
4584251881Speter    {
4585251881Speter      iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4586251881Speter
4587251881Speter      /* close_edit may also update iprops for switched nodes, catching
4588251881Speter         those for which close_directory is never called (e.g. a switch
4589251881Speter         with no changes).  So as a minor optimization we remove any
4590251881Speter         iprops from the hash so as not to set them again in
4591251881Speter         close_edit. */
4592251881Speter      if (iprops)
4593251881Speter        svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4594251881Speter    }
4595251881Speter
4596251881Speter  SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4597251881Speter                                   eb->wcroot_abspath,
4598251881Speter                                   fb->new_relpath,
4599251881Speter                                   eb->repos_root, eb->repos_uuid,
4600251881Speter                                   *eb->target_revision,
4601251881Speter                                   new_base_props,
4602251881Speter                                   fb->changed_rev,
4603251881Speter                                   fb->changed_date,
4604251881Speter                                   fb->changed_author,
4605251881Speter                                   new_checksum,
4606251881Speter                                   (dav_prop_changes->nelts > 0)
4607251881Speter                                     ? svn_prop_array_to_hash(
4608251881Speter                                                      dav_prop_changes,
4609251881Speter                                                      scratch_pool)
4610251881Speter                                     : NULL,
4611251881Speter                                   (fb->add_existed && fb->adding_file),
4612251881Speter                                   (! fb->shadowed) && new_base_props,
4613251881Speter                                   new_actual_props,
4614251881Speter                                   iprops,
4615251881Speter                                   keep_recorded_info,
4616251881Speter                                   (fb->shadowed && fb->obstruction_found),
4617251881Speter                                   conflict_skel,
4618251881Speter                                   all_work_items,
4619251881Speter                                   scratch_pool));
4620251881Speter
4621251881Speter  if (conflict_skel && eb->conflict_func)
4622251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4623251881Speter                                             conflict_skel,
4624251881Speter                                             NULL /* merge_options */,
4625251881Speter                                             eb->conflict_func,
4626251881Speter                                             eb->conflict_baton,
4627251881Speter                                             eb->cancel_func,
4628251881Speter                                             eb->cancel_baton,
4629251881Speter                                             scratch_pool));
4630251881Speter
4631251881Speter  /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4632251881Speter
4633251881Speter  svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4634251881Speter
4635251881Speter  /* Send a notification to the callback function.  (Skip notifications
4636251881Speter     about files which were already notified for another reason.) */
4637251881Speter  if (eb->notify_func && !fb->already_notified
4638251881Speter      && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4639251881Speter    {
4640251881Speter      svn_wc_notify_t *notify;
4641251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_update;
4642251881Speter
4643251881Speter      if (fb->edited)
4644251881Speter        {
4645251881Speter          if (fb->shadowed || fb->edit_obstructed)
4646251881Speter            action = fb->adding_file
4647251881Speter                            ? svn_wc_notify_update_shadowed_add
4648251881Speter                            : svn_wc_notify_update_shadowed_update;
4649251881Speter          else if (fb->obstruction_found || fb->add_existed)
4650251881Speter            {
4651251881Speter              if (content_state != svn_wc_notify_state_conflicted)
4652251881Speter                action = svn_wc_notify_exists;
4653251881Speter            }
4654251881Speter          else if (fb->adding_file)
4655251881Speter            {
4656251881Speter              action = svn_wc_notify_update_add;
4657251881Speter            }
4658251881Speter        }
4659251881Speter      else
4660251881Speter        {
4661251881Speter          SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4662251881Speter          action = svn_wc_notify_update_broken_lock;
4663251881Speter        }
4664251881Speter
4665251881Speter      /* If the file was moved-away, notify for the moved-away node.
4666251881Speter       * The original location only had its BASE info changed and
4667251881Speter       * we don't usually notify about such changes. */
4668251881Speter      notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4669251881Speter      notify->kind = svn_node_file;
4670251881Speter      notify->content_state = content_state;
4671251881Speter      notify->prop_state = prop_state;
4672251881Speter      notify->lock_state = lock_state;
4673251881Speter      notify->revision = *eb->target_revision;
4674251881Speter      notify->old_revision = fb->old_revision;
4675251881Speter
4676251881Speter      /* Fetch the mimetype from the actual properties */
4677251881Speter      notify->mime_type = svn_prop_get_value(new_actual_props,
4678251881Speter                                             SVN_PROP_MIME_TYPE);
4679251881Speter
4680251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
4681251881Speter    }
4682251881Speter
4683251881Speter  svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4684251881Speter
4685251881Speter  /* We have one less referrer to the directory */
4686251881Speter  SVN_ERR(maybe_release_dir_info(pdb));
4687251881Speter
4688251881Speter  return SVN_NO_ERROR;
4689251881Speter}
4690251881Speter
4691251881Speter
4692251881Speter/* An svn_delta_editor_t function. */
4693251881Speterstatic svn_error_t *
4694251881Speterclose_edit(void *edit_baton,
4695251881Speter           apr_pool_t *pool)
4696251881Speter{
4697251881Speter  struct edit_baton *eb = edit_baton;
4698251881Speter  apr_pool_t *scratch_pool = eb->pool;
4699251881Speter
4700251881Speter  /* The editor didn't even open the root; we have to take care of
4701251881Speter     some cleanup stuffs. */
4702251881Speter  if (! eb->root_opened
4703251881Speter      && *eb->target_basename == '\0')
4704251881Speter    {
4705251881Speter      /* We need to "un-incomplete" the root directory. */
4706251881Speter      SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4707251881Speter                                                      eb->anchor_abspath,
4708251881Speter                                                      scratch_pool));
4709251881Speter    }
4710251881Speter
4711251881Speter  /* By definition, anybody "driving" this editor for update or switch
4712251881Speter     purposes at a *minimum* must have called set_target_revision() at
4713251881Speter     the outset, and close_edit() at the end -- even if it turned out
4714251881Speter     that no changes ever had to be made, and open_root() was never
4715251881Speter     called.  That's fine.  But regardless, when the edit is over,
4716251881Speter     this editor needs to make sure that *all* paths have had their
4717251881Speter     revisions bumped to the new target revision. */
4718251881Speter
4719251881Speter  /* Make sure our update target now has the new working revision.
4720251881Speter     Also, if this was an 'svn switch', then rewrite the target's
4721251881Speter     url.  All of this tweaking might happen recursively!  Note
4722251881Speter     that if eb->target is NULL, that's okay (albeit "sneaky",
4723251881Speter     some might say).  */
4724251881Speter
4725251881Speter  /* Extra check: if the update did nothing but make its target
4726251881Speter     'deleted', then do *not* run cleanup on the target, as it
4727251881Speter     will only remove the deleted entry!  */
4728251881Speter  if (! eb->target_deleted)
4729251881Speter    {
4730251881Speter      SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4731251881Speter                                                       eb->target_abspath,
4732251881Speter                                                       eb->requested_depth,
4733251881Speter                                                       eb->switch_relpath,
4734251881Speter                                                       eb->repos_root,
4735251881Speter                                                       eb->repos_uuid,
4736251881Speter                                                       *(eb->target_revision),
4737251881Speter                                                       eb->skipped_trees,
4738251881Speter                                                       eb->wcroot_iprops,
4739251881Speter                                                       eb->notify_func,
4740251881Speter                                                       eb->notify_baton,
4741251881Speter                                                       eb->pool));
4742251881Speter
4743251881Speter      if (*eb->target_basename != '\0')
4744251881Speter        {
4745251881Speter          svn_wc__db_status_t status;
4746251881Speter          svn_error_t *err;
4747251881Speter
4748251881Speter          /* Note: we are fetching information about the *target*, not anchor.
4749251881Speter             There is no guarantee that the target has a BASE node.
4750251881Speter             For example:
4751251881Speter
4752251881Speter               The node was not present in BASE, but locally-added, and the
4753251881Speter               update did not create a new BASE node "under" the local-add.
4754251881Speter
4755251881Speter             If there is no BASE node for the target, then we certainly don't
4756251881Speter             have to worry about removing it. */
4757251881Speter          err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4758251881Speter                                         NULL, NULL, NULL, NULL, NULL, NULL,
4759251881Speter                                         NULL, NULL, NULL, NULL,
4760251881Speter                                         eb->db, eb->target_abspath,
4761251881Speter                                         scratch_pool, scratch_pool);
4762251881Speter          if (err)
4763251881Speter            {
4764251881Speter              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4765251881Speter                return svn_error_trace(err);
4766251881Speter
4767251881Speter              svn_error_clear(err);
4768251881Speter            }
4769251881Speter          else if (status == svn_wc__db_status_excluded)
4770251881Speter            {
4771251881Speter              /* There is a small chance that the explicit target of an update/
4772251881Speter                 switch is gone in the repository, in that specific case the
4773251881Speter                 node hasn't been re-added to the BASE tree by this update.
4774251881Speter
4775251881Speter                 If so, we should get rid of this excluded node now. */
4776251881Speter
4777251881Speter              SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4778251881Speter                                             FALSE /* keep_as_working */,
4779251881Speter                                             FALSE /* queue_deletes */,
4780253734Speter                                             FALSE /* remove_locks */,
4781251881Speter                                             SVN_INVALID_REVNUM,
4782251881Speter                                             NULL, NULL, scratch_pool));
4783251881Speter            }
4784251881Speter        }
4785251881Speter    }
4786251881Speter
4787251881Speter  /* The edit is over: run the wq with proper cancel support,
4788251881Speter     but first kill the handler that would run it on the pool
4789251881Speter     cleanup at the end of this function. */
4790251881Speter  apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4791251881Speter
4792251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4793251881Speter                         eb->cancel_func, eb->cancel_baton,
4794251881Speter                         eb->pool));
4795251881Speter
4796251881Speter  /* The edit is over, free its pool.
4797251881Speter     ### No, this is wrong.  Who says this editor/baton won't be used
4798251881Speter     again?  But the change is not merely to remove this call.  We
4799251881Speter     should also make eb->pool not be a subpool (see make_editor),
4800251881Speter     and change callers of svn_client_{checkout,update,switch} to do
4801251881Speter     better pool management. ### */
4802251881Speter
4803251881Speter  svn_pool_destroy(eb->pool);
4804251881Speter
4805251881Speter  return SVN_NO_ERROR;
4806251881Speter}
4807251881Speter
4808251881Speter
4809251881Speter/*** Returning editors. ***/
4810251881Speter
4811251881Speter/* Helper for the three public editor-supplying functions. */
4812251881Speterstatic svn_error_t *
4813251881Spetermake_editor(svn_revnum_t *target_revision,
4814251881Speter            svn_wc__db_t *db,
4815251881Speter            const char *anchor_abspath,
4816251881Speter            const char *target_basename,
4817251881Speter            apr_hash_t *wcroot_iprops,
4818251881Speter            svn_boolean_t use_commit_times,
4819251881Speter            const char *switch_url,
4820251881Speter            svn_depth_t depth,
4821251881Speter            svn_boolean_t depth_is_sticky,
4822251881Speter            svn_boolean_t allow_unver_obstructions,
4823251881Speter            svn_boolean_t adds_as_modification,
4824251881Speter            svn_boolean_t server_performs_filtering,
4825251881Speter            svn_boolean_t clean_checkout,
4826251881Speter            svn_wc_notify_func2_t notify_func,
4827251881Speter            void *notify_baton,
4828251881Speter            svn_cancel_func_t cancel_func,
4829251881Speter            void *cancel_baton,
4830251881Speter            svn_wc_dirents_func_t fetch_dirents_func,
4831251881Speter            void *fetch_dirents_baton,
4832251881Speter            svn_wc_conflict_resolver_func2_t conflict_func,
4833251881Speter            void *conflict_baton,
4834251881Speter            svn_wc_external_update_t external_func,
4835251881Speter            void *external_baton,
4836251881Speter            const char *diff3_cmd,
4837251881Speter            const apr_array_header_t *preserved_exts,
4838251881Speter            const svn_delta_editor_t **editor,
4839251881Speter            void **edit_baton,
4840251881Speter            apr_pool_t *result_pool,
4841251881Speter            apr_pool_t *scratch_pool)
4842251881Speter{
4843251881Speter  struct edit_baton *eb;
4844251881Speter  void *inner_baton;
4845251881Speter  apr_pool_t *edit_pool = svn_pool_create(result_pool);
4846251881Speter  svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4847251881Speter  const svn_delta_editor_t *inner_editor;
4848251881Speter  const char *repos_root, *repos_uuid;
4849251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
4850251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
4851251881Speter                                svn_delta_shim_callbacks_default(edit_pool);
4852251881Speter
4853251881Speter  /* An unknown depth can't be sticky. */
4854251881Speter  if (depth == svn_depth_unknown)
4855251881Speter    depth_is_sticky = FALSE;
4856251881Speter
4857251881Speter  /* Get the anchor's repository root and uuid. The anchor must already exist
4858251881Speter     in BASE. */
4859251881Speter  SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4860251881Speter                                     db, anchor_abspath,
4861251881Speter                                     result_pool, scratch_pool));
4862251881Speter
4863251881Speter  /* With WC-NG we need a valid repository root */
4864251881Speter  SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4865251881Speter
4866251881Speter  /* Disallow a switch operation to change the repository root of the target,
4867251881Speter     if that is known. */
4868251881Speter  if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4869251881Speter    return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4870251881Speter                             _("'%s'\nis not the same repository as\n'%s'"),
4871251881Speter                             switch_url, repos_root);
4872251881Speter
4873251881Speter  /* Construct an edit baton. */
4874251881Speter  eb = apr_pcalloc(edit_pool, sizeof(*eb));
4875251881Speter  eb->pool                     = edit_pool;
4876251881Speter  eb->use_commit_times         = use_commit_times;
4877251881Speter  eb->target_revision          = target_revision;
4878251881Speter  eb->repos_root               = repos_root;
4879251881Speter  eb->repos_uuid               = repos_uuid;
4880251881Speter  eb->db                       = db;
4881251881Speter  eb->target_basename          = target_basename;
4882251881Speter  eb->anchor_abspath           = anchor_abspath;
4883251881Speter  eb->wcroot_iprops            = wcroot_iprops;
4884251881Speter
4885251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4886251881Speter                                edit_pool, scratch_pool));
4887251881Speter
4888251881Speter  if (switch_url)
4889251881Speter    eb->switch_relpath =
4890251881Speter      svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4891251881Speter  else
4892251881Speter    eb->switch_relpath = NULL;
4893251881Speter
4894251881Speter  if (svn_path_is_empty(target_basename))
4895251881Speter    eb->target_abspath = eb->anchor_abspath;
4896251881Speter  else
4897251881Speter    eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4898251881Speter                                         edit_pool);
4899251881Speter
4900251881Speter  eb->requested_depth          = depth;
4901251881Speter  eb->depth_is_sticky          = depth_is_sticky;
4902251881Speter  eb->notify_func              = notify_func;
4903251881Speter  eb->notify_baton             = notify_baton;
4904251881Speter  eb->external_func            = external_func;
4905251881Speter  eb->external_baton           = external_baton;
4906251881Speter  eb->diff3_cmd                = diff3_cmd;
4907251881Speter  eb->cancel_func              = cancel_func;
4908251881Speter  eb->cancel_baton             = cancel_baton;
4909251881Speter  eb->conflict_func            = conflict_func;
4910251881Speter  eb->conflict_baton           = conflict_baton;
4911251881Speter  eb->allow_unver_obstructions = allow_unver_obstructions;
4912251881Speter  eb->adds_as_modification     = adds_as_modification;
4913251881Speter  eb->clean_checkout           = clean_checkout;
4914251881Speter  eb->skipped_trees            = apr_hash_make(edit_pool);
4915251881Speter  eb->dir_dirents              = apr_hash_make(edit_pool);
4916251881Speter  eb->ext_patterns             = preserved_exts;
4917251881Speter
4918251881Speter  apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4919251881Speter                            apr_pool_cleanup_null);
4920251881Speter
4921251881Speter  /* Construct an editor. */
4922251881Speter  tree_editor->set_target_revision = set_target_revision;
4923251881Speter  tree_editor->open_root = open_root;
4924251881Speter  tree_editor->delete_entry = delete_entry;
4925251881Speter  tree_editor->add_directory = add_directory;
4926251881Speter  tree_editor->open_directory = open_directory;
4927251881Speter  tree_editor->change_dir_prop = change_dir_prop;
4928251881Speter  tree_editor->close_directory = close_directory;
4929251881Speter  tree_editor->absent_directory = absent_directory;
4930251881Speter  tree_editor->add_file = add_file;
4931251881Speter  tree_editor->open_file = open_file;
4932251881Speter  tree_editor->apply_textdelta = apply_textdelta;
4933251881Speter  tree_editor->change_file_prop = change_file_prop;
4934251881Speter  tree_editor->close_file = close_file;
4935251881Speter  tree_editor->absent_file = absent_file;
4936251881Speter  tree_editor->close_edit = close_edit;
4937251881Speter
4938251881Speter  /* Fiddle with the type system. */
4939251881Speter  inner_editor = tree_editor;
4940251881Speter  inner_baton = eb;
4941251881Speter
4942251881Speter  if (!depth_is_sticky
4943251881Speter      && depth != svn_depth_unknown
4944251881Speter      && svn_depth_empty <= depth && depth < svn_depth_infinity
4945251881Speter      && fetch_dirents_func)
4946251881Speter    {
4947251881Speter      /* We are asked to perform an update at a depth less than the ambient
4948251881Speter         depth. In this case the update won't describe additions that would
4949251881Speter         have been reported if we updated at the ambient depth. */
4950251881Speter      svn_error_t *err;
4951251881Speter      svn_node_kind_t dir_kind;
4952251881Speter      svn_wc__db_status_t dir_status;
4953251881Speter      const char *dir_repos_relpath;
4954251881Speter      svn_depth_t dir_depth;
4955251881Speter
4956251881Speter      /* we have to do this on the target of the update, not the anchor */
4957251881Speter      err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4958251881Speter                                     &dir_repos_relpath, NULL, NULL, NULL,
4959251881Speter                                     NULL, NULL, &dir_depth, NULL, NULL, NULL,
4960251881Speter                                     NULL, NULL, NULL,
4961251881Speter                                     db, eb->target_abspath,
4962251881Speter                                     scratch_pool, scratch_pool);
4963251881Speter
4964251881Speter      if (!err
4965251881Speter          && dir_kind == svn_node_dir
4966251881Speter          && dir_status == svn_wc__db_status_normal)
4967251881Speter        {
4968251881Speter          if (dir_depth > depth)
4969251881Speter            {
4970251881Speter              apr_hash_t *dirents;
4971251881Speter
4972251881Speter              /* If we switch, we should look at the new relpath */
4973251881Speter              if (eb->switch_relpath)
4974251881Speter                dir_repos_relpath = eb->switch_relpath;
4975251881Speter
4976251881Speter              SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4977251881Speter                                         repos_root, dir_repos_relpath,
4978251881Speter                                         edit_pool, scratch_pool));
4979251881Speter
4980251881Speter              if (dirents != NULL && apr_hash_count(dirents))
4981251881Speter                svn_hash_sets(eb->dir_dirents,
4982251881Speter                              apr_pstrdup(edit_pool, dir_repos_relpath),
4983251881Speter                              dirents);
4984251881Speter            }
4985251881Speter
4986251881Speter          if (depth == svn_depth_immediates)
4987251881Speter            {
4988251881Speter              /* Worst case scenario of issue #3569 fix: We have to do the
4989251881Speter                 same for all existing subdirs, but then we check for
4990251881Speter                 svn_depth_empty. */
4991251881Speter              const apr_array_header_t *children;
4992251881Speter              apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4993251881Speter              int i;
4994251881Speter              SVN_ERR(svn_wc__db_base_get_children(&children, db,
4995251881Speter                                                   eb->target_abspath,
4996251881Speter                                                   scratch_pool,
4997251881Speter                                                   iterpool));
4998251881Speter
4999251881Speter              for (i = 0; i < children->nelts; i++)
5000251881Speter                {
5001251881Speter                  const char *child_abspath;
5002251881Speter                  const char *child_name;
5003251881Speter
5004251881Speter                  svn_pool_clear(iterpool);
5005251881Speter
5006251881Speter                  child_name = APR_ARRAY_IDX(children, i, const char *);
5007251881Speter
5008251881Speter                  child_abspath = svn_dirent_join(eb->target_abspath,
5009251881Speter                                                  child_name, iterpool);
5010251881Speter
5011251881Speter                  SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5012251881Speter                                                   NULL, &dir_repos_relpath,
5013251881Speter                                                   NULL, NULL, NULL, NULL,
5014251881Speter                                                   NULL, &dir_depth, NULL,
5015251881Speter                                                   NULL, NULL, NULL, NULL,
5016251881Speter                                                   NULL,
5017251881Speter                                                   db, child_abspath,
5018251881Speter                                                   iterpool, iterpool));
5019251881Speter
5020251881Speter                  if (dir_kind == svn_node_dir
5021251881Speter                      && dir_status == svn_wc__db_status_normal
5022251881Speter                      && dir_depth > svn_depth_empty)
5023251881Speter                    {
5024251881Speter                      apr_hash_t *dirents;
5025251881Speter
5026251881Speter                      /* If we switch, we should look at the new relpath */
5027251881Speter                      if (eb->switch_relpath)
5028251881Speter                        dir_repos_relpath = svn_relpath_join(
5029251881Speter                                                eb->switch_relpath,
5030251881Speter                                                child_name, iterpool);
5031251881Speter
5032251881Speter                      SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5033251881Speter                                                 repos_root, dir_repos_relpath,
5034251881Speter                                                 edit_pool, iterpool));
5035251881Speter
5036251881Speter                      if (dirents != NULL && apr_hash_count(dirents))
5037251881Speter                        svn_hash_sets(eb->dir_dirents,
5038251881Speter                                      apr_pstrdup(edit_pool,
5039251881Speter                                                  dir_repos_relpath),
5040251881Speter                                      dirents);
5041251881Speter                    }
5042251881Speter                }
5043251881Speter            }
5044251881Speter        }
5045251881Speter      else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5046251881Speter        svn_error_clear(err);
5047251881Speter      else
5048251881Speter        SVN_ERR(err);
5049251881Speter    }
5050251881Speter
5051251881Speter  /* We need to limit the scope of our operation to the ambient depths
5052251881Speter     present in the working copy already, but only if the requested
5053251881Speter     depth is not sticky. If a depth was explicitly requested,
5054251881Speter     libsvn_delta/depth_filter_editor.c will ensure that we never see
5055251881Speter     editor calls that extend beyond the scope of the requested depth.
5056251881Speter     But even what we do so might extend beyond the scope of our
5057251881Speter     ambient depth.  So we use another filtering editor to avoid
5058251881Speter     modifying the ambient working copy depth when not asked to do so.
5059251881Speter     (This can also be skipped if the server understands depth.) */
5060251881Speter  if (!server_performs_filtering
5061251881Speter      && !depth_is_sticky)
5062251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5063251881Speter                                                &inner_baton,
5064251881Speter                                                db,
5065251881Speter                                                anchor_abspath,
5066251881Speter                                                target_basename,
5067251881Speter                                                inner_editor,
5068251881Speter                                                inner_baton,
5069251881Speter                                                result_pool));
5070251881Speter
5071251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5072251881Speter                                            cancel_baton,
5073251881Speter                                            inner_editor,
5074251881Speter                                            inner_baton,
5075251881Speter                                            editor,
5076251881Speter                                            edit_baton,
5077251881Speter                                            result_pool));
5078251881Speter
5079251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
5080251881Speter  sfb->db = db;
5081251881Speter  sfb->base_abspath = eb->anchor_abspath;
5082251881Speter  sfb->fetch_base = TRUE;
5083251881Speter
5084251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5085251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5086251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5087251881Speter  shim_callbacks->fetch_baton = sfb;
5088251881Speter
5089251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5090251881Speter                                   NULL, NULL, shim_callbacks,
5091251881Speter                                   result_pool, scratch_pool));
5092251881Speter
5093251881Speter  return SVN_NO_ERROR;
5094251881Speter}
5095251881Speter
5096251881Speter
5097251881Spetersvn_error_t *
5098251881Spetersvn_wc__get_update_editor(const svn_delta_editor_t **editor,
5099251881Speter                          void **edit_baton,
5100251881Speter                          svn_revnum_t *target_revision,
5101251881Speter                          svn_wc_context_t *wc_ctx,
5102251881Speter                          const char *anchor_abspath,
5103251881Speter                          const char *target_basename,
5104251881Speter                          apr_hash_t *wcroot_iprops,
5105251881Speter                          svn_boolean_t use_commit_times,
5106251881Speter                          svn_depth_t depth,
5107251881Speter                          svn_boolean_t depth_is_sticky,
5108251881Speter                          svn_boolean_t allow_unver_obstructions,
5109251881Speter                          svn_boolean_t adds_as_modification,
5110251881Speter                          svn_boolean_t server_performs_filtering,
5111251881Speter                          svn_boolean_t clean_checkout,
5112251881Speter                          const char *diff3_cmd,
5113251881Speter                          const apr_array_header_t *preserved_exts,
5114251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5115251881Speter                          void *fetch_dirents_baton,
5116251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5117251881Speter                          void *conflict_baton,
5118251881Speter                          svn_wc_external_update_t external_func,
5119251881Speter                          void *external_baton,
5120251881Speter                          svn_cancel_func_t cancel_func,
5121251881Speter                          void *cancel_baton,
5122251881Speter                          svn_wc_notify_func2_t notify_func,
5123251881Speter                          void *notify_baton,
5124251881Speter                          apr_pool_t *result_pool,
5125251881Speter                          apr_pool_t *scratch_pool)
5126251881Speter{
5127251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5128251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5129251881Speter                     NULL, depth, depth_is_sticky, allow_unver_obstructions,
5130251881Speter                     adds_as_modification, server_performs_filtering,
5131251881Speter                     clean_checkout,
5132251881Speter                     notify_func, notify_baton,
5133251881Speter                     cancel_func, cancel_baton,
5134251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5135251881Speter                     conflict_func, conflict_baton,
5136251881Speter                     external_func, external_baton,
5137251881Speter                     diff3_cmd, preserved_exts, editor, edit_baton,
5138251881Speter                     result_pool, scratch_pool);
5139251881Speter}
5140251881Speter
5141251881Spetersvn_error_t *
5142251881Spetersvn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5143251881Speter                          void **edit_baton,
5144251881Speter                          svn_revnum_t *target_revision,
5145251881Speter                          svn_wc_context_t *wc_ctx,
5146251881Speter                          const char *anchor_abspath,
5147251881Speter                          const char *target_basename,
5148251881Speter                          const char *switch_url,
5149251881Speter                          apr_hash_t *wcroot_iprops,
5150251881Speter                          svn_boolean_t use_commit_times,
5151251881Speter                          svn_depth_t depth,
5152251881Speter                          svn_boolean_t depth_is_sticky,
5153251881Speter                          svn_boolean_t allow_unver_obstructions,
5154251881Speter                          svn_boolean_t server_performs_filtering,
5155251881Speter                          const char *diff3_cmd,
5156251881Speter                          const apr_array_header_t *preserved_exts,
5157251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5158251881Speter                          void *fetch_dirents_baton,
5159251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5160251881Speter                          void *conflict_baton,
5161251881Speter                          svn_wc_external_update_t external_func,
5162251881Speter                          void *external_baton,
5163251881Speter                          svn_cancel_func_t cancel_func,
5164251881Speter                          void *cancel_baton,
5165251881Speter                          svn_wc_notify_func2_t notify_func,
5166251881Speter                          void *notify_baton,
5167251881Speter                          apr_pool_t *result_pool,
5168251881Speter                          apr_pool_t *scratch_pool)
5169251881Speter{
5170251881Speter  SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5171251881Speter
5172251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5173251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5174251881Speter                     switch_url,
5175251881Speter                     depth, depth_is_sticky, allow_unver_obstructions,
5176251881Speter                     FALSE /* adds_as_modification */,
5177251881Speter                     server_performs_filtering,
5178251881Speter                     FALSE /* clean_checkout */,
5179251881Speter                     notify_func, notify_baton,
5180251881Speter                     cancel_func, cancel_baton,
5181251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5182251881Speter                     conflict_func, conflict_baton,
5183251881Speter                     external_func, external_baton,
5184251881Speter                     diff3_cmd, preserved_exts,
5185251881Speter                     editor, edit_baton,
5186251881Speter                     result_pool, scratch_pool);
5187251881Speter}
5188251881Speter
5189251881Speter
5190251881Speter
5191251881Speter/* ### Note that this function is completely different from the rest of the
5192251881Speter       update editor in what it updates. The update editor changes only BASE
5193251881Speter       and ACTUAL and this function just changes WORKING and ACTUAL.
5194251881Speter
5195251881Speter       In the entries world this function shared a lot of code with the
5196251881Speter       update editor but in the wonderful new WC-NG world it will probably
5197251881Speter       do more and more by itself and would be more logically grouped with
5198251881Speter       the add/copy functionality in adm_ops.c and copy.c. */
5199251881Spetersvn_error_t *
5200251881Spetersvn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5201251881Speter                       const char *local_abspath,
5202251881Speter                       svn_stream_t *new_base_contents,
5203251881Speter                       svn_stream_t *new_contents,
5204251881Speter                       apr_hash_t *new_base_props,
5205251881Speter                       apr_hash_t *new_props,
5206251881Speter                       const char *copyfrom_url,
5207251881Speter                       svn_revnum_t copyfrom_rev,
5208251881Speter                       svn_cancel_func_t cancel_func,
5209251881Speter                       void *cancel_baton,
5210251881Speter                       apr_pool_t *scratch_pool)
5211251881Speter{
5212251881Speter  svn_wc__db_t *db = wc_ctx->db;
5213251881Speter  const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5214251881Speter  svn_wc__db_status_t status;
5215251881Speter  svn_node_kind_t kind;
5216251881Speter  const char *tmp_text_base_abspath;
5217251881Speter  svn_checksum_t *new_text_base_md5_checksum;
5218251881Speter  svn_checksum_t *new_text_base_sha1_checksum;
5219251881Speter  const char *source_abspath = NULL;
5220251881Speter  svn_skel_t *all_work_items = NULL;
5221251881Speter  svn_skel_t *work_item;
5222251881Speter  const char *repos_root_url;
5223251881Speter  const char *repos_uuid;
5224251881Speter  const char *original_repos_relpath;
5225251881Speter  svn_revnum_t changed_rev;
5226251881Speter  apr_time_t changed_date;
5227251881Speter  const char *changed_author;
5228251881Speter  svn_error_t *err;
5229251881Speter  apr_pool_t *pool = scratch_pool;
5230251881Speter
5231251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5232251881Speter  SVN_ERR_ASSERT(new_base_contents != NULL);
5233251881Speter  SVN_ERR_ASSERT(new_base_props != NULL);
5234251881Speter
5235251881Speter  /* We should have a write lock on this file's parent directory.  */
5236251881Speter  SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5237251881Speter
5238251881Speter  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5239251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5240251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5241251881Speter                             NULL, NULL, NULL,
5242251881Speter                             db, local_abspath, scratch_pool, scratch_pool);
5243251881Speter
5244251881Speter  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5245251881Speter    return svn_error_trace(err);
5246251881Speter  else if(err)
5247251881Speter    svn_error_clear(err);
5248251881Speter  else
5249251881Speter    switch (status)
5250251881Speter      {
5251251881Speter        case svn_wc__db_status_not_present:
5252251881Speter        case svn_wc__db_status_deleted:
5253251881Speter          break;
5254251881Speter        default:
5255251881Speter          return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5256251881Speter                                   _("Node '%s' exists."),
5257251881Speter                                   svn_dirent_local_style(local_abspath,
5258251881Speter                                                          scratch_pool));
5259251881Speter      }
5260251881Speter
5261251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5262251881Speter                               &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5263251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5264251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5265251881Speter                               db, dir_abspath, scratch_pool, scratch_pool));
5266251881Speter
5267251881Speter  switch (status)
5268251881Speter    {
5269251881Speter      case svn_wc__db_status_normal:
5270251881Speter      case svn_wc__db_status_added:
5271251881Speter        break;
5272251881Speter      case svn_wc__db_status_deleted:
5273251881Speter        return
5274251881Speter          svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5275251881Speter                            _("Can't add '%s' to a parent directory"
5276251881Speter                              " scheduled for deletion"),
5277251881Speter                            svn_dirent_local_style(local_abspath,
5278251881Speter                                                   scratch_pool));
5279251881Speter      default:
5280251881Speter        return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5281251881Speter                                 _("Can't find parent directory's node while"
5282251881Speter                                   " trying to add '%s'"),
5283251881Speter                                 svn_dirent_local_style(local_abspath,
5284251881Speter                                                        scratch_pool));
5285251881Speter    }
5286251881Speter  if (kind != svn_node_dir)
5287251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5288251881Speter                             _("Can't schedule an addition of '%s'"
5289251881Speter                               " below a not-directory node"),
5290251881Speter                             svn_dirent_local_style(local_abspath,
5291251881Speter                                                    scratch_pool));
5292251881Speter
5293251881Speter  /* Fabricate the anticipated new URL of the target and check the
5294251881Speter     copyfrom URL to be in the same repository. */
5295251881Speter  if (copyfrom_url != NULL)
5296251881Speter    {
5297251881Speter      /* Find the repository_root via the parent directory, which
5298251881Speter         is always versioned before this function is called */
5299251881Speter
5300251881Speter      if (!repos_root_url)
5301251881Speter        {
5302251881Speter          /* The parent is an addition, scan upwards to find the right info */
5303251881Speter          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5304251881Speter                                           &repos_root_url, &repos_uuid,
5305251881Speter                                           NULL, NULL, NULL, NULL,
5306251881Speter                                           wc_ctx->db, dir_abspath,
5307251881Speter                                           scratch_pool, scratch_pool));
5308251881Speter        }
5309251881Speter      SVN_ERR_ASSERT(repos_root_url);
5310251881Speter
5311251881Speter      original_repos_relpath =
5312251881Speter          svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5313251881Speter
5314251881Speter      if (!original_repos_relpath)
5315251881Speter        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5316251881Speter                                 _("Copyfrom-url '%s' has different repository"
5317251881Speter                                   " root than '%s'"),
5318251881Speter                                 copyfrom_url, repos_root_url);
5319251881Speter    }
5320251881Speter  else
5321251881Speter    {
5322251881Speter      original_repos_relpath = NULL;
5323251881Speter      copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5324251881Speter    }
5325251881Speter
5326251881Speter  /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5327251881Speter     filter NEW_BASE_PROPS so it contains only regular props. */
5328251881Speter  {
5329251881Speter    apr_array_header_t *regular_props;
5330251881Speter    apr_array_header_t *entry_props;
5331251881Speter
5332251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5333251881Speter                                 &entry_props, NULL, &regular_props,
5334251881Speter                                 pool));
5335251881Speter
5336251881Speter    /* Put regular props back into a hash table. */
5337251881Speter    new_base_props = svn_prop_array_to_hash(regular_props, pool);
5338251881Speter
5339251881Speter    /* Get the change_* info from the entry props.  */
5340251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5341251881Speter                                   &changed_date,
5342251881Speter                                   &changed_author,
5343251881Speter                                   entry_props, pool, pool));
5344251881Speter  }
5345251881Speter
5346251881Speter  /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5347251881Speter     it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5348251881Speter     NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5349251881Speter  {
5350251881Speter    svn_stream_t *tmp_base_contents;
5351251881Speter
5352251881Speter    SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5353251881Speter                                       &tmp_text_base_abspath,
5354251881Speter                                       &new_text_base_md5_checksum,
5355251881Speter                                       &new_text_base_sha1_checksum,
5356251881Speter                                       wc_ctx->db, local_abspath,
5357251881Speter                                       pool, pool));
5358251881Speter    SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5359251881Speter                             cancel_func, cancel_baton, pool));
5360251881Speter  }
5361251881Speter
5362251881Speter  /* If the caller gave us a new working file, copy it to a safe (temporary)
5363251881Speter     location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5364251881Speter     that into place after the node's state has been created.  */
5365251881Speter  if (new_contents)
5366251881Speter    {
5367251881Speter      const char *temp_dir_abspath;
5368251881Speter      svn_stream_t *tmp_contents;
5369251881Speter
5370251881Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5371251881Speter                                             local_abspath, pool, pool));
5372251881Speter      SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5373251881Speter                                     temp_dir_abspath, svn_io_file_del_none,
5374251881Speter                                     pool, pool));
5375251881Speter      SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5376251881Speter                               cancel_func, cancel_baton, pool));
5377251881Speter    }
5378251881Speter
5379251881Speter  /* Install new text base for copied files. Added files do NOT have a
5380251881Speter     text base.  */
5381251881Speter  if (copyfrom_url != NULL)
5382251881Speter    {
5383251881Speter      SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5384251881Speter                                          new_text_base_sha1_checksum,
5385251881Speter                                          new_text_base_md5_checksum, pool));
5386251881Speter    }
5387251881Speter  else
5388251881Speter    {
5389251881Speter      /* ### There's something wrong around here.  Sometimes (merge from a
5390251881Speter         foreign repository, at least) we are called with copyfrom_url =
5391251881Speter         NULL and an empty new_base_contents (and an empty set of
5392251881Speter         new_base_props).  Why an empty "new base"?
5393251881Speter
5394251881Speter         That happens in merge_tests.py 54,87,88,89,143.
5395251881Speter
5396251881Speter         In that case, having been given this supposed "new base" file, we
5397251881Speter         copy it and calculate its checksum but do not install it.  Why?
5398251881Speter         That must be wrong.
5399251881Speter
5400251881Speter         To crudely work around one issue with this, that we shouldn't
5401251881Speter         record a checksum in the database if we haven't installed the
5402251881Speter         corresponding pristine text, for now we'll just set the checksum
5403251881Speter         to NULL.
5404251881Speter
5405251881Speter         The proper solution is probably more like: the caller should pass
5406251881Speter         NULL for the missing information, and this function should learn to
5407251881Speter         handle that. */
5408251881Speter
5409251881Speter      new_text_base_sha1_checksum = NULL;
5410251881Speter      new_text_base_md5_checksum = NULL;
5411251881Speter    }
5412251881Speter
5413251881Speter  /* For added files without NEW_CONTENTS, then generate the working file
5414251881Speter     from the provided "pristine" contents.  */
5415251881Speter  if (new_contents == NULL && copyfrom_url == NULL)
5416251881Speter    source_abspath = tmp_text_base_abspath;
5417251881Speter
5418251881Speter  {
5419251881Speter    svn_boolean_t record_fileinfo;
5420251881Speter
5421251881Speter    /* If new contents were provided, then we do NOT want to record the
5422251881Speter       file information. We assume the new contents do not match the
5423251881Speter       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5424251881Speter    record_fileinfo = (new_contents == NULL);
5425251881Speter
5426251881Speter    /* Install the working copy file (with appropriate translation) from
5427251881Speter       the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5428251881Speter       installation from the pristine (available for copied/moved files),
5429251881Speter       or it will specify a temporary file where we placed a "pristine"
5430251881Speter       (for an added file) or a detranslated local-mods file.  */
5431251881Speter    SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5432251881Speter                                          db, local_abspath,
5433251881Speter                                          source_abspath,
5434251881Speter                                          FALSE /* use_commit_times */,
5435251881Speter                                          record_fileinfo,
5436251881Speter                                          pool, pool));
5437251881Speter    all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5438251881Speter
5439251881Speter    /* If we installed from somewhere besides the official pristine, then
5440251881Speter       it is a temporary file, which needs to be removed.  */
5441251881Speter    if (source_abspath != NULL)
5442251881Speter      {
5443251881Speter        SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5444251881Speter                                             source_abspath,
5445251881Speter                                             pool, pool));
5446251881Speter        all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5447251881Speter      }
5448251881Speter  }
5449251881Speter
5450251881Speter  /* ### ideally, we would have a single DB operation, and queue the work
5451251881Speter     ### items on that. for now, we'll queue them with the second call.  */
5452251881Speter
5453251881Speter  SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5454251881Speter                                  new_base_props,
5455251881Speter                                  changed_rev,
5456251881Speter                                  changed_date,
5457251881Speter                                  changed_author,
5458251881Speter                                  original_repos_relpath,
5459251881Speter                                  original_repos_relpath ? repos_root_url
5460251881Speter                                                         : NULL,
5461251881Speter                                  original_repos_relpath ? repos_uuid : NULL,
5462251881Speter                                  copyfrom_rev,
5463251881Speter                                  new_text_base_sha1_checksum,
5464251881Speter                                  TRUE,
5465251881Speter                                  new_props,
5466251881Speter                                  FALSE /* is_move */,
5467251881Speter                                  NULL /* conflict */,
5468251881Speter                                  all_work_items,
5469251881Speter                                  pool));
5470251881Speter
5471251881Speter  return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5472251881Speter                                        cancel_func, cancel_baton,
5473251881Speter                                        pool));
5474251881Speter}
5475251881Speter
5476251881Spetersvn_error_t *
5477251881Spetersvn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5478251881Speter                               const char *local_abspath,
5479251881Speter                               apr_hash_t *new_original_props,
5480251881Speter                               const char *copyfrom_url,
5481251881Speter                               svn_revnum_t copyfrom_rev,
5482251881Speter                               apr_pool_t *scratch_pool)
5483251881Speter{
5484251881Speter  svn_wc__db_status_t status;
5485251881Speter  svn_node_kind_t kind;
5486251881Speter  const char *original_repos_relpath;
5487251881Speter  const char *original_root_url;
5488251881Speter  const char *original_uuid;
5489251881Speter  svn_boolean_t had_props;
5490251881Speter  svn_boolean_t props_mod;
5491251881Speter
5492251881Speter  svn_revnum_t original_revision;
5493251881Speter  svn_revnum_t changed_rev;
5494251881Speter  apr_time_t changed_date;
5495251881Speter  const char *changed_author;
5496251881Speter
5497251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5498251881Speter                               NULL, NULL, NULL, NULL, NULL,
5499251881Speter                               &original_repos_relpath, &original_root_url,
5500251881Speter                               &original_uuid, &original_revision, NULL, NULL,
5501251881Speter                               NULL, NULL, NULL, NULL, &had_props, &props_mod,
5502251881Speter                               NULL, NULL, NULL,
5503251881Speter                               wc_ctx->db, local_abspath,
5504251881Speter                               scratch_pool, scratch_pool));
5505251881Speter
5506251881Speter  if (status != svn_wc__db_status_added
5507251881Speter      || kind != svn_node_dir
5508251881Speter      || had_props
5509251881Speter      || props_mod
5510251881Speter      || !original_repos_relpath)
5511251881Speter    {
5512251881Speter      return svn_error_createf(
5513251881Speter                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5514251881Speter                    _("'%s' is not an unmodified copied directory"),
5515251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5516251881Speter    }
5517251881Speter  if (original_revision != copyfrom_rev
5518251881Speter      || strcmp(copyfrom_url,
5519251881Speter                 svn_path_url_add_component2(original_root_url,
5520251881Speter                                             original_repos_relpath,
5521251881Speter                                             scratch_pool)))
5522251881Speter    {
5523251881Speter      return svn_error_createf(
5524251881Speter                    SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5525251881Speter                    _("Copyfrom '%s' doesn't match original location of '%s'"),
5526251881Speter                    copyfrom_url,
5527251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5528251881Speter    }
5529251881Speter
5530251881Speter  {
5531251881Speter    apr_array_header_t *regular_props;
5532251881Speter    apr_array_header_t *entry_props;
5533251881Speter
5534251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5535251881Speter                                                        scratch_pool),
5536251881Speter                                 &entry_props, NULL, &regular_props,
5537251881Speter                                 scratch_pool));
5538251881Speter
5539251881Speter    /* Put regular props back into a hash table. */
5540251881Speter    new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5541251881Speter
5542251881Speter    /* Get the change_* info from the entry props.  */
5543251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5544251881Speter                                   &changed_date,
5545251881Speter                                   &changed_author,
5546251881Speter                                   entry_props, scratch_pool, scratch_pool));
5547251881Speter  }
5548251881Speter
5549251881Speter  return svn_error_trace(
5550251881Speter            svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5551251881Speter                                   new_original_props,
5552251881Speter                                   changed_rev, changed_date, changed_author,
5553251881Speter                                   original_repos_relpath, original_root_url,
5554251881Speter                                   original_uuid, original_revision,
5555251881Speter                                   NULL /* children */,
5556251881Speter                                   FALSE /* is_move */,
5557251881Speter                                   svn_depth_infinity,
5558251881Speter                                   NULL /* conflict */,
5559251881Speter                                   NULL /* work_items */,
5560251881Speter                                   scratch_pool));
5561251881Speter}
5562