You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2013/01/02 15:38:26 UTC
svn commit: r1427801 [2/2] - in /subversion/trunk/subversion/libsvn_wc:
update_editor.c wcroot_anchor.c
Copied: subversion/trunk/subversion/libsvn_wc/wcroot_anchor.c (from r1426819, subversion/trunk/subversion/libsvn_wc/update_editor.c)
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wcroot_anchor.c?p2=subversion/trunk/subversion/libsvn_wc/wcroot_anchor.c&p1=subversion/trunk/subversion/libsvn_wc/update_editor.c&r1=1426819&r2=1427801&rev=1427801&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wcroot_anchor.c Wed Jan 2 14:38:26 2013
@@ -1,5 +1,5 @@
/*
- * update_editor.c : main editor for checkouts and updates
+ * wcroot_anchor.c : wcroot and anchor functions
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
@@ -26,4857 +26,17 @@
#include <stdlib.h>
#include <string.h>
-#include <apr_pools.h>
-#include <apr_hash.h>
-#include <apr_md5.h>
-#include <apr_tables.h>
-#include <apr_strings.h>
-
-#include "svn_types.h"
-#include "svn_pools.h"
-#include "svn_delta.h"
-#include "svn_hash.h"
-#include "svn_string.h"
-#include "svn_dirent_uri.h"
-#include "svn_path.h"
-#include "svn_error.h"
-#include "svn_io.h"
-#include "svn_private_config.h"
-#include "svn_time.h"
-
-#include "wc.h"
-#include "adm_files.h"
-#include "conflicts.h"
-#include "entries.h"
-#include "translate.h"
-#include "workqueue.h"
-
-#include "private/svn_subr_private.h"
-#include "private/svn_wc_private.h"
-/* Checks whether a svn_wc__db_status_t indicates whether a node is
- present in a working copy. Used by the editor implementation */
-#define IS_NODE_PRESENT(status) \
- ((status) != svn_wc__db_status_server_excluded &&\
- (status) != svn_wc__db_status_excluded && \
- (status) != svn_wc__db_status_not_present)
-
-static svn_error_t *
-path_join_under_root(const char **result_path,
- const char *base_path,
- const char *add_path,
- apr_pool_t *result_pool);
-
-
-/*
- * This code handles "checkout" and "update" and "switch".
- * A checkout is similar to an update that is only adding new items.
- *
- * The intended behaviour of "update" and "switch", focusing on the checks
- * to be made before applying a change, is:
- *
- * For each incoming change:
- * if target is already in conflict or obstructed:
- * skip this change
- * else
- * if this action will cause a tree conflict:
- * record the tree conflict
- * skip this change
- * else:
- * make this change
- *
- * In more detail:
- *
- * For each incoming change:
- *
- * 1. if # Incoming change is inside an item already in conflict:
- * a. tree/text/prop change to node beneath tree-conflicted dir
- * then # Skip all changes in this conflicted subtree [*1]:
- * do not update the Base nor the Working
- * notify "skipped because already in conflict" just once
- * for the whole conflicted subtree
- *
- * if # Incoming change affects an item already in conflict:
- * b. tree/text/prop change to tree-conflicted dir/file, or
- * c. tree change to a text/prop-conflicted file/dir, or
- * d. text/prop change to a text/prop-conflicted file/dir [*2], or
- * e. tree change to a dir tree containing any conflicts,
- * then # Skip this change [*1]:
- * do not update the Base nor the Working
- * notify "skipped because already in conflict"
- *
- * 2. if # Incoming change affects an item that's "obstructed":
- * a. on-disk node kind doesn't match recorded Working node kind
- * (including an absence/presence mis-match),
- * then # Skip this change [*1]:
- * do not update the Base nor the Working
- * notify "skipped because obstructed"
- *
- * 3. if # Incoming change raises a tree conflict:
- * a. tree/text/prop change to node beneath sched-delete dir, or
- * b. tree/text/prop change to sched-delete dir/file, or
- * c. text/prop change to tree-scheduled dir/file,
- * then # Skip this change:
- * do not update the Base nor the Working [*3]
- * notify "tree conflict"
- *
- * 4. Apply the change:
- * update the Base
- * update the Working, possibly raising text/prop conflicts
- * notify
- *
- * Notes:
- *
- * "Tree change" here refers to an add or delete of the target node,
- * including the add or delete part of a copy or move or rename.
- *
- * [*1] We should skip changes to an entire node, as the base revision number
- * applies to the entire node. Not sure how this affects attempts to
- * handle text and prop changes separately.
- *
- * [*2] Details of which combinations of property and text changes conflict
- * are not specified here.
- *
- * [*3] For now, we skip the update, and require the user to:
- * - Modify the WC to be compatible with the incoming change;
- * - Mark the conflict as resolved;
- * - Repeat the update.
- * Ideally, it would be possible to resolve any conflict without
- * repeating the update. To achieve this, we would have to store the
- * necessary data at conflict detection time, and delay the update of
- * the Base until the time of resolving.
- */
-
-
-/*** batons ***/
-
-struct edit_baton
-{
- /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
- directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
- target, the values are identical.
-
- TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
- ANCHOR_ABSPATH is the target */
- const char *target_basename;
-
- /* Absolute variants of ANCHOR and TARGET */
- const char *anchor_abspath;
- const char *target_abspath;
-
- /* The DB handle for managing the working copy state. */
- svn_wc__db_t *db;
-
- /* Array of file extension patterns to preserve as extensions in
- generated conflict files. */
- const apr_array_header_t *ext_patterns;
-
- /* Hash mapping const char * absolute working copy paths to depth-first
- ordered arrays of svn_prop_inherited_item_t * structures representing
- the properties inherited by the base node at that working copy path.
- May be NULL. */
- apr_hash_t *wcroot_iprops;
-
- /* The revision we're targeting...or something like that. This
- starts off as a pointer to the revision to which we are updating,
- or SVN_INVALID_REVNUM, but by the end of the edit, should be
- pointing to the final revision. */
- svn_revnum_t *target_revision;
-
- /* The requested depth of this edit. */
- svn_depth_t requested_depth;
-
- /* Is the requested depth merely an operational limitation, or is
- also the new sticky ambient depth of the update target? */
- svn_boolean_t depth_is_sticky;
-
- /* Need to know if the user wants us to overwrite the 'now' times on
- edited/added files with the last-commit-time. */
- svn_boolean_t use_commit_times;
-
- /* Was the root actually opened (was this a non-empty edit)? */
- svn_boolean_t root_opened;
-
- /* Was the update-target deleted? This is a special situation. */
- svn_boolean_t target_deleted;
-
- /* Allow unversioned obstructions when adding a path. */
- svn_boolean_t allow_unver_obstructions;
-
- /* Handle local additions as modifications of new nodes */
- svn_boolean_t adds_as_modification;
-
- /* If set, we check out into an empty directory. This allows for a number
- of conflict checks to be omitted. */
- svn_boolean_t clean_checkout;
-
- /* If this is a 'switch' operation, the new relpath of target_abspath,
- else NULL. */
- const char *switch_relpath;
-
- /* The URL to the root of the repository. */
- const char *repos_root;
-
- /* The UUID of the repos, or NULL. */
- const char *repos_uuid;
-
- /* External diff3 to use for merges (can be null, in which case
- internal merge code is used). */
- const char *diff3_cmd;
-
- /* Externals handler */
- svn_wc_external_update_t external_func;
- void *external_baton;
-
- /* This editor sends back notifications as it edits. */
- svn_wc_notify_func2_t notify_func;
- void *notify_baton;
-
- /* This editor is normally wrapped in a cancellation editor anyway,
- so it doesn't bother to check for cancellation itself. However,
- it needs a cancel_func and cancel_baton available to pass to
- long-running functions. */
- svn_cancel_func_t cancel_func;
- void *cancel_baton;
-
- /* This editor will invoke a interactive conflict-resolution
- callback, if available. */
- svn_wc_conflict_resolver_func2_t conflict_func;
- void *conflict_baton;
-
- /* Subtrees that were skipped during the edit, and therefore shouldn't
- have their revision/url info updated at the end. If a path is a
- directory, its descendants will also be skipped. The keys are paths
- relative to the working copy root and the values unspecified. */
- apr_hash_t *skipped_trees;
-
- /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
- returned from fetch_dirents_func for that repos_relpath. These
- are used to avoid issue #3569 in specific update scenarios where a
- restricted depth is used. */
- apr_hash_t *dir_dirents;
-
- /* Absolute path of the working copy root or NULL if not initialized yet */
- const char *wcroot_abspath;
-
- apr_pool_t *pool;
-};
-
-
-/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
- * updated.
- *
- * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
- * LOCAL_ABSPATH.
- */
-static svn_error_t *
-remember_skipped_tree(struct edit_baton *eb,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
- apr_hash_set(eb->skipped_trees,
- apr_pstrdup(eb->pool,
- svn_dirent_skip_ancestor(eb->wcroot_abspath,
- local_abspath)),
- APR_HASH_KEY_STRING, (void*)1);
-
- return SVN_NO_ERROR;
-}
-
-struct dir_baton
-{
- /* Basename of this directory. */
- const char *name;
-
- /* Absolute path of this directory */
- const char *local_abspath;
-
- /* The repository relative path this directory will correspond to. */
- const char *new_relpath;
-
- /* The revision of the directory before updating */
- svn_revnum_t old_revision;
-
- /* The repos_relpath before updating/switching */
- const char *old_repos_relpath;
-
- /* The global edit baton. */
- struct edit_baton *edit_baton;
-
- /* Baton for this directory's parent, or NULL if this is the root
- directory. */
- struct dir_baton *parent_baton;
-
- /* Set if updates to this directory are skipped */
- svn_boolean_t skip_this;
-
- /* Set if there was a previous notification for this directory */
- svn_boolean_t already_notified;
-
- /* Set if this directory is being added during this editor drive. */
- svn_boolean_t adding_dir;
-
- /* Set on a node and its descendants are not present in the working copy
- but should still be updated (not skipped). These nodes should all be
- marked as deleted. */
- svn_boolean_t shadowed;
-
- /* The (new) changed_* information, cached to avoid retrieving it later */
- svn_revnum_t changed_rev;
- apr_time_t changed_date;
- const char *changed_author;
-
- /* If not NULL, contains a mapping of const char* basenames of children that
- have been deleted to their svn_skel_t* tree conflicts.
- We store this hash to allow replacements to continue under a just
- installed tree conflict.
-
- The add after the delete will then update the tree conflicts information
- and reinstall it. */
- apr_hash_t *deletion_conflicts;
-
- /* A hash of file names (only the hash key matters) seen by add_file
- and not yet added to the database by close_file. */
- apr_hash_t *not_present_files;
-
- /* Set if an unversioned dir of the same name already existed in
- this directory. */
- svn_boolean_t obstruction_found;
-
- /* Set if a dir of the same name already exists and is
- scheduled for addition without history. */
- svn_boolean_t add_existed;
-
- /* An array of svn_prop_t structures, representing all the property
- changes to be applied to this directory. */
- apr_array_header_t *propchanges;
-
- /* A boolean indicating whether this node or one of its children has
- received any 'real' changes. Used to avoid tree conflicts for simple
- entryprop changes, like lock management */
- svn_boolean_t edited;
-
- /* The tree conflict to install once the node is really edited */
- svn_skel_t *edit_conflict;
-
- /* The bump information for this directory. */
- struct bump_dir_info *bump_info;
-
- /* The depth of the directory in the wc (or inferred if added). Not
- used for filtering; we have a separate wrapping editor for that. */
- svn_depth_t ambient_depth;
-
- /* Was the directory marked as incomplete before the update?
- (In other words, are we resuming an interrupted update?)
-
- If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
- and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
- we only receive the changes in/for children and properties.*/
- svn_boolean_t was_incomplete;
-
- /* The pool in which this baton itself is allocated. */
- apr_pool_t *pool;
-};
-
-
-/* The bump information is tracked separately from the directory batons.
- This is a small structure kept in the edit pool, while the heavier
- directory baton is managed by the editor driver.
-
- In a postfix delta case, the directory batons are going to disappear.
- The files will refer to these structures, rather than the full
- directory baton. */
-struct bump_dir_info
-{
- /* ptr to the bump information for the parent directory */
- struct bump_dir_info *parent;
-
- /* how many entries are referring to this bump information? */
- int ref_count;
-
- /* Pool that should be cleared after the dir is bumped */
- apr_pool_t *pool;
-};
-
-
-struct handler_baton
-{
- svn_txdelta_window_handler_t apply_handler;
- void *apply_baton;
- apr_pool_t *pool;
- struct file_baton *fb;
-
- /* Where we are assembling the new file. */
- const char *new_text_base_tmp_abspath;
-
- /* The expected source checksum of the text source or NULL if no base
- checksum is available (MD5 if the server provides a checksum, SHA1 if
- the server doesn't) */
- svn_checksum_t *expected_source_checksum;
-
- /* Why two checksums?
- The editor currently provides an md5 which we use to detect corruption
- during transmission. We use the sha1 inside libsvn_wc both for pristine
- handling and corruption detection. In the future, the editor will also
- provide a sha1, so we may not have to calculate both, but for the time
- being, that's the way it is. */
-
- /* The calculated checksum of the text source or NULL if the acual
- checksum is not being calculated. The checksum kind is identical to the
- kind of expected_source_checksum. */
- svn_checksum_t *actual_source_checksum;
-
- /* The stream used to calculate the source checksums */
- svn_stream_t *source_checksum_stream;
-
- /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
- This is initialized to all zeroes when the baton is created, then
- populated with the MD5 digest of the resultant fulltext after the
- last window is handled by the handler returned from
- apply_textdelta(). */
- unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
-
- /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
- eventually writing the pristine. */
- svn_checksum_t * new_text_base_sha1_checksum;
-};
-
-
-/* Get an empty file in the temporary area for WRI_ABSPATH. The file will
- not be set for automatic deletion, and the name will be returned in
- TMP_FILENAME.
-
- This implementation creates a new empty file with a unique name.
-
- ### This is inefficient for callers that just want an empty file to read
- ### from. There could be (and there used to be) a permanent, shared
- ### empty file for this purpose.
-
- ### This is inefficient for callers that just want to reserve a unique
- ### file name to create later. A better way may not be readily available.
- */
-static svn_error_t *
-get_empty_tmp_file(const char **tmp_filename,
- svn_wc__db_t *db,
- const char *wri_abspath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- const char *temp_dir_abspath;
-
- SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
- svn_io_file_del_none,
- scratch_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-/* An APR pool cleanup handler. This runs the working queue for an
- editor baton. */
-static apr_status_t
-cleanup_edit_baton(void *edit_baton)
-{
- struct edit_baton *eb = edit_baton;
- svn_error_t *err;
- apr_pool_t *pool = apr_pool_parent_get(eb->pool);
-
- err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
- NULL /* cancel_func */, NULL /* cancel_baton */,
- pool);
-
- if (err)
- {
- apr_status_t apr_err = err->apr_err;
- svn_error_clear(err);
- return apr_err;
- }
- return APR_SUCCESS;
-}
-
-/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
- If PATH and PB are NULL, this is the root directory of the edit; in this
- case, make the new dir baton in a subpool of EB->pool.
- ADDING should be TRUE if we are adding this directory. */
-static svn_error_t *
-make_dir_baton(struct dir_baton **d_p,
- const char *path,
- struct edit_baton *eb,
- struct dir_baton *pb,
- svn_boolean_t adding,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *dir_pool;
- struct dir_baton *d;
- struct bump_dir_info *bdi;
-
- if (pb != NULL)
- dir_pool = svn_pool_create(pb->pool);
- else
- dir_pool = svn_pool_create(eb->pool);
-
- SVN_ERR_ASSERT(path || (! pb));
-
- /* Okay, no easy out, so allocate and initialize a dir baton. */
- d = apr_pcalloc(dir_pool, sizeof(*d));
-
- /* Construct the PATH and baseNAME of this directory. */
- if (path)
- {
- d->name = svn_dirent_basename(path, dir_pool);
- SVN_ERR(path_join_under_root(&d->local_abspath,
- pb->local_abspath, d->name, dir_pool));
- }
- else
- {
- /* This is the root baton. */
- d->name = NULL;
- d->local_abspath = eb->anchor_abspath;
- }
-
- /* Figure out the new_relpath for this directory. */
- if (eb->switch_relpath)
- {
- /* Handle switches... */
-
- if (pb == NULL)
- {
- if (*eb->target_basename == '\0')
- {
- /* No parent baton and target_basename=="" means that we are
- the target of the switch. Thus, our NEW_RELPATH will be
- the SWITCH_RELPATH. */
- d->new_relpath = eb->switch_relpath;
- }
- else
- {
- /* This node is NOT the target of the switch (one of our
- children is the target); therefore, it must already exist.
- Get its old REPOS_RELPATH, as it won't be changing. */
- SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
- eb->db, d->local_abspath,
- dir_pool, scratch_pool));
- }
- }
- else
- {
- /* This directory is *not* the root (has a parent). If there is
- no grandparent, then we may have anchored at the parent,
- and self is the target. If we match the target, then set
- NEW_RELPATH to the SWITCH_RELPATH.
-
- Otherwise, we simply extend NEW_RELPATH from the parent. */
- if (pb->parent_baton == NULL
- && strcmp(eb->target_basename, d->name) == 0)
- d->new_relpath = eb->switch_relpath;
- else
- d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
- dir_pool);
- }
- }
- else /* must be an update */
- {
- /* If we are adding the node, then simply extend the parent's
- relpath for our own. */
- if (adding)
- {
- SVN_ERR_ASSERT(pb != NULL);
- d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
- dir_pool);
- }
- else
- {
- SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
- eb->db, d->local_abspath,
- dir_pool, scratch_pool));
- SVN_ERR_ASSERT(d->new_relpath);
- }
- }
-
- /* the bump information lives in the edit pool */
- bdi = apr_pcalloc(dir_pool, sizeof(*bdi));
- bdi->parent = pb ? pb->bump_info : NULL;
- bdi->ref_count = 1;
- bdi->pool = dir_pool;
-
- /* the parent's bump info has one more referer */
- if (pb)
- ++bdi->parent->ref_count;
-
- d->edit_baton = eb;
- d->parent_baton = pb;
- d->pool = dir_pool;
- d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
- d->obstruction_found = FALSE;
- d->add_existed = FALSE;
- d->bump_info = bdi;
- d->old_revision = SVN_INVALID_REVNUM;
- d->adding_dir = adding;
- d->changed_rev = SVN_INVALID_REVNUM;
- d->not_present_files = apr_hash_make(dir_pool);
-
- /* Copy some flags from the parent baton */
- if (pb)
- {
- d->skip_this = pb->skip_this;
- d->shadowed = pb->shadowed;
- }
-
- /* The caller of this function needs to fill these in. */
- d->ambient_depth = svn_depth_unknown;
- d->was_incomplete = FALSE;
-
- *d_p = d;
- return SVN_NO_ERROR;
-}
-
-
-/* Forward declarations. */
-static svn_error_t *
-already_in_a_tree_conflict(svn_boolean_t *conflicted,
- svn_wc__db_t *db,
- const char *local_abspath,
- apr_pool_t *scratch_pool);
-
-
-static void
-do_notification(const struct edit_baton *eb,
- const char *local_abspath,
- svn_node_kind_t kind,
- svn_wc_notify_action_t action,
- apr_pool_t *scratch_pool)
-{
- svn_wc_notify_t *notify;
-
- if (eb->notify_func == NULL)
- return;
-
- notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
- notify->kind = kind;
-
- (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
-}
-
-/* Decrement the bump_dir_info's reference count. If it hits zero,
- then this directory is "done". This means it is safe to clear its pool.
-
- In addition, when the directory is "done", we loop onto the parent's
- bump information to possibly mark it as done, too.
-*/
-static svn_error_t *
-maybe_release_dir_info(struct bump_dir_info *bdi)
-{
- /* Keep moving up the tree of directories until we run out of parents,
- or a directory is not yet "done". */
- while (bdi != NULL)
- {
- apr_pool_t *destroy_pool;
-
- if (--bdi->ref_count > 0)
- break; /* directory isn't done yet */
-
- destroy_pool = bdi->pool;
- bdi = bdi->parent;
-
- svn_pool_destroy(destroy_pool);
- }
- /* we exited the for loop because there are no more parents */
-
- return SVN_NO_ERROR;
-}
-
-struct file_baton
-{
- /* Pool specific to this file_baton. */
- apr_pool_t *pool;
-
- /* Name of this file (its entry in the directory). */
- const char *name;
-
- /* Absolute path to this file */
- const char *local_abspath;
-
- /* The repository relative path this file will correspond to. */
- const char *new_relpath;
-
- /* The revision of the file before updating */
- svn_revnum_t old_revision;
-
- /* The repos_relpath before updating/switching */
- const char *old_repos_relpath;
-
- /* The global edit baton. */
- struct edit_baton *edit_baton;
-
- /* The parent directory of this file. */
- struct dir_baton *dir_baton;
-
- /* Set if updates to this directory are skipped */
- svn_boolean_t skip_this;
-
- /* Set if there was a previous notification */
- svn_boolean_t already_notified;
-
- /* Set if this file is new. */
- svn_boolean_t adding_file;
-
- /* Set if an unversioned file of the same name already existed in
- this directory. */
- svn_boolean_t obstruction_found;
-
- /* Set if a file of the same name already exists and is
- scheduled for addition without history. */
- svn_boolean_t add_existed;
-
- /* Set if this file is being added in the BASE layer, but is not-present
- in the working copy (replaced, deleted, etc.). */
- svn_boolean_t shadowed;
-
- /* The (new) changed_* information, cached to avoid retrieving it later */
- svn_revnum_t changed_rev;
- apr_time_t changed_date;
- const char *changed_author;
-
- /* If there are file content changes, these are the checksums of the
- resulting new text base, which is in the pristine store, else NULL. */
- const svn_checksum_t *new_text_base_md5_checksum;
- const svn_checksum_t *new_text_base_sha1_checksum;
-
- /* The checksum of the file before the update */
- const svn_checksum_t *original_checksum;
-
- /* An array of svn_prop_t structures, representing all the property
- changes to be applied to this file. Once a file baton is
- initialized, this is never NULL, but it may have zero elements. */
- apr_array_header_t *propchanges;
-
- /* For existing files, whether there are local modifications. FALSE for added
- files */
- svn_boolean_t local_prop_mods;
-
- /* Bump information for the directory this file lives in */
- struct bump_dir_info *bump_info;
-
- /* A boolean indicating whether this node or one of its children has
- received any 'real' changes. Used to avoid tree conflicts for simple
- entryprop changes, like lock management */
- svn_boolean_t edited;
-
- /* The tree conflict to install once the node is really edited */
- svn_skel_t *edit_conflict;
-};
-
-
-/* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
- * PATH is relative to the root of the edit. ADDING tells whether this file
- * is being added. */
-static svn_error_t *
-make_file_baton(struct file_baton **f_p,
- struct dir_baton *pb,
- const char *path,
- svn_boolean_t adding,
- apr_pool_t *scratch_pool)
-{
- struct edit_baton *eb = pb->edit_baton;
- apr_pool_t *file_pool = svn_pool_create(pb->pool);
- struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
-
- SVN_ERR_ASSERT(path);
-
- /* Make the file's on-disk name. */
- f->name = svn_dirent_basename(path, file_pool);
- f->old_revision = SVN_INVALID_REVNUM;
- SVN_ERR(path_join_under_root(&f->local_abspath,
- pb->local_abspath, f->name, file_pool));
-
- /* Figure out the new URL for this file. */
- if (eb->switch_relpath)
- {
- /* Handle switches... */
-
- /* This file has a parent directory. If there is
- no grandparent, then we may have anchored at the parent,
- and self is the target. If we match the target, then set
- NEW_RELPATH to the SWITCH_RELPATH.
-
- Otherwise, we simply extend NEW_RELPATH from the parent. */
- if (pb->parent_baton == NULL
- && strcmp(eb->target_basename, f->name) == 0)
- f->new_relpath = eb->switch_relpath;
- else
- f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
- file_pool);
- }
- else /* must be an update */
- {
- if (adding)
- f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
- else
- {
- SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
- eb->db, f->local_abspath,
- file_pool, scratch_pool));
- SVN_ERR_ASSERT(f->new_relpath);
- }
- }
-
- f->pool = file_pool;
- f->edit_baton = pb->edit_baton;
- f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
- f->bump_info = pb->bump_info;
- f->adding_file = adding;
- f->obstruction_found = FALSE;
- f->add_existed = FALSE;
- f->skip_this = pb->skip_this;
- f->shadowed = pb->shadowed;
- f->dir_baton = pb;
- f->changed_rev = SVN_INVALID_REVNUM;
-
- /* the directory's bump info has one more referer now */
- ++f->bump_info->ref_count;
-
- *f_p = f;
- return SVN_NO_ERROR;
-}
-
-/* Complete a conflict skel by describing the update.
- *
- * All temporary allocations are be made in SCRATCH_POOL, while allocations
- * needed for the returned conflict struct are made in RESULT_POOL.
- */
-static svn_error_t *
-complete_conflict(svn_skel_t *conflict,
- const struct dir_baton *pb,
- const char *local_abspath,
- const char *old_repos_relpath,
- svn_revnum_t old_revision,
- svn_node_kind_t kind,
- apr_pool_t *result_pool, apr_pool_t *scratch_pool)
-{
- const struct edit_baton *eb = pb->edit_baton;
- svn_wc_conflict_version_t *src_left_version;
- svn_boolean_t is_complete;
-
- if (!conflict)
- return SVN_NO_ERROR; /* Not conflicted */
-
- SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
-
- if (is_complete)
- return SVN_NO_ERROR; /* Already completed */
-
- if (old_repos_relpath)
- src_left_version = svn_wc_conflict_version_create2(eb->repos_root,
- eb->repos_uuid,
- old_repos_relpath,
- old_revision,
- kind,
- result_pool);
- else
- src_left_version = NULL;
-
-
- if (eb->switch_relpath)
- SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
- src_left_version,
- result_pool, scratch_pool));
- else
- SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
- src_left_version,
- result_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-
-/* Called when a directory is really edited, to avoid marking a
- tree conflict on a node for a no-change edit */
-static svn_error_t *
-mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
-{
- if (db->edited)
- return SVN_NO_ERROR;
-
- if (db->parent_baton)
- SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
-
- db->edited = TRUE;
-
- if (db->edit_conflict)
- {
- /* We have a (delayed) tree conflict to install */
-
- SVN_ERR(complete_conflict(db->edit_conflict, db->parent_baton,
- db->local_abspath, db->old_repos_relpath,
- db->old_revision, svn_node_dir,
- db->pool, scratch_pool));
- SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
- db->local_abspath,
- db->edit_conflict, NULL,
- scratch_pool));
-
- do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
- svn_wc_notify_tree_conflict, scratch_pool);
- db->already_notified = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Called when a file is really edited, to avoid marking a
- tree conflict on a node for a no-change edit */
-static svn_error_t *
-mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
-{
- if (fb->edited)
- return SVN_NO_ERROR;
-
- SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
-
- fb->edited = TRUE;
-
- if (fb->edit_conflict)
- {
- /* We have a (delayed) tree conflict to install */
-
- SVN_ERR(complete_conflict(fb->edit_conflict, fb->dir_baton,
- fb->local_abspath, fb->old_repos_relpath,
- fb->old_revision, svn_node_file,
- fb->pool, scratch_pool));
-
- SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
- fb->local_abspath,
- fb->edit_conflict, NULL,
- scratch_pool));
-
- do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
- svn_wc_notify_tree_conflict, scratch_pool);
- fb->already_notified = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Handle the next delta window of the file described by BATON. If it is
- * the end (WINDOW == NULL), then check the checksum, store the text in the
- * pristine store and write its details into BATON->fb->new_text_base_*. */
-static svn_error_t *
-window_handler(svn_txdelta_window_t *window, void *baton)
-{
- struct handler_baton *hb = baton;
- struct file_baton *fb = hb->fb;
- svn_wc__db_t *db = fb->edit_baton->db;
- svn_error_t *err;
-
- /* Apply this window. We may be done at that point. */
- err = hb->apply_handler(window, hb->apply_baton);
- if (window != NULL && !err)
- return SVN_NO_ERROR;
-
- if (hb->expected_source_checksum)
- {
- /* Close the stream to calculate HB->actual_source_md5_checksum. */
- svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
-
- if (!err2)
- {
- SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
- hb->actual_source_checksum->kind);
-
- if (!svn_checksum_match(hb->expected_source_checksum,
- hb->actual_source_checksum))
- {
- err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
- _("Checksum mismatch while updating '%s':\n"
- " expected: %s\n"
- " actual: %s\n"),
- svn_dirent_local_style(fb->local_abspath, hb->pool),
- svn_checksum_to_cstring(hb->expected_source_checksum,
- hb->pool),
- svn_checksum_to_cstring(hb->actual_source_checksum,
- hb->pool));
- }
- }
-
- err = svn_error_compose_create(err, err2);
- }
-
- if (err)
- {
- /* We failed to apply the delta; clean up the temporary file. */
- svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, TRUE,
- hb->pool));
- }
- else
- {
- /* Tell the file baton about the new text base's checksums. */
- fb->new_text_base_md5_checksum =
- svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
- fb->new_text_base_sha1_checksum =
- svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
-
- /* Store the new pristine text in the pristine store now. Later, in a
- single transaction we will update the BASE_NODE to include a
- reference to this pristine text's checksum. */
- SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
- fb->new_text_base_sha1_checksum,
- fb->new_text_base_md5_checksum,
- hb->pool));
- }
-
- svn_pool_destroy(hb->pool);
-
- return err;
-}
-
-
-/* Find the last-change info within ENTRY_PROPS, and return then in the
- CHANGED_* parameters. Each parameter will be initialized to its "none"
- value, and will contain the relavent info if found.
-
- CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
- used for some temporary allocations.
-*/
-static svn_error_t *
-accumulate_last_change(svn_revnum_t *changed_rev,
- apr_time_t *changed_date,
- const char **changed_author,
- const apr_array_header_t *entry_props,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- int i;
-
- *changed_rev = SVN_INVALID_REVNUM;
- *changed_date = 0;
- *changed_author = NULL;
-
- for (i = 0; i < entry_props->nelts; ++i)
- {
- const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
-
- /* A prop value of NULL means the information was not
- available. We don't remove this field from the entries
- file; we have convention just leave it empty. So let's
- just skip those entry props that have no values. */
- if (! prop->value)
- continue;
-
- if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
- *changed_author = apr_pstrdup(result_pool, prop->value->data);
- else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
- {
- apr_int64_t rev;
- SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
- *changed_rev = (svn_revnum_t)rev;
- }
- else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
- SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
- scratch_pool));
-
- /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
- property here. */
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".."
- * component of it resolves to a path above BASE_PATH, then return
- * SVN_ERR_WC_OBSTRUCTED_UPDATE.
- *
- * This is to prevent the situation where the repository contains,
- * say, "..\nastyfile". Although that's perfectly legal on some
- * systems, when checked out onto Win32 it would cause "nastyfile" to
- * be created in the parent of the current edit directory.
- *
- * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
- */
-static svn_error_t *
-path_join_under_root(const char **result_path,
- const char *base_path,
- const char *add_path,
- apr_pool_t *pool)
-{
- svn_boolean_t under_root;
-
- SVN_ERR(svn_dirent_is_under_root(&under_root,
- result_path, base_path, add_path, pool));
-
- if (! under_root)
- {
- return svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Path '%s' is not in the working copy"),
- svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-set_target_revision(void *edit_baton,
- svn_revnum_t target_revision,
- apr_pool_t *pool)
-{
- struct edit_baton *eb = edit_baton;
-
- *(eb->target_revision) = target_revision;
- return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-open_root(void *edit_baton,
- svn_revnum_t base_revision, /* This is ignored in co */
- apr_pool_t *pool,
- void **dir_baton)
-{
- struct edit_baton *eb = edit_baton;
- struct dir_baton *db;
- svn_boolean_t already_conflicted;
- svn_error_t *err;
- svn_wc__db_status_t status;
-
- /* Note that something interesting is actually happening in this
- edit run. */
- eb->root_opened = TRUE;
-
- SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
- *dir_baton = db;
-
- err = already_in_a_tree_conflict(&already_conflicted, eb->db,
- db->local_abspath, pool);
-
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- already_conflicted = FALSE;
- }
- else if (already_conflicted)
- {
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- /* Notify that we skipped the target, while we actually skipped
- the anchor */
- do_notification(eb, eb->target_abspath, svn_node_unknown,
- svn_wc_notify_skip_conflicted, pool);
-
- return SVN_NO_ERROR;
- }
-
- if (*eb->target_basename == '\0')
- {
- /* For an update with a NULL target, this is equivalent to open_dir(): */
-
- /* Read the depth from the entry. */
- SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &db->old_revision,
- &db->old_repos_relpath, NULL, NULL,
- &db->changed_rev, &db->changed_date,
- &db->changed_author, &db->ambient_depth,
- NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, db->local_abspath,
- db->pool, pool));
- db->was_incomplete = (status == svn_wc__db_status_incomplete);
-
- SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
- db->local_abspath,
- db->new_relpath,
- *eb->target_revision,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* ===================================================================== */
-/* Checking for local modifications. */
-
-/* A baton for use with modcheck_found_entry(). */
-typedef struct modcheck_baton_t {
- svn_wc__db_t *db; /* wc_db to access nodes */
- svn_boolean_t found_mod; /* whether a modification has been found */
- svn_boolean_t found_not_delete; /* Found a not-delete modification */
- svn_boolean_t is_copy; /* check for post-copy modifications only */
- const char *modcheck_root_abspath;
-} modcheck_baton_t;
-
-/* An implementation of svn_wc_status_func4_t. */
-static svn_error_t *
-modcheck_callback(void *baton,
- const char *local_abspath,
- const svn_wc_status3_t *status,
- apr_pool_t *scratch_pool)
-{
- modcheck_baton_t *mb = baton;
-
- switch (status->node_status)
- {
- case svn_wc_status_normal:
- case svn_wc_status_incomplete:
- case svn_wc_status_ignored:
- case svn_wc_status_none:
- case svn_wc_status_unversioned:
- case svn_wc_status_external:
- break;
-
- case svn_wc_status_deleted:
- mb->found_mod = TRUE;
- break;
-
- case svn_wc_status_missing:
- case svn_wc_status_obstructed:
- if (status->prop_status != svn_wc_status_modified)
- break;
-
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
-
- case svn_wc_status_added:
- /* Ignore the copy status if we expect a copy, but don't ignore
- other changes */
- if (!mb->is_copy
- || status->text_status == svn_wc_status_modified
- || status->prop_status == svn_wc_status_modified
- || strcmp(mb->modcheck_root_abspath, local_abspath) != 0)
- {
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
- }
- break;
-
- default:
- case svn_wc_status_replaced:
- case svn_wc_status_modified:
- mb->found_mod = TRUE;
- mb->found_not_delete = TRUE;
- /* Exit from the status walker: We know what we want to know */
- return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Set *MODIFIED to true iff there are any local modifications within the
- * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
- * is set to true and all the local modifications were deletes then set
- * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
- * may be a file or a directory. If IS_COPY is TRUE, LOCAL_ABSPATH is a
- * copied (or moved) node, and nodes at or within LOCAL_ABSPATH are only
- * considered modified if they were modified post-copy. */
-static svn_error_t *
-node_has_local_mods(svn_boolean_t *modified,
- svn_boolean_t *all_edits_are_deletes,
- svn_boolean_t is_copy,
- svn_wc__db_t *db,
- const char *local_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE, FALSE };
- svn_error_t *err;
-
- modcheck_baton.db = db;
- modcheck_baton.is_copy = is_copy;
- modcheck_baton.modcheck_root_abspath = local_abspath;
-
- /* Walk the WC tree for status with depth infinity, looking for any local
- * modifications. If it's a "sparse" directory, that's OK: there can be
- * no local mods in the pieces that aren't present in the WC. */
-
- err = svn_wc__internal_walk_status(db, local_abspath,
- svn_depth_infinity,
- FALSE, FALSE, FALSE, NULL,
- modcheck_callback, &modcheck_baton,
- cancel_func, cancel_baton,
- scratch_pool);
-
- if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
- svn_error_clear(err);
- else
- SVN_ERR(err);
-
- *modified = modcheck_baton.found_mod;
- *all_edits_are_deletes = !modcheck_baton.found_not_delete;
-
- return SVN_NO_ERROR;
-}
-
-/* Indicates an unset svn_wc_conflict_reason_t. */
-#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
-
-/* Check whether the incoming change ACTION on FULL_PATH would conflict with
- * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
- * LOCAL_ABSPATH as the victim.
- *
- * The edit baton EB gives information including whether the operation is
- * an update or a switch.
- *
- * WORKING_STATUS and WORKING_KIND are the current node status of LOCAL_ABSPATH
- * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
- * for this node.
- *
- * If a tree conflict reason was found for the incoming action, the resulting
- * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
- * while *PCONFLICT is always overwritten.
- *
- * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
- * SCRATCH_POOL.
- */
-static svn_error_t *
-check_tree_conflict(svn_skel_t **pconflict,
- struct edit_baton *eb,
- const char *local_abspath,
- svn_wc__db_status_t working_status,
- svn_kind_t working_kind,
- svn_boolean_t exists_in_repos,
- svn_wc_conflict_action_t action,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
- svn_boolean_t modified = FALSE;
- svn_boolean_t all_mods_are_deletes = FALSE;
-
- *pconflict = NULL;
-
- /* Find out if there are any local changes to this node that may
- * be the "reason" of a tree-conflict with the incoming "action". */
- switch (working_status)
- {
- case svn_wc__db_status_added:
- case svn_wc__db_status_moved_here:
- case svn_wc__db_status_copied:
- if (!exists_in_repos)
- {
- /* The node is locally added, and it did not exist before. This
- * is an 'update', so the local add can only conflict with an
- * incoming 'add'. In fact, if we receive anything else than an
- * svn_wc_conflict_action_add (which includes 'added',
- * 'copied-here' and 'moved-here') during update on a node that
- * did not exist before, then something is very wrong.
- * Note that if there was no action on the node, this code
- * would not have been called in the first place. */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
-
- /* Scan the addition in case our caller didn't. */
- if (working_status == svn_wc__db_status_added)
- SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- eb->db, local_abspath,
- scratch_pool, scratch_pool));
-
- if (working_status == svn_wc__db_status_moved_here)
- reason = svn_wc_conflict_reason_moved_here;
- else
- reason = svn_wc_conflict_reason_added;
- }
- else
- {
- /* The node is locally replaced. */
- reason = svn_wc_conflict_reason_replaced;
- }
- break;
-
-
- case svn_wc__db_status_deleted:
- {
- const char *moved_to_abspath;
-
- SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
- NULL, NULL, eb->db,
- local_abspath,
- scratch_pool, scratch_pool));
- if (moved_to_abspath)
- reason = svn_wc_conflict_reason_moved_away;
- else
- reason = svn_wc_conflict_reason_deleted;
- }
- break;
-
- case svn_wc__db_status_incomplete:
- /* We used svn_wc__db_read_info(), so 'incomplete' means
- * - there is no node in the WORKING tree
- * - a BASE node is known to exist
- * So the node exists and is essentially 'normal'. We still need to
- * check prop and text mods, and those checks will retrieve the
- * missing information (hopefully). */
- case svn_wc__db_status_normal:
- if (action == svn_wc_conflict_action_edit)
- /* An edit onto a local edit or onto *no* local changes is no
- * tree-conflict. (It's possibly a text- or prop-conflict,
- * but we don't handle those here.) */
- return SVN_NO_ERROR;
-
- /* Replace is handled as delete and then specifically in
- add_directory() and add_file(), so we only expect deletes here */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
-
- /* Check if the update wants to delete or replace a locally
- * modified node. */
-
-
- /* Do a deep tree detection of local changes. The update editor will
- * not visit the subdirectories of a directory that it wants to delete.
- * Therefore, we need to start a separate crawl here. */
-
- SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes, FALSE,
- eb->db, local_abspath,
- eb->cancel_func, eb->cancel_baton,
- scratch_pool));
-
- if (modified)
- {
- if (all_mods_are_deletes)
- reason = svn_wc_conflict_reason_deleted;
- else
- reason = svn_wc_conflict_reason_edited;
- }
- break;
-
- case svn_wc__db_status_server_excluded:
- /* Not allowed to view the node. Not allowed to report tree
- * conflicts. */
- case svn_wc__db_status_excluded:
- /* Locally marked as excluded. No conflicts wanted. */
- case svn_wc__db_status_not_present:
- /* A committed delete (but parent not updated). The delete is
- committed, so no conflict possible during update. */
- return SVN_NO_ERROR;
-
- case svn_wc__db_status_base_deleted:
- /* An internal status. Should never show up here. */
- SVN_ERR_MALFUNCTION();
- break;
-
- }
-
- if (reason == SVN_WC_CONFLICT_REASON_NONE)
- /* No conflict with the current action. */
- return SVN_NO_ERROR;
-
-
- /* Sanity checks. Note that if there was no action on the node, this function
- * would not have been called in the first place.*/
- if (reason == svn_wc_conflict_reason_edited
- || reason == svn_wc_conflict_reason_deleted
- || reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced)
- /* When the node existed before (it was locally deleted, replaced or
- * edited), then 'update' cannot add it "again". So it can only send
- * _action_edit, _delete or _replace. */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_edit
- || action == svn_wc_conflict_action_delete
- || action == svn_wc_conflict_action_replace);
- else if (reason == svn_wc_conflict_reason_added ||
- reason == svn_wc_conflict_reason_moved_here)
- /* When the node did not exist before (it was locally added), then 'update'
- * cannot want to modify it in any way. It can only send _action_add. */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
-
-
- /* A conflict was detected. Create a conflict skel to record it. */
- *pconflict = svn_wc__conflict_skel_create(result_pool);
-
- SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
- eb->db, local_abspath,
- reason,
- action,
- result_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-
-/* If LOCAL_ABSPATH is inside a conflicted tree, set *CONFLICTED to TRUE,
- * Otherwise set *CONFLICTED to FALSE. Use SCRATCH_POOL for temporary
- * allocations.
- */
-static svn_error_t *
-already_in_a_tree_conflict(svn_boolean_t *conflicted,
- svn_wc__db_t *db,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- const char *ancestor_abspath = local_abspath;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
- *conflicted = FALSE;
-
- while (TRUE)
- {
- svn_boolean_t is_wc_root, tree_conflicted;
-
- svn_pool_clear(iterpool);
-
- SVN_ERR(svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted,
- db, ancestor_abspath, iterpool));
-
- if (tree_conflicted)
- {
- *conflicted = TRUE;
- break;
- }
-
- SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
- iterpool));
-
- if (is_wc_root)
- break;
-
- ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-/* Temporary helper until the new conflict handling is in place */
-static svn_error_t *
-node_already_conflicted(svn_boolean_t *conflicted,
- svn_wc__db_t *db,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
-
- SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted,
- &prop_conflicted,
- &tree_conflicted,
- db, local_abspath,
- scratch_pool));
-
- *conflicted = (text_conflicted || prop_conflicted || tree_conflicted);
- return SVN_NO_ERROR;
-}
-
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-delete_entry(const char *path,
- svn_revnum_t revision,
- void *parent_baton,
- apr_pool_t *pool)
-{
- struct dir_baton *pb = parent_baton;
- struct edit_baton *eb = pb->edit_baton;
- const char *base = svn_relpath_basename(path, NULL);
- const char *local_abspath;
- const char *repos_relpath;
- svn_kind_t kind, base_kind;
- svn_revnum_t old_revision;
- svn_boolean_t conflicted;
- svn_boolean_t have_work;
- svn_skel_t *tree_conflict = NULL;
- svn_wc__db_status_t status;
- svn_wc__db_status_t base_status;
- apr_pool_t *scratch_pool;
- svn_boolean_t deleting_target;
- svn_boolean_t keep_as_working = FALSE;
-
- if (pb->skip_this)
- return SVN_NO_ERROR;
-
- scratch_pool = svn_pool_create(pb->pool);
-
- SVN_ERR(mark_directory_edited(pb, scratch_pool));
-
- SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
- scratch_pool));
-
- deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
-
- /* Detect obstructing working copies */
- {
- svn_boolean_t is_root;
-
- SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
- scratch_pool));
-
- if (is_root)
- {
- /* Just skip this node; a future update will handle it */
- SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
- do_notification(eb, local_abspath, svn_node_unknown,
- svn_wc_notify_update_skip_obstruction, scratch_pool);
-
- svn_pool_destroy(scratch_pool);
-
- return SVN_NO_ERROR;
- }
- }
-
- SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- &conflicted, NULL, NULL, NULL,
- NULL, NULL, &have_work,
- eb->db, local_abspath,
- scratch_pool, scratch_pool));
-
- if (!have_work)
- {
- base_status = status;
- base_kind = kind;
- }
- else
- SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
- &repos_relpath,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- eb->db, local_abspath,
- scratch_pool, scratch_pool));
-
- /* Is this path a conflict victim? */
- if (conflicted)
- SVN_ERR(node_already_conflicted(&conflicted, eb->db, local_abspath,
- scratch_pool));
- if (conflicted)
- {
- SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
-
- do_notification(eb, local_abspath, svn_node_unknown,
- svn_wc_notify_skip_conflicted,
- scratch_pool);
-
- svn_pool_destroy(scratch_pool);
-
- return SVN_NO_ERROR;
- }
-
-
-
- /* Receive the remote removal of excluded/server-excluded/not present node.
- Do not notify, but perform the change even when the node is shadowed */
- if (base_status == svn_wc__db_status_not_present
- || base_status == svn_wc__db_status_excluded
- || base_status == svn_wc__db_status_server_excluded)
- {
- SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- FALSE, SVN_INVALID_REVNUM,
- NULL, NULL,
- scratch_pool));
-
- if (deleting_target)
- eb->target_deleted = TRUE;
-
- svn_pool_destroy(scratch_pool);
-
- return SVN_NO_ERROR;
- }
-
- /* Is this path the victim of a newly-discovered tree conflict? If so,
- * remember it and notify the client. Then (if it was existing and
- * modified), re-schedule the node to be added back again, as a (modified)
- * copy of the previous base version. */
-
- /* Check for conflicts only when we haven't already recorded
- * a tree-conflict on a parent node. */
- if (!pb->shadowed)
- {
- SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
- status, kind, TRUE,
- svn_wc_conflict_action_delete,
- pb->pool, scratch_pool));
- }
-
- if (tree_conflict != NULL)
- {
- svn_wc_conflict_reason_t reason;
- /* When we raise a tree conflict on a node, we don't want to mark the
- * node as skipped, to allow a replacement to continue doing at least
- * a bit of its work (possibly adding a not present node, for the
- * next update) */
- if (!pb->deletion_conflicts)
- pb->deletion_conflicts = apr_hash_make(pb->pool);
-
- apr_hash_set(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
- APR_HASH_KEY_STRING, tree_conflict);
-
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
- eb->db, local_abspath,
- tree_conflict,
- scratch_pool, scratch_pool));
-
- if (reason == svn_wc_conflict_reason_edited)
- {
- /* The item exists locally and has some sort of local mod.
- * It no longer exists in the repository at its target URL@REV.
- *
- * To prepare the "accept mine" resolution for the tree conflict,
- * we must schedule the existing content for re-addition as a copy
- * of what it was, but with its local modifications preserved. */
- keep_as_working = TRUE;
-
- /* Fall through to remove the BASE_NODEs properly, with potentially
- keeping a not-present marker */
- }
- else if (reason == svn_wc_conflict_reason_deleted
- || reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced)
- {
- /* The item does not exist locally because it was already shadowed.
- * We must complete the deletion, leaving the tree conflict info
- * as the only difference from a normal deletion. */
-
- /* Fall through to the normal "delete" code path. */
- }
- else
- SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
- }
-
- SVN_ERR(complete_conflict(tree_conflict, pb, local_abspath, repos_relpath,
- old_revision,
- (kind == svn_kind_dir)
- ? svn_node_dir
- : svn_node_file,
- pb->pool, scratch_pool));
-
- /* Issue a wq operation to delete the BASE_NODE data and to delete actual
- nodes based on that from disk, but leave any WORKING_NODEs on disk.
-
- Local modifications are already turned into copies at this point.
-
- If the thing being deleted is the *target* of this update, then
- we need to recreate a 'deleted' entry, so that the parent can give
- accurate reports about itself in the future. */
- if (! deleting_target)
- {
- /* Delete, and do not leave a not-present node. */
- SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- keep_as_working, SVN_INVALID_REVNUM,
- tree_conflict, NULL,
- scratch_pool));
- }
- else
- {
- /* Delete, leaving a not-present node. */
- SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
- keep_as_working, *eb->target_revision,
- tree_conflict, NULL,
- scratch_pool));
- eb->target_deleted = TRUE;
- }
-
- SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
- eb->cancel_func, eb->cancel_baton,
- scratch_pool));
-
- /* Notify. */
- if (tree_conflict)
- do_notification(eb, local_abspath, svn_node_unknown,
- svn_wc_notify_tree_conflict, scratch_pool);
- else
- {
- svn_wc_notify_action_t action = svn_wc_notify_update_delete;
- svn_node_kind_t node_kind;
-
- if (pb->shadowed)
- action = svn_wc_notify_update_shadowed_delete;
-
- if (kind == svn_kind_dir)
- node_kind = svn_node_dir;
- else
- node_kind = svn_node_file;
-
- do_notification(eb, local_abspath, node_kind, action, scratch_pool);
- }
-
- svn_pool_destroy(scratch_pool);
-
- return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-add_directory(const char *path,
- void *parent_baton,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_rev,
- apr_pool_t *pool,
- void **child_baton)
-{
- struct dir_baton *pb = parent_baton;
- struct edit_baton *eb = pb->edit_baton;
- struct dir_baton *db;
- svn_node_kind_t kind;
- svn_wc__db_status_t status;
- svn_kind_t wc_kind;
- svn_boolean_t conflicted;
- svn_boolean_t versioned_locally_and_present;
- svn_skel_t *tree_conflict = NULL;
- svn_error_t *err;
-
- SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
-
- SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
- *child_baton = db;
-
- if (db->skip_this)
- return SVN_NO_ERROR;
-
- SVN_ERR(mark_directory_edited(db, pool));
-
- if (strcmp(eb->target_abspath, db->local_abspath) == 0)
- {
- /* The target of the edit is being added, give it the requested
- depth of the edit (but convert svn_depth_unknown to
- svn_depth_infinity). */
- db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
- ? svn_depth_infinity : eb->requested_depth;
- }
- else if (eb->requested_depth == svn_depth_immediates
- || (eb->requested_depth == svn_depth_unknown
- && pb->ambient_depth == svn_depth_immediates))
- {
- db->ambient_depth = svn_depth_empty;
- }
- else
- {
- db->ambient_depth = svn_depth_infinity;
- }
-
- /* It may not be named the same as the administrative directory. */
- if (svn_wc_is_adm_dir(db->name, pool))
- return svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Failed to add directory '%s': object of the same name as the "
- "administrative directory"),
- svn_dirent_local_style(db->local_abspath, pool));
-
- SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
-
- err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, db->local_abspath, db->pool, db->pool);
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- wc_kind = svn_kind_unknown;
- status = svn_wc__db_status_normal;
- conflicted = FALSE;
-
- versioned_locally_and_present = FALSE;
- }
- else if (wc_kind == svn_kind_dir
- && status == svn_wc__db_status_normal)
- {
- /* !! We found the root of a separate working copy obstructing the wc !!
-
- If the directory would be part of our own working copy then
- we wouldn't have been called as an add_directory().
-
- The only thing we can do is add a not-present node, to allow
- a future update to bring in the new files when the problem is
- resolved. Note that svn_wc__db_base_add_not_present_node()
- explicitly adds the node into the parent's node database. */
-
- SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
- db->new_relpath,
- eb->repos_root,
- eb->repos_uuid,
- *eb->target_revision,
- svn_kind_file,
- NULL, NULL,
- pool));
-
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_update_skip_obstruction, pool);
-
- return SVN_NO_ERROR;
- }
- else if (status == svn_wc__db_status_normal
- && (wc_kind == svn_kind_file
- || wc_kind == svn_kind_symlink))
- {
- /* We found a file external occupating the place we need in BASE.
-
- We can't add a not-present node in this case as that would overwrite
- the file external. Luckily the file external itself stops us from
- forgetting a child of this parent directory like an obstructing
- working copy would.
-
- The reason we get here is that the adm crawler doesn't report
- file externals.
- */
-
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_file,
- svn_wc_notify_update_skip_obstruction, pool);
-
- return SVN_NO_ERROR;
- }
- else if (wc_kind == svn_kind_unknown)
- versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
- else
- versioned_locally_and_present = IS_NODE_PRESENT(status);
-
- /* Is this path a conflict victim? */
- if (conflicted)
- {
- if (pb->deletion_conflicts)
- tree_conflict = apr_hash_get(pb->deletion_conflicts, db->name,
- APR_HASH_KEY_STRING);
-
- if (tree_conflict)
- {
- svn_wc_conflict_reason_t reason;
- /* So this deletion wasn't just a deletion, it is actually a
- replacement. Let's install a better tree conflict. */
-
- /* ### Should store the conflict in DB to allow reinstalling
- ### with theoretically more data in close_directory() */
-
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
- eb->db,
- db->local_abspath,
- tree_conflict,
- db->pool, db->pool));
-
- tree_conflict = svn_wc__conflict_skel_create(db->pool);
-
- SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
- tree_conflict,
- eb->db, db->local_abspath,
- reason, svn_wc_conflict_action_replace,
- db->pool, db->pool));
-
- /* And now stop checking for conflicts here and just perform
- a shadowed update */
- db->edit_conflict = tree_conflict; /* Cache for close_directory */
- tree_conflict = NULL; /* No direct notification */
- db->shadowed = TRUE; /* Just continue */
- conflicted = FALSE; /* No skip */
- }
- else
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- db->local_abspath, pool));
- }
-
- /* Now the "usual" behaviour if already conflicted. Skip it. */
- if (conflicted)
- {
- /* Record this conflict so that its descendants are skipped silently. */
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
-
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- /* We skip this node, but once the update completes the parent node will
- be updated to the new revision. So a future recursive update of the
- parent will not bring in this new node as the revision of the parent
- describes to the repository that all children are available.
-
- To resolve this problem, we add a not-present node to allow bringing
- the node in once this conflict is resolved.
-
- Note that we can safely assume that no present base node exists,
- because then we would not have received an add_directory.
- */
- SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
- db->new_relpath,
- eb->repos_root,
- eb->repos_uuid,
- *eb->target_revision,
- svn_kind_dir,
- NULL, NULL,
- pool));
-
- /* ### TODO: Also print victim_path in the skip msg. */
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_skip_conflicted, pool);
- return SVN_NO_ERROR;
- }
-
- if (db->shadowed)
- {
- /* Nothing to check; does not and will not exist in working copy */
- }
- else if (versioned_locally_and_present)
- {
- /* What to do with a versioned or schedule-add dir:
-
- A dir already added without history is OK. Set add_existed
- so that user notification is delayed until after any prop
- conflicts have been found.
-
- An existing versioned dir is an error. In the future we may
- relax this restriction and simply update such dirs.
-
- A dir added with history is a tree conflict. */
-
- svn_boolean_t local_is_non_dir;
- svn_wc__db_status_t add_status = svn_wc__db_status_normal;
-
- /* Is the local add a copy? */
- if (status == svn_wc__db_status_added)
- SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, db->local_abspath,
- pool, pool));
-
-
- /* Is there *something* that is not a dir? */
- local_is_non_dir = (wc_kind != svn_kind_dir
- && status != svn_wc__db_status_deleted);
-
- /* Do tree conflict checking if
- * - if there is a local copy.
- * - if this is a switch operation
- * - the node kinds mismatch
- *
- * During switch, local adds at the same path as incoming adds get
- * "lost" in that switching back to the original will no longer have the
- * local add. So switch always alerts the user with a tree conflict. */
- if (!eb->adds_as_modification
- || local_is_non_dir
- || add_status != svn_wc__db_status_added)
- {
- SVN_ERR(check_tree_conflict(&tree_conflict, eb,
- db->local_abspath,
- status, wc_kind, FALSE,
- svn_wc_conflict_action_add,
- pool, pool));
- }
-
- if (tree_conflict == NULL)
- db->add_existed = TRUE; /* Take over WORKING */
- else
- db->shadowed = TRUE; /* Only update BASE */
- }
- else if (kind != svn_node_none)
- {
- /* There's an unversioned node at this path. */
- db->obstruction_found = TRUE;
-
- /* Unversioned, obstructing dirs are handled by prop merge/conflict,
- * if unversioned obstructions are allowed. */
- if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
- {
- /* Bring in the node as deleted */ /* ### Obstructed Conflict */
- db->shadowed = TRUE;
-
- /* Mark a conflict */
- tree_conflict = svn_wc__conflict_skel_create(db->pool);
-
- SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
- tree_conflict,
- eb->db, db->local_abspath,
- svn_wc_conflict_reason_unversioned,
- svn_wc_conflict_action_add,
- db->pool, pool));
- db->edit_conflict = tree_conflict;
- }
- }
-
- SVN_ERR(complete_conflict(tree_conflict, pb, db->local_abspath,
- db->old_repos_relpath, db->old_revision,
- svn_node_dir, db->pool, pool));
-
- SVN_ERR(svn_wc__db_base_add_incomplete_directory(
- eb->db, db->local_abspath,
- db->new_relpath,
- eb->repos_root,
- eb->repos_uuid,
- *eb->target_revision,
- db->ambient_depth,
- (db->shadowed && db->obstruction_found),
- (! db->shadowed
- && status == svn_wc__db_status_added),
- tree_conflict, NULL,
- pool));
-
- /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
- updating the DB */
- if (!db->shadowed)
- SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
-
- if (tree_conflict != NULL)
- {
- db->already_notified = TRUE;
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_tree_conflict, pool);
- }
-
-
- /* If this add was obstructed by dir scheduled for addition without
- history let close_file() handle the notification because there
- might be properties to deal with. If PATH was added inside a locally
- deleted tree, then suppress notification, a tree conflict was already
- issued. */
- if (eb->notify_func && !db->already_notified && !db->add_existed)
- {
- svn_wc_notify_action_t action;
-
- if (db->shadowed)
- action = svn_wc_notify_update_shadowed_add;
- else if (db->obstruction_found)
- action = svn_wc_notify_exists;
- else
- action = svn_wc_notify_update_add;
-
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-open_directory(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *pool,
- void **child_baton)
-{
- struct dir_baton *db, *pb = parent_baton;
- struct edit_baton *eb = pb->edit_baton;
- svn_boolean_t have_work;
- svn_boolean_t conflicted;
- svn_skel_t *tree_conflict = NULL;
- svn_wc__db_status_t status, base_status;
- svn_kind_t wc_kind;
-
- SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
- *child_baton = db;
-
- if (db->skip_this)
- return SVN_NO_ERROR;
-
- /* Detect obstructing working copies */
- {
- svn_boolean_t is_root;
-
- SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
- pool));
-
- if (is_root)
- {
- /* Just skip this node; a future update will handle it */
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_update_skip_obstruction, pool);
-
- return SVN_NO_ERROR;
- }
- }
-
- /* We should have a write lock on every directory touched. */
- SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
-
- SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
- &db->old_repos_relpath, NULL, NULL,
- &db->changed_rev, &db->changed_date,
- &db->changed_author, &db->ambient_depth,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- &conflicted, NULL, NULL, NULL,
- NULL, NULL, &have_work,
- eb->db, db->local_abspath,
- db->pool, pool));
-
- if (!have_work)
- base_status = status;
- else
- SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
- &db->old_repos_relpath, NULL, NULL,
- &db->changed_rev, &db->changed_date,
- &db->changed_author, &db->ambient_depth,
- NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, db->local_abspath,
- db->pool, pool));
-
- db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
-
- /* Is this path a conflict victim? */
- if (conflicted)
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- db->local_abspath, pool));
- if (conflicted)
- {
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
-
- db->skip_this = TRUE;
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_unknown,
- svn_wc_notify_skip_conflicted, pool);
-
- return SVN_NO_ERROR;
- }
-
- /* Is this path a fresh tree conflict victim? If so, skip the tree
- with one notification. */
-
- /* Check for conflicts only when we haven't already recorded
- * a tree-conflict on a parent node. */
- if (!db->shadowed)
- SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
- status, wc_kind, TRUE,
- svn_wc_conflict_action_edit,
- db->pool, pool));
-
- /* Remember the roots of any locally deleted trees. */
- if (tree_conflict != NULL)
- {
- svn_wc_conflict_reason_t reason;
- db->edit_conflict = tree_conflict;
- /* Other modifications wouldn't be a tree conflict */
-
- SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
- eb->db, db->local_abspath,
- tree_conflict,
- db->pool, db->pool));
- SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
- || reason == svn_wc_conflict_reason_moved_away
- || reason == svn_wc_conflict_reason_replaced);
-
- /* Continue updating BASE */
- db->shadowed = TRUE;
- }
-
- /* Mark directory as being at target_revision and URL, but incomplete. */
- SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
- db->new_relpath,
- *eb->target_revision,
- pool));
-
- return SVN_NO_ERROR;
-}
-
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-change_dir_prop(void *dir_baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- svn_prop_t *propchange;
- struct dir_baton *db = dir_baton;
-
- if (db->skip_this)
- return SVN_NO_ERROR;
-
- propchange = apr_array_push(db->propchanges);
- propchange->name = apr_pstrdup(db->pool, name);
- propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
-
- if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
- SVN_ERR(mark_directory_edited(db, pool));
-
- return SVN_NO_ERROR;
-}
-
-/* If any of the svn_prop_t objects in PROPCHANGES represents a change
- to the SVN_PROP_EXTERNALS property, return that change, else return
- null. If PROPCHANGES contains more than one such change, return
- the first. */
-static const svn_prop_t *
-externals_prop_changed(const apr_array_header_t *propchanges)
-{
- int i;
-
- for (i = 0; i < propchanges->nelts; i++)
- {
- const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
- if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
- return p;
- }
-
- return NULL;
-}
-
-
-
-/* An svn_delta_editor_t function. */
-static svn_error_t *
-close_directory(void *dir_baton,
- apr_pool_t *pool)
-{
- struct dir_baton *db = dir_baton;
- struct edit_baton *eb = db->edit_baton;
- svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
- apr_array_header_t *entry_prop_changes;
- apr_array_header_t *dav_prop_changes;
- apr_array_header_t *regular_prop_changes;
- apr_hash_t *base_props;
- apr_hash_t *actual_props;
- apr_hash_t *new_base_props = NULL;
- apr_hash_t *new_actual_props = NULL;
- svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
- apr_time_t new_changed_date = 0;
- const char *new_changed_author = NULL;
- apr_pool_t *scratch_pool = db->pool;
- svn_skel_t *all_work_items = NULL;
- svn_skel_t *conflict_skel = NULL;
-
- /* Skip if we're in a conflicted tree. */
- if (db->skip_this)
- {
- /* Allow the parent to complete its update. */
- SVN_ERR(maybe_release_dir_info(db->bump_info));
-
- return SVN_NO_ERROR;
- }
-
- if (db->edited)
- conflict_skel = db->edit_conflict;
-
- SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
- &dav_prop_changes, ®ular_prop_changes, pool));
-
- /* Fetch the existing properties. */
- if ((!db->adding_dir || db->add_existed)
- && !db->shadowed)
- {
- SVN_ERR(svn_wc__get_actual_props(&actual_props,
- eb->db, db->local_abspath,
- scratch_pool, scratch_pool));
- }
- else
- actual_props = apr_hash_make(pool);
-
- if (db->add_existed)
- {
- /* This node already exists. Grab the current pristine properties. */
- SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
- eb->db, db->local_abspath,
- scratch_pool, scratch_pool));
- }
- else if (!db->adding_dir)
- {
- /* Get the BASE properties for proper merging. */
- SVN_ERR(svn_wc__db_base_get_props(&base_props,
- eb->db, db->local_abspath,
- scratch_pool, scratch_pool));
- }
- else
- base_props = apr_hash_make(pool);
-
- /* An incomplete directory might have props which were supposed to be
- deleted but weren't. Because the server sent us all the props we're
- supposed to have, any previous base props not in this list must be
- deleted (issue #1672). */
- if (db->was_incomplete)
- {
- int i;
- apr_hash_t *props_to_delete;
- apr_hash_index_t *hi;
-
- /* In a copy of the BASE props, remove every property that we see an
- incoming change for. The remaining unmentioned properties are those
- which need to be deleted. */
- props_to_delete = apr_hash_copy(pool, base_props);
- for (i = 0; i < regular_prop_changes->nelts; i++)
- {
- const svn_prop_t *prop;
- prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
- apr_hash_set(props_to_delete, prop->name,
- APR_HASH_KEY_STRING, NULL);
- }
-
- /* Add these props to the incoming propchanges (in
- * regular_prop_changes). */
- for (hi = apr_hash_first(pool, props_to_delete);
- hi != NULL;
- hi = apr_hash_next(hi))
- {
- const char *propname = svn__apr_hash_index_key(hi);
- svn_prop_t *prop = apr_array_push(regular_prop_changes);
-
- /* Record a deletion for PROPNAME. */
- prop->name = propname;
- prop->value = NULL;
- }
- }
-
- /* If this directory has property changes stored up, now is the time
- to deal with them. */
- if (regular_prop_changes->nelts)
- {
- /* If recording traversal info, then see if the
- SVN_PROP_EXTERNALS property on this directory changed,
- and record before and after for the change. */
- if (eb->external_func)
- {
- const svn_prop_t *change
- = externals_prop_changed(regular_prop_changes);
-
[... 2736 lines stripped ...]