[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

simple tree conflict detection

From: Stephen Butler <sbutler_at_elego.de>
Date: 2007-11-12 21:00:38 CET

Hi folks,

we understand that most of you are busy getting 1.5 ready for prime
time, and that right now is not the time for discussing new features
for Subversion.

Folks at CollabNet have asked us to continue investigating tree
conflicts anyway due to customer interest in the subject.

We hope that at least a few of you can afford the time to take a look
at this design proposal. It does not try to provide an ideal solution
to the use cases we submitted earlier. We just want a mechanism to
protect users from the most common errors.

We tried to keep it short!

Steve Butler
Stefan Sperling

                            -*- text -*-


Issue reference: http://subversion.tigris.org/issues/show_bug.cgi?id=2282
See also subversion/notes/tree-conflicts/use-cases.txt.

To cut down the complexity of this task, we split the problem of tree
conflict handling into three parts.

1. Signaling
    Detect and describe the tree conflict to the user.
    This is use case specific.

2. Persistence
    Disallow commit in the presence of a tree conflict.

3. Automatic fixes
    Rescue the modifications that are most likely to get lost.
    This is use case specific.

For now we consider only parts 1 and 2.

To signal a tree conflict during an update or merge, we write a human-
readable description of the tree conflict to a user-visible file
inside the parent directory in the working copy. The name of this
file is stored in the conflict_wrk slot in the svn_wc_entry_t
structure, as suggested by C. Michael Pilato:

   IIRC, directories currently only use the property-conflict-file entry
   slot -- maybe the working-conflict-file slot could be used for
   directories in some fashion?

To ensure that the warning persists, svn_wc_conflicted_p() in
libsvn_wc/questions.c will halt with an SVN_ERR_WC_FOUND_CONFLICT
error if a new function called svn_wc__is_tree_conflicted() encounters
a working-conflict-file attached to a directory.

A directory may contain more than one conflict. Each conflict is
identifiable by the path in the working copy that was deleted or
modified, which we call the "victim" of the tree conflict.

Running 'svn resolved' on the victim will cause the working-conflict-file
to be refreshed, removing the victim's conflict description.

To facilitate merging into the 1.5 branch, a requirement desired by
CollabNet, we avoid changing public 1.5 API at the expense of design
purity. We modify only internals of libsvn_wc, we don't change its API.

We avoid assuming rename tracking in any form. Use case 5, described in
the paper attached to issue #2282, requires renames to be detected
properly, which is impossible given how Subversion currently handles
'svn move' internally.

Here are the data structures we propose for our design.
We plan on adding a new header file called libsvn_wc/treeconflicts.h
to declare them, and a corresponding libsvn_wc/treeconflicts.c
file to implement the functions.

   /* Operations that can cause a tree conflict. */
   typedef enum svn_wc__tree_conflict_cause_t
     svn_wc__tree_conflict_cause_update = 0,
   } svn_wc__tree_conflict_cause_t;

   /* This could be merged with svn_wc_conflict_description_t in 1.6. */
   typedef struct svn_wc__tree_conflict_description_t
     /* The path that is being operated on and its node type.
      * For tree conflicts, PATH is always a directory. */
     const char *path;
     svn_node_kind_t node_kind;

     /* What operation caused this conflict? */
     svn_wc__tree_conflict_cause_t cause;

     /* The path that is the subject of the tree conflict.
      * It may be a file that has been edited in the working
      * copy and deleted in the repository, for example. */
     const char *victim_path;

     /* If not NULL, an open working copy access baton to either the
      * path itself (if PATH is a directory), or to the parent
      * directory (if PATH is a file.) */
     svn_wc_adm_access_t *access;

     /* The action being attempted on the victim by the update or merge. */
     svn_wc_conflict_action_t their_action;

     /* What state in the working copy is causing the conflict? */
     svn_wc_conflict_reason_t reason;

   } svn_wc__tree_conflict_description_t;

   /* Mark the directory at DIR_ENTRY as tree conflicted.
    * The caller has to pass a description of the tree conflict that
    * occurred in CONFLICT. Do all allocations in POOL. */
   svn_wc__mark_tree_conflicted(svn_wc_entry_t dir,
                                svn_wc__tree_conflict_description_t *conflict,
                                apr_pool_t *pool);

   /* Set TREE_CONFLICTED to TRUE if the directory DIR_PATH has
    * been marked as tree conflicted.
    * Do all allocations in POOL. */
   svn_wc__is_tree_conflicted(const char *dir_path,
                              svn_boolean_t *tree_conflicted.
                              apr_pool_t *pool);

   /* Return the name of the file containing tree conflict descriptions
    * for all tree conflicts marked in DIR_PATH. */
   const char*
   svn_wc__get_tree_conflict_descs_path(const char *dir_path,
                                        apr_pool_t *pool);

   /* Mark the tree conflict rooted at directory @a dir_entry
    * with respect to the file specified by @a victim_path as resolved.
    * Do all allocations in POOL. */
   svn_wc__tree_conflict_resolved(svn_wc_entry_t *dir_entry,
                                  const char* victim_path,
                                  apr_pool_t *pool);

Here are the strings which we want to use to create human readable
tree conflict descriptions in svn_wc__mark_tree_conflicted():

   struct svn_wc__tree_conflict_human_readable_phrases
     const char *update_delete;
     const char *update_edit;
     const char *merge_delete;
     const char *merge_edit;
     const char *we_deleted;
     const char *we_edited;
     const char *does_not_exist;

   static struct svn_wc__tree_conflict_human_readable_phrases phrases = {
     "The update wants to delete the file '%s'\n"
         "(possibly as part of a rename operation)"\n,
     "The update wants to edit the file '%s'\n",
     "The merge wants to delete the file '%s'\n"
         "(possibly as part of a rename operation)"\n,
     "The merge wants to edit the file '%s'\n",
     "but you have deleted it locally. Maybe you renamed it?\n",
     "but you have edited it locally.\n",
     "but it does not exist locally. Maybe you renamed it?\n"

Currently we plan to detect use cases 1 to 3 at the following points
in the code:

   libsvn_wc/update_editor.c, open_file():

     If the file is scheduled for deletion, we have a tree conflict.
     This is use case 1 described in the paper attached to issue #2282

   libsvn_wc/update_editor.c, do_entry_deletion():

     If we are about to delete a path that has local mods,
     mark the containing directory as tree conflicted.
     This is tree conflict use case 2 as described in the
     paper attached to issue #2282.

     If we are about to delete a path that has been scheduled
     for deletion, mark the containing directory as tree conflicted.
     This _could_ be tree conflict use case 3 as described in the
     paper attached to issue #2282. Proper detection would require
     true renames.

Stephen Butler | Software Developer
elego Software Solutions GmbH
Gustav-Meyer-Allee 25 | 13355 Berlin | Germany
fon: +49 30 2345 8696 | mobile: +49 163 25 45 015
fax: +49 30 2345 8695 | http://www.elegosoft.com
Geschäftsführer: Olaf Wagner | Sitz der Gesellschaft: Berlin
Amtsgericht Charlottenburg HRB 77719 | USt-IdNr: DE163214194
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Mon Nov 12 21:00:58 2007

This is an archived mail posted to the Subversion Dev mailing list.