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

tree conflict on tc_url_rev branch?

From: Stefan Sperling <stsp_at_elego.de>
Date: Sun, 23 Nov 2008 17:30:50 +0000

Hi,

in r34288, I added this file on trunk:

[svn-trunk] $ svn log notes/tree-conflicts/use-cases-resolution.txt
------------------------------------------------------------------------
r34288 | stsp | 2008-11-20 18:26:21 +0000 (Thu, 20 Nov 2008) | 11 lines

Add notes about resolving tree conflict use cases 1 to 6.

This file does not describe any design requirements.
It just shows how one may go about resolving particular
tree conflicts using what we currently have in trunk.

Maybe it will provide inspiration, or come in handy as
a reference, for someone, somewhere, some day.

* notes/tree-conflicts/use-cases-resolution.txt: New file.

------------------------------------------------------------------------

When I run this to show the diff between trunk, as of the last
sync-up commit made by sbutler in r34356, and the branch:

svn diff -x -p https://svn.collab.net/repos/svn/trunk@34356 \
               https://svn.collab.net/repos/svn/branches/tc_url_rev

the file is considered deleted on the branch (see diff output pasted below).

In my tc_url_rev working copy, the file still exists, but is unversioned:

[svn-tc_url_rev] $ svn status notes/tree-conflicts/
? notes/tree-conflicts/use-cases-resolution.txt
[svn-tc_url_rev] $

No changes have been made in notes/tree-conflicts on the branch
since the branch was first created, and the file (which was created
after the branch) was never added to the branch.

[svn-tc_url_rev] $ svn log --stop-on-copy notes/tree-conflicts/
------------------------------------------------------------------------
r34277 | neels | 2008-11-20 04:41:56 +0000 (Thu, 20 Nov 2008) | 5 lines

New lightweight branch tc_url_rev: Issue #3322: store URL_at_REV of older+theirs.
Tree-conflicts should show URL_at_REV info (and more) on victims for both sides
of a conflict.

------------------------------------------------------------------------
[svn-tc_url_rev] $

For some reason, the file was not added in the sync-up commit
I made in r34325. I don't really understand why this happened.

Now, on to the exercise: Suggestions on how to resolve, anyone? :)

Stefan

Index: notes/tree-conflicts/use-cases-resolution.txt
===================================================================
--- notes/tree-conflicts/use-cases-resolution.txt (.../trunk) (revision 34356)
+++ notes/tree-conflicts/use-cases-resolution.txt (.../branches/tc_url_rev) (revision 34365)
@@ -1,459 +0,0 @@
-### NOTE:
-### This file covers the state of Subversion trunk as of about r34285.
-### It may not be 100% applicable to future versions if not updated.
-
-Resolution recipes for tree conflict use cases 1 to 6
-(see use-cases.txt and detection.txt), for files and directories.
-
-In practice, users will likely run into more complex scenarios
-than presented here (e.g. moved files and directories are not
-considered below). However, the examples below show that
-all these use cases are principally resolvable.
-
-=== Use case 1, files
-
-Conflict reported:
-
-$ svn status
-D C alpha
-$ svn info alpha
-[...]
-Tree conflict:
- The update attempted to edit 'alpha'.
- You have deleted 'alpha' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) restore alpha, apply modification
- 2) leave alpha deleted
-
---- Resolution 1)
-$ svn revert alpha
-Reverted 'alpha'
-### revert has cleared tree-conflicted status:
-$ svn status alpha
-### alpha is now at r2 again:
-$ svn info alpha | grep Revision
-Revision: 2
-$ cat alpha
-alpha
-$ svn diff -c3 ^/trunk/alpha
-Index: alpha
-===================================================================
---- alpha (revision 2)
-+++ alpha (revision 3)
-@@ -1 +1 @@
--alpha
-+alpha, modified
-$ svn update
-U alpha
-Updated to revision 3.
-$ cat alpha
-alpha, modified
-
---- Resolution 2)
-$ svn status alpha
-D C alpha
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status alpha
-D alpha
-
-
-=== Use case 1, directories
-
-Conflict reported:
-
-$ svn status
-D C gamma
-D gamma/delta
-$ svn info gamma
-[...]
-Tree conflict:
- The update attempted to edit 'gamma'.
- You have deleted 'gamma' locally.
- Maybe you renamed it?
-
-### In this case, the modification made to the directory gamma
-### was a modification to the file gamma/delta. This is why
-### resolution recipes below sometimes operate on gamma/delta.
-
-Possible resolutions:
- 1) restore gamma, apply modification
- 2) leave gamma deleted
-
---- Resolution 1)
-$ svn revert gamma/delta
-Reverted 'gamma/delta'
-$ svn status
-$ cat gamma/delta
-delta
-$ svn info gamma/delta | grep Revision
-Revision: 2
-$ svn update
-U gamma/delta
-Updated to revision 3.
-
--- Resolution 2)
-$ svn status
-D C gamma
-D gamma/delta
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
-D gamma
-D gamma/delta
-
-=== Use case 2, files
-
-Conflict reported:
-$ svn status
-M C alpha
-$ svn info alpha
-[...]
-Tree conflict:
- The update attempted to delete 'alpha',
- or attempted to rename it.
- You have edited 'alpha' locally.
-
-Possible resolutions:
- 1) Leave alpha in place, with local modifications.
- 2) Remove alpha including local modifications.
-
--- Resolution 1)
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status
-M alpha
-
--- Resolution 2)
-$ svn remove --force alpha
-D alpha
-$ svn status alpha
-D C alpha
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status alpha
-D alpha
-
-
-=== Use case 2, directories
-
-Conflict reported:
-$ svn status
- C gamma
-A gamma/iota
-$ svn info gamma
-[...]
-Tree conflict:
- The update attempted to delete 'gamma',
- or attempted to rename it.
- You have edited 'gamma' locally.
-
-Possible resolutions:
- 1) Leave gamma in place, with local modifications.
- 2) Remove gamma including local modifications.
-
---- Resolution 1)
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
-A gamma/iota
-
---- Resolution 2)
-$ svn remove --force gamma
-D gamma/delta
-D gamma/iota
-D gamma
-$ svn status
-D C gamma
-D gamma/delta
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
-D gamma
-D gamma/delta
-
-
-=== Use case 3, files
-
-Conflict reported:
-$ svn status
-D C alpha
-$ svn info alpha
-[...]
-Tree conflict:
- The update attempted to delete 'alpha',
- or attempted to rename it.
- You have deleted 'alpha' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) Leave alpha deleted.
- (Not shown below):
- If alpha was renamed on both sides, figure out the conflicting
- move target paths and decide where alpha should be moved to,
- and delete the undesired move target path before committing.
- If alpha was only renamed on one side, decide whether the
- file at the move target path should be deleted or not.
-
---- Resolution 1):
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status
-D alpha
-### Additional steps may be needed here in case alpha was renamed, see above.
-
-
-=== Use case 3, directories
-
-Conflict reported:
-
-$ svn status
-D C gamma
-D gamma/delta
-$ svn info gamma
-[...]
- The update attempted to delete 'gamma',
- or attempted to rename it.
- You have deleted 'gamma' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) Leave gamma deleted.
- (Not shown below):
- If gamma was renamed on both sides, figure out the conflicting
- move target paths and decide where gamma should be moved to,
- and delete the undesired move target path before committing.
- If gamma was only renamed on one side, decide whether the
- directory at the move target path should be deleted or not.
-
---- Resolution 1):
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
-D gamma
-D gamma/delta
-### Additional steps may be needed here in case gamma was renamed, see above.
-
-=== Use case 4, files
-
-Conflict reported:
-$ svn status
- M .
-! C alpha
-$ svn info alpha
-Path: alpha
-Name: alpha
-Node Kind: none
-Tree conflict:
- The merge attempted to edit 'alpha'.
-'alpha' does not exist locally. Maybe you renamed it? Or has it been
-renamed in the history of the branch you are merging into?
-
-Possible resolutions:
- 1) Ignore incoming edit.
- 2) Restore alpha in branch and run merge again.
- (Not shown below:)
- In case alpha was renamed, rename alpha in the branch
- and run the merge again.
-
---- Resolution 1)
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status
- M . # mergeinfo
-
---- Resolution 2)
-### revert merge to start from clean state, this also removes
-### tree conflict markers:
-$ svn revert -R .
-Reverted '.'
-Reverted 'alpha'
-$ svn status
-### find out when alpha was deleted on branch:
-$ svn log -r4 ^/branch
-------------------------------------------------------------------------
-r4 | stsp | 2008-11-20 14:43:21 +0000 (Thu, 20 Nov 2008) | 1 line
-
-deleted alpha
-------------------------------------------------------------------------
-### alpha was deleted in r4, restore alpha from r3:
-$ svn copy ^/branch/alpha_at_3 .
-A alpha
-### run the merge again, it succeeds:
-$ svn merge ^/trunk
---- Merging r2 through r4 into '.':
-U alpha
-$ svn status
- M .
-A + alpha
-
-
-== Use case 4, directories
-
-Conflict reported:
-$ svn status
- M .
-! C gamma
-$ svn info gamma
-Path: gamma
-Name: gamma
-Node Kind: none
-Tree conflict:
- The merge attempted to edit 'gamma'.
- You have deleted 'gamma' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) Ignore incoming edit.
- 2) Restore gamma in branch and run merge again.
- (Not shown below:)
- In case gamma was renamed, rename gamma in the branch
- and run the merge again.
-
--- Resolution 1)
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
- M . # mergeinfo
-
---- Resolution 2)
-### revert merge to start from clean state, this also removes
-### tree conflict markers:
-$ svn revert -R .
-Reverted '.'
-Reverted 'gamma'
-$ svn status
-### find out when gamma was deleted on branch:
-$ svn log -r4 ^/branch
-------------------------------------------------------------------------
-r4 | stsp | 2008-11-20 15:02:08 +0000 (Thu, 20 Nov 2008) | 1 line
-
-deleted gamma
-------------------------------------------------------------------------
-### gamma was deleted in r4, restore gamma from r3:
-$ svn copy ^/branch/gamma_at_3 .
-A gamma/delta
-Checked out revision 3.
-A gamma
-### run the merge again, it succeeds:
-$ svn merge ^/trunk
---- Merging r2 through r4 into '.':
-A gamma/iota
-
-
-== Use case 5, files
-
-Conflict reported:
-$ svn status
- M .
-M C alpha
-$ svn info alpha
-[...]
-Tree conflict:
- The merge attempted to delete 'alpha',
- or attempted to rename it.
-Either you have edited 'alpha' locally, or it has been edited in the
-history of the branch you are merging into, but those edits are not
-present on the branch you are merging from.
-
-Possible resolutions:
- 1) Keep alpha with local modifications.
- 2) Delete alpha.
- (Not shown below:)
- If alpha was renamed, determine move target path and
- apply local modifications there first.
-
--- Resolution 1)
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status alpha
-M alpha
-
--- Resolution 2)
-$ svn status
- M .
-M C alpha
-### If alpha was renamed, additional steps may be needed here
-### to transfer local modifications to the new location.
-$ svn remove --force alpha
-D alpha
-$ svn status
- M .
-D C alpha
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status
- M .
-D alpha
-
-
-== Use case 5, directories
-
-Conflict is not detected automatically.
-Check merge result manually for this conflict.
-
-
-=== Use case 6, files
-
-Conflict reported:
-$ svn status
- M .
-! C alpha
-$ svn info alpha
-Path: alpha
-Name: alpha
-Node Kind: none
-Tree conflict:
- The merge attempted to delete 'alpha',
- or attempted to rename it.
- You have deleted 'alpha' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) Delete alpha
- (Not shown below:)
- If alpha was renamed on both sides, figure out the conflicting
- move target paths and decide where alpha should be moved to,
- and delete the undesired move target path before committing.
- If alpha was only renamed on one side, decide whether the
- file at the move target path should be deleted or not.
-
---- Resolution 1)
-$ svn resolved alpha
-Resolved conflicted state of 'alpha'
-$ svn status
- M . # mergeinfo
-
-
-=== Use case 6, directories
-
-Conflict reported:
-$ svn status
- M .
-! C gamma
-$ svn info gamma
-Path: gamma
-Name: gamma
-Node Kind: none
-Tree conflict:
- The merge attempted to delete 'gamma',
- or attempted to rename it.
- You have deleted 'gamma' locally.
- Maybe you renamed it?
-
-Possible resolutions:
- 1) Delete gamma.
- (Not shown below:)
- If gamma was renamed on both sides, figure out the conflicting
- move target paths and decide where gamma should be moved to,
- and delete the undesired move target path before committing.
- If gamma was only renamed on one side, decide whether the
- file at the move target path should be deleted or not.
-
--- Resolution 1)
-$ svn resolved gamma
-Resolved conflicted state of 'gamma'
-$ svn status
- M . # mergeinfo
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (.../trunk) (revision 34356)
+++ subversion/svn/cl.h (.../branches/tc_url_rev) (revision 34365)
@@ -595,11 +595,16 @@ svn_cl__xml_print_header(const char *tagname, apr_
 svn_error_t *
 svn_cl__xml_print_footer(const char *tagname, apr_pool_t *pool);
 
-/* Return a (non-localised) string representation of KIND, being "dir" or
- "file" or, in any other case, the empty string. */
+/* For use in XML output, return a non-localised string representation
+ * of KIND, being "none" or "dir" or "file" or, in any other case,
+ * the empty string. */
 const char *
-svn_cl__node_kind_str(svn_node_kind_t kind);
+svn_cl__node_kind_str_xml(svn_node_kind_t kind);
 
+/* Return a (possibly localised) string representation of KIND, being "none" or
+ "dir" or "file" or, in any other case, the empty string. */
+const char *
+svn_cl__node_kind_str_human_readable(svn_node_kind_t kind);
 
 /* If PROPNAME is one of the svn: properties with a boolean value, and
  * PROPVAL looks like an attempt to turn the property off (i.e., it's
@@ -638,6 +643,13 @@ svn_cl__indent_string(const char *str,
                       const char *indent,
                       apr_pool_t *pool);
 
+
+/* Return a string showing NODE's kind, URL and revision, to the extent that
+ * that information is available in NODE. */
+const char *
+svn_cl__node_description(const svn_wc_conflict_version_t *node,
+ apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/svn/diff-cmd.c
===================================================================
--- subversion/svn/diff-cmd.c (.../trunk) (revision 34356)
+++ subversion/svn/diff-cmd.c (.../branches/tc_url_rev) (revision 34365)
@@ -94,7 +94,7 @@ summarize_xml(const svn_client_diff_summarize_t *s
     path = svn_path_local_style(path, pool);
 
   svn_xml_make_open_tag(&sb, pool, svn_xml_protect_pcdata, "path",
- "kind", svn_cl__node_kind_str(summary->node_kind),
+ "kind", svn_cl__node_kind_str_xml(summary->node_kind),
                         "item", kind_to_word(summary->summarize_kind),
                         "props", summary->prop_changed ? "modified" : "none",
                         NULL);
Index: subversion/svn/list-cmd.c
===================================================================
--- subversion/svn/list-cmd.c (.../trunk) (revision 34356)
+++ subversion/svn/list-cmd.c (.../branches/tc_url_rev) (revision 34365)
@@ -156,7 +156,7 @@ print_dirent_xml(void *baton,
   sb = svn_stringbuf_create("", pool);
 
   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
- "kind", svn_cl__node_kind_str(dirent->kind),
+ "kind", svn_cl__node_kind_str_xml(dirent->kind),
                         NULL);
 
   svn_cl__xml_tagged_cdata(&sb, pool, "name", entryname);
Index: subversion/svn/util.c
===================================================================
--- subversion/svn/util.c (.../trunk) (revision 34356)
+++ subversion/svn/util.c (.../branches/tc_url_rev) (revision 34365)
@@ -999,10 +999,12 @@ svn_cl__xml_print_footer(const char *tagname,
 
 
 const char *
-svn_cl__node_kind_str(svn_node_kind_t kind)
+svn_cl__node_kind_str_xml(svn_node_kind_t kind)
 {
   switch (kind)
     {
+ case svn_node_none:
+ return "none";
     case svn_node_dir:
       return "dir";
     case svn_node_file:
@@ -1012,6 +1014,21 @@ const char *
     }
 }
 
+const char *
+svn_cl__node_kind_str_human_readable(svn_node_kind_t kind)
+{
+ switch (kind)
+ {
+ case svn_node_none:
+ return _("none");
+ case svn_node_dir:
+ return _("dir");
+ case svn_node_file:
+ return _("file");
+ default:
+ return "";
+ }
+}
 
 svn_error_t *
 svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets,
@@ -1171,3 +1188,26 @@ svn_cl__indent_string(const char *str,
     }
   return out->data;
 }
+
+const char *
+svn_cl__node_description(const svn_wc_conflict_version_t *node,
+ apr_pool_t *pool)
+{
+ const char *url_str;
+
+ /* Construct the whole URL if we can, else use whatever we have. */
+ if (node->repos_url && node->path_in_repos)
+ url_str = svn_path_url_add_component(node->repos_url,
+ node->path_in_repos, pool);
+ else if (node->repos_url)
+ url_str = svn_path_url_add_component(node->repos_url, "...", pool);
+ else if (node->path_in_repos)
+ url_str = node->path_in_repos;
+ else
+ url_str = "...";
+
+ return apr_psprintf(pool, "(%s) %s@%ld",
+ svn_cl__node_kind_str_human_readable(node->node_kind),
+ url_str, node->peg_rev);
+}
+
Index: subversion/svn/tree-conflicts.c
===================================================================
--- subversion/svn/tree-conflicts.c (.../trunk) (revision 34356)
+++ subversion/svn/tree-conflicts.c (.../branches/tc_url_rev) (revision 34365)
@@ -20,10 +20,12 @@
 #include "svn_xml.h"
 #include "svn_path.h"
 
+#include "cl.h"
+
 #include "svn_private_config.h"
 
 static const char *
-select_action(const svn_wc_conflict_description_t *conflict)
+action_str(const svn_wc_conflict_description_t *conflict)
 {
   switch (conflict->action)
     {
@@ -39,7 +41,7 @@ static const char *
 }
 
 static const char *
-select_reason(const svn_wc_conflict_description_t *conflict)
+reason_str(const svn_wc_conflict_description_t *conflict)
 {
   switch (conflict->reason)
     {
@@ -66,12 +68,14 @@ svn_cl__get_human_readable_tree_conflict_descripti
   const svn_wc_conflict_description_t *conflict,
   apr_pool_t *pool)
 {
- const char *victim_name, *action, *reason;
+ const char *victim_name, *action, *reason, *operation;
   victim_name = svn_path_basename(conflict->path, pool);
- action = select_action(conflict);
- reason = select_reason(conflict);
+ reason = reason_str(conflict);
+ action = action_str(conflict);
+ operation = svn_wc_operation_str_human_readable(conflict->operation, pool);
   SVN_ERR_ASSERT(action && reason);
- *desc = apr_psprintf(pool, _("incoming %s, local %s"), action, reason);
+ *desc = apr_psprintf(pool, _("local %s, incoming %s upon %s"),
+ reason, action, operation);
   return SVN_NO_ERROR;
 }
 
Index: subversion/svn/info-cmd.c
===================================================================
--- subversion/svn/info-cmd.c (.../trunk) (revision 34356)
+++ subversion/svn/info-cmd.c (.../branches/tc_url_rev) (revision 34365)
@@ -90,7 +90,7 @@ print_info_xml(void *baton,
   /* "<entry ...>" */
   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
                         "path", svn_path_local_style(target, pool),
- "kind", svn_cl__node_kind_str(info->kind),
+ "kind", svn_cl__node_kind_str_xml(info->kind),
                         "revision", rev_str,
                         NULL);
 
@@ -431,10 +431,36 @@ print_info(void *baton,
 
   if (info->tree_conflict)
     {
- const char *desc;
+ const char *desc, *older_version, *their_version;
+
       SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
                 &desc, info->tree_conflict, pool));
- svn_cmdline_printf(pool, _("Tree conflict: %s"), desc);
+ older_version =
+ svn_cl__node_description(info->tree_conflict->older_version, pool);
+ their_version =
+ svn_cl__node_description(info->tree_conflict->their_version, pool);
+
+ SVN_ERR_ASSERT(older_version && their_version);
+
+ svn_cmdline_printf(pool,
+ "%s: %s\n"
+ " %s: %s\n"
+ " %s: %s",
+ _("Tree conflict"),
+ desc,
+ (info->tree_conflict->operation
+ == svn_wc_operation_merge)
+ ? _("Merge left") /* (1) */
+ : _("Older version"),
+ older_version,
+ (info->tree_conflict->operation
+ == svn_wc_operation_merge)
+ ? _("Merge right")
+ : _("Their version"),
+ their_version);
+ /* (1): Sneaking in a space in "Merge left" so that it is the
+ * same length as "Merge right" while it still starts in the same
+ * column. That's just a tiny tweak in the English `svn'. */
     }
 
   /* Print extra newline separator. */
Index: subversion/include/svn_wc.h
===================================================================
--- subversion/include/svn_wc.h (.../trunk) (revision 34356)
+++ subversion/include/svn_wc.h (.../branches/tc_url_rev) (revision 34365)
@@ -1167,7 +1167,54 @@ typedef enum svn_wc_operation_t
 
 } svn_wc_operation_t;
 
+/** Provides a possibly localized human readable string for
+ * a given @a operation.
+ * @note @a pool is currently not used.
+ * @since New in 1.6.
+ */
+const char *
+svn_wc_operation_str_human_readable(svn_wc_operation_t operation,
+ apr_pool_t *pool);
 
+/** Provides an XML name for a given @a operation.
+ * @note @a pool is currently not used.
+ * @since New in 1.6.
+ */
+const char *
+svn_wc_operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool);
+
+
+/** Info about one of the conflicting versions of a node. Each field may
+ * have its respective null/invalid/unknown value if the corresponding
+ * information is not relevant or not available.
+ *
+ * @note Fields may be added to the end of this structure in future
+ * versions. Therefore, to preserve binary compatibility, users
+ * should not directly allocate structures of this type.
+ *
+ * @since New in 1.6.
+*/
+typedef struct svn_wc_conflict_version_t
+{
+ /* Where to find this node version in a repository */
+ const char *repos_url; /* URL of repository root */
+ /* ### Also? repos_uuid; */
+ svn_revnum_t peg_rev; /* revision at which to look up path_in_repos */
+ const char *path_in_repos; /* path within repos; must not start with '/' */
+
+ /* Info about this node */
+ svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */
+
+ /* ### Also? ... */
+ /* Where to find a local copy of the node */
+ /* const char *content_cache_path; */
+ /* const char *props_cache_path; */
+
+ /* Remember to update svn_wc__conflict_version_dup()
+ * in case you add fields to this struct. */
+} svn_wc_conflict_version_t;
+
+
 /** A struct that describes a conflict that has occurred in the
  * working copy. Passed to @c svn_wc_conflict_resolver_func_t.
  *
@@ -1189,7 +1236,8 @@ typedef struct svn_wc_conflict_description_t
   /** The path that is in conflict (for a tree conflict, it is the victim) */
   const char *path;
 
- /** The node type of the path being operated on */
+ /** The node type of the path being operated on (for a tree conflict,
+ * ### which version?) */
   svn_node_kind_t node_kind;
 
   /** What sort of conflict are we describing? */
@@ -1210,7 +1258,10 @@ typedef struct svn_wc_conflict_description_t
 
   /** If not NULL, an open working copy access baton to either the
    * path itself (if @c path is a directory), or to the parent
- * directory (if @c path is a file.) */
+ * directory (if @c path is a file.)
+ * For a tree conflict, this will always be an access baton
+ * to the parent directory of the path, even if the path is
+ * a directory. */
   svn_wc_adm_access_t *access;
 
   /** The action being attempted on the conflicted node or property.
@@ -1255,6 +1306,14 @@ typedef struct svn_wc_conflict_description_t
    */
   svn_wc_operation_t operation;
 
+ /** Info on the "merge-left source" or "older" version of incoming change.
+ * @since New in 1.6. */
+ svn_wc_conflict_version_t *older_version;
+
+ /** Info on the "merge-right source" or "their" version of incoming change.
+ * @since New in 1.6. */
+ svn_wc_conflict_version_t *their_version;
+
   /* Remember to adjust svn_wc__conflict_description_dup()
    * if you add new fields to this struct. */
 } svn_wc_conflict_description_t;
@@ -1308,9 +1367,10 @@ svn_wc_conflict_description_create_prop(const char
  *
  * Set the @c path field of the created struct to @a path, the @c access
  * field to @a adm_access, the @c kind field to @c
- * svn_wc_conflict_kind_tree, the @c node_kind to @a node_kind, and the @c
- * operation to @a operation. Make only shallow copies of the pointer
- * arguments.
+ * svn_wc_conflict_kind_tree, the @c node_kind to @a node_kind, the @c
+ * operation to @a operation, the @c older_version field to @a older_version,
+ * and the @c their_version field to @a their_version.
+ * Make only shallow copies of the pointer arguments.
  *
  * @note: It is the caller's responsibility to set the other required fields
  * (such as the four file names and @c action and @c reason).
@@ -1322,6 +1382,8 @@ svn_wc_conflict_description_create_tree(const char
                                         svn_wc_adm_access_t *adm_access,
                                         svn_node_kind_t node_kind,
                                         svn_wc_operation_t operation,
+ svn_wc_conflict_version_t *older_version,
+ svn_wc_conflict_version_t *their_version,
                                         apr_pool_t *pool);
 
 
Index: subversion/include/private/svn_wc_private.h
===================================================================
--- subversion/include/private/svn_wc_private.h (.../trunk) (revision 34356)
+++ subversion/include/private/svn_wc_private.h (.../branches/tc_url_rev) (revision 34365)
@@ -227,6 +227,15 @@ svn_wc_conflict_description_t *
 svn_wc__conflict_description_dup(const svn_wc_conflict_description_t *conflict,
                                  apr_pool_t *pool);
 
+/** Return a duplicate of @a version, allocated in @a pool.
+ * No part of the new version will be shared with @a version.
+ *
+ * @since New in 1.6.
+ */
+svn_wc_conflict_version_t *
+svn_wc__conflict_version_dup(const svn_wc_conflict_version_t *version,
+ apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/libsvn_wc/util.c
===================================================================
--- subversion/libsvn_wc/util.c (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/util.c (.../branches/tc_url_rev) (revision 34365)
@@ -29,6 +29,7 @@
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "wc.h" /* just for prototypes of things in this .c file */
+#include "tree_conflicts.h" /* just for the SVN_WC__OPERATION* defines */
 #include "private/svn_wc_private.h"
 
 #include "svn_private_config.h"
@@ -333,6 +334,8 @@ svn_wc_conflict_description_create_tree(const char
                                         svn_wc_adm_access_t *adm_access,
                                         svn_node_kind_t node_kind,
                                         svn_wc_operation_t operation,
+ svn_wc_conflict_version_t *older_version,
+ svn_wc_conflict_version_t *their_version,
                                         apr_pool_t *pool)
 {
   svn_wc_conflict_description_t *conflict;
@@ -343,6 +346,8 @@ svn_wc_conflict_description_create_tree(const char
   conflict->kind = svn_wc_conflict_kind_tree;
   conflict->access = adm_access;
   conflict->operation = operation;
+ conflict->older_version = older_version;
+ conflict->their_version = their_version;
   return conflict;
 }
 
@@ -372,6 +377,63 @@ svn_wc__conflict_description_dup(const svn_wc_conf
     new_conflict->my_file = apr_pstrdup(pool, conflict->my_file);
   if (conflict->merged_file)
     new_conflict->merged_file = apr_pstrdup(pool, conflict->merged_file);
+ if (conflict->older_version)
+ new_conflict->older_version =
+ svn_wc__conflict_version_dup(conflict->older_version, pool);
+ if (conflict->their_version)
+ new_conflict->their_version =
+ svn_wc__conflict_version_dup(conflict->their_version, pool);
 
   return new_conflict;
 }
+
+svn_wc_conflict_version_t *
+svn_wc__conflict_version_dup(const svn_wc_conflict_version_t *version,
+ apr_pool_t *pool)
+{
+
+ svn_wc_conflict_version_t *new_version;
+
+ new_version = apr_pcalloc(pool, sizeof(*new_version));
+
+ /* Shallow copy all members. */
+ *new_version = *version;
+
+ if (version->repos_url)
+ new_version->repos_url = apr_pstrdup(pool, version->repos_url);
+
+ if (version->path_in_repos)
+ new_version->path_in_repos = apr_pstrdup(pool, version->path_in_repos);
+
+ return new_version;
+}
+
+const char *
+svn_wc_operation_str_human_readable(svn_wc_operation_t operation,
+ apr_pool_t *pool)
+{
+ switch(operation){
+ case svn_wc_operation_update:
+ return _("update");
+ case svn_wc_operation_switch:
+ return _("switch");
+ case svn_wc_operation_merge:
+ return _("merge");
+ }
+ return _("unknown operation");
+}
+
+const char *
+svn_wc_operation_str_xml(svn_wc_operation_t operation, apr_pool_t *pool)
+{
+ switch(operation){
+ case svn_wc_operation_update:
+ return "update";
+ case svn_wc_operation_switch:
+ return "switch";
+ case svn_wc_operation_merge:
+ return "merge";
+ }
+ return "unknown_operation";
+}
+
Index: subversion/libsvn_wc/wc.h
===================================================================
--- subversion/libsvn_wc/wc.h (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/wc.h (.../branches/tc_url_rev) (revision 34365)
@@ -255,16 +255,24 @@ svn_wc__text_modified_internal_p(svn_boolean_t *mo
    conflict is encountered, giving the callback a chance to resolve
    the conflict (before marking the file 'conflicted').
 
+ When LEFT_VERSION and RIGHT_VERSION are non-NULL, pass them to the
+ conflict resolver as older_version and their_version.
+
+ ## TODO: We should store the information in LEFT_VERSION and RIGHT_VERSION
+ in the workingcopy for future retrieval via svn info.
+
    Property changes sent by the update are provided in PROP_DIFF.
 
- For a complete description, see svn_wc_merge2() for which this is
+ For a complete description, see svn_wc_merge3() for which this is
    the (loggy) implementation.
 */
 svn_error_t *
 svn_wc__merge_internal(svn_stringbuf_t **log_accum,
                        enum svn_wc_merge_outcome_t *merge_outcome,
                        const char *left,
+ svn_wc_conflict_version_t *left_version,
                        const char *right,
+ svn_wc_conflict_version_t *right_version,
                        const char *merge_target,
                        const char *copyfrom_text,
                        svn_wc_adm_access_t *adm_access,
Index: subversion/libsvn_wc/update_editor.c
===================================================================
--- subversion/libsvn_wc/update_editor.c (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/update_editor.c (.../branches/tc_url_rev) (revision 34365)
@@ -556,6 +556,7 @@ static svn_error_t *
 do_entry_deletion(struct edit_baton *eb,
                   const char *parent_path,
                   const char *path,
+ const char *their_path,
                   int *log_number,
                   apr_pool_t *pool);
 
@@ -610,7 +611,11 @@ complete_directory(struct edit_baton *eb,
               if (!target_access && entry->kind == svn_node_dir)
                 {
                   int log_number = 0;
- SVN_ERR(do_entry_deletion(eb, eb->anchor, eb->target,
+ /* I can't find a tree-conflict activated from this call,
+ * so we don't *need* THEIR_PATH at all and can skip
+ * composing it. Just pass NULL. */
+ /* ### TODO make really really sure. */
+ SVN_ERR(do_entry_deletion(eb, eb->anchor, eb->target, NULL,
                                             &log_number, pool));
                 }
               else
@@ -1389,6 +1394,8 @@ check_tree_conflict(svn_wc_conflict_description_t
                     const svn_wc_entry_t *entry,
                     svn_wc_adm_access_t *parent_adm_access,
                     svn_wc_conflict_action_t action,
+ svn_node_kind_t their_node_kind,
+ const char *their_url,
                     apr_pool_t *pool)
 {
   svn_wc_conflict_reason_t reason = (svn_wc_conflict_reason_t)(-1);
@@ -1415,6 +1422,7 @@ check_tree_conflict(svn_wc_conflict_description_t
 
     case svn_wc_conflict_action_delete:
       /* Use case 3: Deleting a locally-deleted item. */
+ their_node_kind = svn_node_none;
       if (entry->schedule == svn_wc_schedule_delete
           || entry->schedule == svn_wc_schedule_replace)
         reason = svn_wc_conflict_reason_deleted;
@@ -1460,11 +1468,61 @@ check_tree_conflict(svn_wc_conflict_description_t
   if (reason != (svn_wc_conflict_reason_t)(-1))
     {
       svn_wc_conflict_description_t *conflict;
+ svn_wc_conflict_version_t *older_version;
+ svn_wc_conflict_version_t *their_version;
+ const char *repos_url = NULL;
+ const char *path_in_repos = NULL;
 
+ repos_url = entry->repos;
+ path_in_repos = svn_path_is_child(repos_url, entry->url, pool);
+ if (path_in_repos == NULL)
+ path_in_repos = ".";
+ /* "Their" repos_url (repository root URL) will be the same. */
+
+ if (eb->switch_url != NULL)
+ {
+ /* do_entry_deletion() still passes NULL for their_url
+ * sometimes. However, I could not find any case where
+ * it was needed during those calls. This is just paranoia: */
+ if (their_url != NULL)
+ path_in_repos = svn_path_is_child(repos_url, their_url, pool);
+ else
+ {
+ /* Oh no! We have no complete URL!
+ * At the time of writing this, there is no known case that
+ * would cause this to happen. Shout if you've found one. */
+ path_in_repos = svn_path_is_child(repos_url, eb->switch_url,
+ pool);
+ path_in_repos = apr_pstrcat(
+ pool, path_in_repos,
+ "_THIS_IS_INCOMPLETE",
+ NULL);
+ }
+ }
+
+ older_version = apr_pcalloc(pool, sizeof(*older_version));
+ older_version->repos_url = repos_url;
+ older_version->peg_rev = entry->revision;
+ older_version->path_in_repos = path_in_repos;
+ older_version->node_kind =
+ (entry->schedule == svn_wc_schedule_delete) ? svn_node_none
+ : entry->kind;
+ /* entry->kind is both base kind and working kind, because schedule
+ * replace-by-different-kind is not supported. */
+ /* ### TODO: but in case the entry is locally removed, entry->kind
+ * is svn_node_none and doesn't reflect the older kind. Then we
+ * need to find out the older kind in a different way! */
+
+ their_version = apr_pcalloc(pool, sizeof(*their_version));
+ their_version->repos_url = repos_url;
+ their_version->peg_rev = *eb->target_revision;
+ their_version->path_in_repos = path_in_repos;
+ their_version->node_kind = their_node_kind;
+
       conflict = svn_wc_conflict_description_create_tree(
         full_path, parent_adm_access, entry->kind,
         eb->switch_url ? svn_wc_operation_switch : svn_wc_operation_update,
- pool);
+ older_version, their_version, pool);
       conflict->action = action;
       conflict->reason = reason;
 
@@ -1577,6 +1635,7 @@ static svn_error_t *
 do_entry_deletion(struct edit_baton *eb,
                   const char *parent_path,
                   const char *path,
+ const char *their_path,
                   int *log_number,
                   apr_pool_t *pool)
 {
@@ -1628,7 +1687,8 @@ do_entry_deletion(struct edit_baton *eb,
   if (victim_path == NULL)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, log_item, full_path,
                                 entry, adm_access,
- svn_wc_conflict_action_delete, pool));
+ svn_wc_conflict_action_delete,
+ svn_node_none, their_path, pool));
 
   if (tree_conflict != NULL)
     {
@@ -1772,11 +1832,13 @@ delete_entry(const char *path,
              apr_pool_t *pool)
 {
   struct dir_baton *pb = parent_baton;
+ const char *path_basename = svn_path_basename(path, pool);
+ const char *their_path = svn_path_url_add_component(pb->new_URL,
+ path_basename, pool);
 
- SVN_ERR(check_path_under_root(pb->path, svn_path_basename(path, pool),
- pool));
- return do_entry_deletion(pb->edit_baton, pb->path, path, &pb->log_number,
- pool);
+ SVN_ERR(check_path_under_root(pb->path, path_basename, pool));
+ return do_entry_deletion(pb->edit_baton, pb->path, path, their_path,
+ &pb->log_number, pool);
 }
 
 
@@ -1953,7 +2015,8 @@ add_directory(const char *path,
               SVN_ERR(check_tree_conflict(&tree_conflict, eb,
                                           pb->log_accum, db->path, entry,
                                           parent_adm_access,
- svn_wc_conflict_action_add, pool));
+ svn_wc_conflict_action_add,
+ svn_node_dir, db->new_URL, pool));
 
               if (tree_conflict != NULL)
                 {
@@ -2175,7 +2238,8 @@ open_directory(const char *path,
        with one notification. */
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, pb->log_accum,
                                 full_path, entry, parent_adm_access,
- svn_wc_conflict_action_edit, pool));
+ svn_wc_conflict_action_edit,
+ svn_node_dir, db->new_URL, pool));
 
   /* If property-conflicted, skip the tree with notification. */
   SVN_ERR(svn_wc_conflicted_p2(NULL, &prop_conflicted, NULL, full_path,
@@ -2975,7 +3039,8 @@ add_file(const char *path,
   if (victim_path == NULL)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, pb->log_accum, full_path,
                                 entry, adm_access,
- svn_wc_conflict_action_add, subpool));
+ svn_wc_conflict_action_add,
+ svn_node_file, fb->new_URL, subpool));
 
   if (victim_path != NULL || tree_conflict != NULL)
     {
@@ -3139,7 +3204,8 @@ open_file(const char *path,
   if (victim_path == NULL)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, pb->log_accum, full_path,
                                 entry, adm_access,
- svn_wc_conflict_action_edit, pool));
+ svn_wc_conflict_action_edit,
+ svn_node_file, fb->new_URL, pool));
 
   /* Does the file already have text or property conflicts? */
   SVN_ERR(svn_wc_conflicted_p2(&text_conflicted, &prop_conflicted, NULL,
@@ -3756,8 +3822,8 @@ merge_file(svn_wc_notify_state_t *content_state,
                  Remember that this function wants full paths! */
               SVN_ERR(svn_wc__merge_internal
                       (&log_accum, &merge_outcome,
- merge_left,
- new_text_base_path,
+ merge_left, NULL,
+ new_text_base_path, NULL,
                        fb->path,
                        fb->copied_working_text,
                        adm_access,
@@ -4004,7 +4070,13 @@ close_edit(void *edit_baton,
      pretend that the editor deleted the entry. The helper function
      do_entry_deletion() will take care of the necessary steps. */
   if ((*eb->target) && (svn_wc__adm_missing(eb->adm_access, target_path)))
- SVN_ERR(do_entry_deletion(eb, eb->anchor, eb->target, &log_number, pool));
+ /* I can't find a tree-conflict activated from this call
+ * (tried e.g. in update_tests.py 14 update_deleted_missing_dir),
+ * so we don't *need* THEIR_PATH at all and can skip composing it.
+ * Just pass NULL. */
+ /* ### TODO make really really sure. */
+ SVN_ERR(do_entry_deletion(eb, eb->anchor, eb->target, NULL,
+ &log_number, pool));
 
   /* The editor didn't even open the root; we have to take care of
      some cleanup stuffs. */
Index: subversion/libsvn_wc/merge.c
===================================================================
--- subversion/libsvn_wc/merge.c (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/merge.c (.../branches/tc_url_rev) (revision 34365)
@@ -271,7 +271,9 @@ svn_error_t *
 svn_wc__merge_internal(svn_stringbuf_t **log_accum,
                        enum svn_wc_merge_outcome_t *merge_outcome,
                        const char *left,
+ svn_wc_conflict_version_t *left_version,
                        const char *right,
+ svn_wc_conflict_version_t *right_version,
                        const char *merge_target,
                        const char *copyfrom_text,
                        svn_wc_adm_access_t *adm_access,
@@ -424,6 +426,9 @@ svn_wc__merge_internal(svn_stringbuf_t **log_accum
               cdesc->my_file = tmp_target;
               cdesc->merged_file = result_target;
 
+ cdesc->older_version = left_version;
+ cdesc->their_version = right_version;
+
               SVN_ERR(conflict_func(&result, cdesc, conflict_baton, pool));
               if (result == NULL)
                 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
@@ -732,6 +737,9 @@ svn_wc__merge_internal(svn_stringbuf_t **log_accum
           cdesc->my_file = tmp_target;
           cdesc->merged_file = NULL; /* notice there is NO merged file! */
 
+ cdesc->older_version = left_version;
+ cdesc->their_version = right_version;
+
           SVN_ERR(conflict_func(&result, cdesc, conflict_baton, pool));
           if (result == NULL)
             return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
@@ -910,7 +918,9 @@ svn_wc_merge3(enum svn_wc_merge_outcome_t *merge_o
   svn_stringbuf_t *log_accum = svn_stringbuf_create("", pool);
 
   SVN_ERR(svn_wc__merge_internal(&log_accum, merge_outcome,
- left, right, merge_target,
+ left, NULL,
+ right, NULL,
+ merge_target,
                                  NULL,
                                  adm_access,
                                  left_label, right_label, target_label,
Index: subversion/libsvn_wc/tree_conflicts.c
===================================================================
--- subversion/libsvn_wc/tree_conflicts.c (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/tree_conflicts.c (.../branches/tc_url_rev) (revision 34365)
@@ -62,21 +62,6 @@ static const char field_separator = SVN_WC__TREE_C
 static const char desc_separator = SVN_WC__TREE_CONFLICT_DESC_SEPARATOR;
 static const char escape_char = SVN_WC__TREE_CONFLICT_ESCAPE_CHAR;
 
-/* If **INPUT starts with *TOKEN, advance *INPUT by the length of *TOKEN
- * and return TRUE. Else, return FALSE and leave *INPUT alone. */
-static svn_boolean_t
-advance_on_match(const char **input, const char *token)
-{
- int len = strlen(token);
- if ((strncmp(*input, token, len)) == 0)
- {
- *input += len;
- return TRUE;
- }
- else
- return FALSE;
-}
-
 /* Ensure the next character at position *START is a field separator, and
  * advance *START past it. */
 static svn_error_t *
@@ -85,15 +70,30 @@ read_field_separator(const char **start,
 {
   if (*start >= end || **start != field_separator)
     return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Missing field delimiter in tree conflict description"));
+ _("Missing field separator in tree conflict description"));
 
   (*start)++;
   return SVN_NO_ERROR;
 }
 
+/* Ensure the next character at position *START is a description separator,
+ * and advance *START past it. */
+static svn_error_t *
+read_desc_separator(const char **start,
+ const char *end)
+{
+ if (*start >= end || **start != desc_separator)
+ return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
+ _("No separator at end of tree conflict description, "
+ "even though there is still data left to read"));
+
+ (*start)++;
+ return SVN_NO_ERROR;
+}
+
 /* Parse a string field out of the data pointed to by *START. Set *STR to a
  * copy of the unescaped string, allocated in POOL. The string may be empty.
- * Stop reading at an unescaped field- or description delimiter, and never
+ * Stop reading at an unescaped field- or description separator, and never
  * read past END.
  * After reading, make *START point to the character after the field.
  */
@@ -138,148 +138,125 @@ read_string_field(const char **str,
   return SVN_NO_ERROR;
 }
 
-/* Parse the 'victim path' field pointed to by *START. Modify the 'path'
- * field of *CONFLICT by appending the victim name to its existing value.
- * Stop reading at a field delimiter and never read past END.
- * After reading, make *START point to the character after the field.
- * Do all allocations in POOL.
- */
-static svn_error_t *
-read_victim_path(svn_wc_conflict_description_t *conflict,
- const char **start,
- const char *end,
- apr_pool_t *pool)
+/* A mapping between a string STR and an enumeration value VAL. */
+typedef struct enum_mapping_t
 {
- const char *victim_basename;
+ const char *str;
+ int val;
+} enum_mapping_t;
 
- SVN_ERR(read_string_field(&victim_basename, start, end, pool));
+/* A map for svn_node_kind_t values. */
+static const enum_mapping_t node_kind_map[] =
+{
+ { SVN_WC__NODE_NONE, svn_node_none },
+ { SVN_WC__NODE_FILE, svn_node_file },
+ { SVN_WC__NODE_DIR, svn_node_dir },
+ { "", svn_node_unknown },
+ { NULL, 0 }
+};
 
- if (victim_basename[0] == '\0')
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Empty 'victim' field in tree conflict "
- "description"));
+/* A map for svn_wc_operation_t values. */
+static const enum_mapping_t operation_map[] =
+{
+ { SVN_WC__OPERATION_UPDATE, svn_wc_operation_update },
+ { SVN_WC__OPERATION_SWITCH, svn_wc_operation_switch },
+ { SVN_WC__OPERATION_MERGE, svn_wc_operation_merge },
+ { NULL, 0 }
+};
 
- conflict->path = svn_path_join(conflict->path, victim_basename, pool);
+/* A map for svn_wc_conflict_action_t values. */
+static const enum_mapping_t action_map[] =
+{
+ { SVN_WC__CONFLICT_ACTION_EDITED, svn_wc_conflict_action_edit },
+ { SVN_WC__CONFLICT_ACTION_DELETED, svn_wc_conflict_action_delete },
+ { SVN_WC__CONFLICT_ACTION_ADDED, svn_wc_conflict_action_add },
+ { NULL, 0 }
+};
 
- return SVN_NO_ERROR;
-}
+/* A map for svn_wc_conflict_reason_t values. */
+static const enum_mapping_t reason_map[] =
+{
+ { SVN_WC__CONFLICT_REASON_EDITED, svn_wc_conflict_reason_edited },
+ { SVN_WC__CONFLICT_REASON_DELETED, svn_wc_conflict_reason_deleted },
+ { SVN_WC__CONFLICT_REASON_MISSING, svn_wc_conflict_reason_missing },
+ { SVN_WC__CONFLICT_REASON_OBSTRUCTED, svn_wc_conflict_reason_obstructed },
+ { SVN_WC__CONFLICT_REASON_ADDED, svn_wc_conflict_reason_added },
+ { NULL, 0 }
+};
 
-/* Parse the 'node_kind' field pointed to by *START into the
- * the tree conflict descriptor pointed to by DESC.
+/* Parse the enumeration field pointed to by *START into *RESULT as a plain
+ * 'int', using MAP to convert from strings to enumeration values.
+ * In MAP, a null STR field marks the end of the map.
  * Don't read further than END.
  * After reading, make *START point to the character after the field.
  */
 static svn_error_t *
-read_node_kind(svn_wc_conflict_description_t *conflict,
- const char **start,
- const char *end)
+read_enum_field(int *result,
+ const enum_mapping_t *map,
+ const char **start,
+ const char *end,
+ apr_pool_t *pool)
 {
- if (*start >= end)
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Expected tree conflict data but got none"));
+ const char *str;
+ int i;
 
- if (advance_on_match(start, SVN_WC__NODE_FILE))
- conflict->node_kind = svn_node_file;
- else if (advance_on_match(start, SVN_WC__NODE_DIR))
- conflict->node_kind = svn_node_dir;
- else
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Invalid 'node_kind' field in tree conflict description"));
+ SVN_ERR(read_string_field(&str, start, end, pool));
 
- return SVN_NO_ERROR;
-}
+ /* Find STR in MAP; error if not found. */
+ for (i = 0; ; i++)
+ {
+ if (map[i].str == NULL)
+ return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
+ _("Unknown enumeration value in tree conflict "
+ "description"));
+ if (strcmp(str, map[i].str) == 0)
+ break;
+ }
 
-/* Parse the 'operation' field pointed to by *START into the
- * the tree conflict descriptor pointed to by DESC.
- * Don't read further than END.
- * After reading, make *START point to the character after the field.
- */
-static svn_error_t *
-read_operation(svn_wc_conflict_description_t *conflict,
- const char **start,
- const char *end)
-{
- if (*start >= end)
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Expected tree conflict data but got none"));
-
- if (advance_on_match(start, SVN_WC__OPERATION_UPDATE))
- conflict->operation = svn_wc_operation_update;
- else if (advance_on_match(start, SVN_WC__OPERATION_SWITCH))
- conflict->operation = svn_wc_operation_switch;
- else if (advance_on_match(start, SVN_WC__OPERATION_MERGE))
- conflict->operation = svn_wc_operation_merge;
- else
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Invalid 'operation' field in tree conflict description"));
-
+ *result = map[i].val;
   return SVN_NO_ERROR;
 }
 
-/* Parse the 'action' field pointed to by *START into the
- * the tree conflict descriptor pointed to by DESC.
+/* Parse the conflict info fields pointed to by *START into *VERSION_INFO.
  * Don't read further than END.
  * After reading, make *START point to the character after the field.
  */
 static svn_error_t *
-read_action(svn_wc_conflict_description_t *conflict,
- const char **start,
- const char *end)
+read_node_version_info(svn_wc_conflict_version_t *version_info,
+ const char **start,
+ const char *end,
+ apr_pool_t *pool)
 {
- if (*start >= end)
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Expected tree conflict data but got none"));
+ const char *str;
+ int n;
 
- if (advance_on_match(start, SVN_WC__CONFLICT_ACTION_EDITED))
- conflict->action = svn_wc_conflict_action_edit;
- else if (advance_on_match(start, SVN_WC__CONFLICT_ACTION_DELETED))
- conflict->action = svn_wc_conflict_action_delete;
- else if (advance_on_match(start, SVN_WC__CONFLICT_ACTION_ADDED))
- conflict->action = svn_wc_conflict_action_add;
- else
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Invalid 'action' field in tree conflict description"));
+ /* repos_url */
+ SVN_ERR(read_string_field(&str, start, end, pool));
+ version_info->repos_url = (str[0] == '\0') ? NULL : str;
+ SVN_ERR(read_field_separator(start, end));
 
- return SVN_NO_ERROR;
-}
+ /* peg_rev */
+ SVN_ERR(read_string_field(&str, start, end, pool));
+ version_info->peg_rev = (str[0] == '\0') ? SVN_INVALID_REVNUM
+ : SVN_STR_TO_REV(str);
+ SVN_ERR(read_field_separator(start, end));
 
-/* Parse the 'reason' field pointed to by *START into the
- * the tree conflict descriptor pointed to by DESC.
- * Don't read further than END.
- * After reading, make *START point to the character after the field.
- */
-static svn_error_t *
-read_reason(svn_wc_conflict_description_t *conflict,
- const char **start,
- const char *end)
-{
- if (*start >= end)
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Expected tree conflict data but got none"));
+ /* path_in_repos */
+ SVN_ERR(read_string_field(&str, start, end, pool));
+ version_info->path_in_repos = (str[0] == '\0') ? NULL : str;
+ SVN_ERR(read_field_separator(start, end));
 
- if (advance_on_match(start, SVN_WC__CONFLICT_REASON_EDITED))
- conflict->reason = svn_wc_conflict_reason_edited;
- else if (advance_on_match(start, SVN_WC__CONFLICT_REASON_DELETED))
- conflict->reason = svn_wc_conflict_reason_deleted;
- else if (advance_on_match(start, SVN_WC__CONFLICT_REASON_MISSING))
- conflict->reason = svn_wc_conflict_reason_missing;
- else if (advance_on_match(start, SVN_WC__CONFLICT_REASON_OBSTRUCTED))
- conflict->reason = svn_wc_conflict_reason_obstructed;
- else if (advance_on_match(start, SVN_WC__CONFLICT_REASON_ADDED))
- conflict->reason = svn_wc_conflict_reason_added;
- else
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Invalid 'reason' field in tree conflict description"));
+ /* node_kind */
+ SVN_ERR(read_enum_field(&n, node_kind_map, start, end, pool));
+ version_info->node_kind = (svn_node_kind_t)n;
 
   return SVN_NO_ERROR;
 }
 
 /* Parse a newly allocated svn_wc_conflict_description_t object from the
  * character string pointed to by *START. Return the result in *CONFLICT.
- * Don't read further than END. If a description separator is found in
- * the string after the conflict description that was read, set *START
- * to the first character of the next conflict description.
- * Otherwise, set *START to NULL.
+ * Don't read further than END. Set *START to point to the next character
+ * after the description that was read.
  * DIR_PATH is the path to the WC directory whose conflicts are being read.
  * Do all allocations in pool.
  */
@@ -290,38 +267,64 @@ read_one_tree_conflict(svn_wc_conflict_description
                        const char *dir_path,
                        apr_pool_t *pool)
 {
- if (*start >= end)
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Expected tree conflict data but got none"));
+ const char *victim_basename;
+ svn_node_kind_t node_kind;
+ svn_wc_operation_t operation;
+ svn_wc_conflict_version_t *older_version;
+ svn_wc_conflict_version_t *their_version;
+ int n;
 
- *conflict = svn_wc_conflict_description_create_tree(
- dir_path, NULL, svn_node_none, 0, pool);
+ SVN_ERR_ASSERT(*start < end);
 
- /* Each of these modifies *START ! */
- SVN_ERR(read_victim_path(*conflict, start, end, pool));
+ /* Each read_...() call modifies *START ! */
+
+ /* victim basename */
+ SVN_ERR(read_string_field(&victim_basename, start, end, pool));
+ if (victim_basename[0] == '\0')
+ return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
+ _("Empty 'victim' field in tree conflict "
+ "description"));
   SVN_ERR(read_field_separator(start, end));
- SVN_ERR(read_node_kind(*conflict, start, end));
+
+ /* node_kind */
+ SVN_ERR(read_enum_field(&n, node_kind_map, start, end, pool));
+ node_kind = (svn_node_kind_t)n;
+ if (node_kind != svn_node_file && node_kind != svn_node_dir)
+ return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
+ _("Invalid 'node_kind' field in tree conflict description"));
   SVN_ERR(read_field_separator(start, end));
- SVN_ERR(read_operation(*conflict, start, end));
+
+ /* operation */
+ SVN_ERR(read_enum_field(&n, operation_map, start, end, pool));
+ operation = (svn_wc_operation_t)n;
   SVN_ERR(read_field_separator(start, end));
- SVN_ERR(read_action(*conflict, start, end));
+
+ /* Construct the description object */
+ older_version = apr_pcalloc(pool, sizeof(*older_version));
+ their_version = apr_pcalloc(pool, sizeof(*their_version));
+ *conflict = svn_wc_conflict_description_create_tree(
+ svn_path_join(dir_path, victim_basename, pool),
+ NULL, node_kind, operation, older_version, their_version, pool);
+
+ /* action */
+ SVN_ERR(read_enum_field(&n, action_map, start, end, pool));
+ (*conflict)->action = (svn_wc_conflict_action_t)n;
   SVN_ERR(read_field_separator(start, end));
- SVN_ERR(read_reason(*conflict, start, end));
 
- /* *START should now point to a description separator
- * if there are any descriptions left. */
- if (**start == desc_separator)
- (*start)++;
- else
- {
- if (*start >= end)
- *start = NULL;
- else
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("No delimiter at end of tree conflict description, "
- "even though there is still data left to read"));
- }
+ /* reason */
+ SVN_ERR(read_enum_field(&n, reason_map, start, end, pool));
+ (*conflict)->reason = (svn_wc_conflict_reason_t)n;
+ SVN_ERR(read_field_separator(start, end));
 
+ /* older_version */
+ SVN_ERR(read_node_version_info((*conflict)->older_version, start, end,
+ pool));
+ SVN_ERR(read_field_separator(start, end));
+
+ /* their_version */
+ SVN_ERR(read_node_version_info((*conflict)->their_version, start, end,
+ pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -332,34 +335,29 @@ svn_wc__read_tree_conflicts(apr_array_header_t **c
                             apr_pool_t *pool)
 {
   const char *start, *end;
- svn_wc_conflict_description_t *conflict = NULL;
 
   if (conflict_data == NULL)
- {
- return SVN_NO_ERROR;
- }
+ return SVN_NO_ERROR;
 
   SVN_ERR_ASSERT(*conflicts);
 
   start = conflict_data;
   end = start + strlen(start);
 
- while (start != NULL && start <= end) /* Yes, '<=', because 'start == end'
- is a special case that is dealt
- with further down the call chain. */
+ while (start < end)
     {
+ svn_wc_conflict_description_t *conflict;
+
       SVN_ERR(read_one_tree_conflict(&conflict, &start, end, dir_path, pool));
       if (conflict != NULL)
         APR_ARRAY_PUSH(*conflicts, svn_wc_conflict_description_t *) = conflict;
+
+ /* *START should now point to a description separator
+ * if there are any descriptions left. */
+ if (start < end)
+ SVN_ERR(read_desc_separator(&start, end));
     }
 
- if (start != NULL)
- /* Not all conflicts have been read from the entry, but no error
- * has been thrown yet. We should not even be here! */
- return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
- _("Invalid tree conflict data in 'entries' file, "
- "but no idea what went wrong"));
-
   return SVN_NO_ERROR;
 }
 
@@ -384,6 +382,58 @@ write_string_field(svn_stringbuf_t *buf,
     }
 }
 
+/* Append to BUF the string corresponding to enumeration value N, as found
+ * in MAP. */
+static svn_error_t *
+write_enum_field(svn_stringbuf_t *buf,
+ const enum_mapping_t *map,
+ int n)
+{
+ int i;
+
+ for (i = 0; ; i++)
+ {
+ SVN_ERR_ASSERT(map[i].str != NULL);
+ if (map[i].val == n)
+ break;
+ }
+ svn_stringbuf_appendcstr(buf, map[i].str);
+ return SVN_NO_ERROR;
+}
+
+/* Append to BUF the denary form of the number N. */
+static void
+write_integer_field(svn_stringbuf_t *buf,
+ int n,
+ apr_pool_t *pool)
+{
+ const char *str = apr_psprintf(pool, "%d", n);
+
+ svn_stringbuf_appendcstr(buf, str);
+}
+
+/* Append to BUF the several fields that represent VERSION_INFO, */
+static svn_error_t *
+write_node_version_info(svn_stringbuf_t *buf,
+ const svn_wc_conflict_version_t *version_info,
+ apr_pool_t *pool)
+{
+ if (version_info->repos_url)
+ write_string_field(buf, version_info->repos_url);
+ svn_stringbuf_appendbytes(buf, &field_separator, 1);
+
+ if (SVN_IS_VALID_REVNUM(version_info->peg_rev))
+ write_integer_field(buf, version_info->peg_rev, pool);
+ svn_stringbuf_appendbytes(buf, &field_separator, 1);
+
+ if (version_info->path_in_repos)
+ write_string_field(buf, version_info->path_in_repos);
+ svn_stringbuf_appendbytes(buf, &field_separator, 1);
+
+ SVN_ERR(write_enum_field(buf, node_kind_map, version_info->node_kind));
+ return SVN_NO_ERROR;
+}
+
 /*
  * This function could be static, but we need to link to it
  * in a unit test in tests/libsvn_wc/, so it isn't.
@@ -402,82 +452,45 @@ svn_wc__write_tree_conflicts(char **conflict_data,
       const svn_wc_conflict_description_t *conflict =
           APR_ARRAY_IDX(conflicts, i, svn_wc_conflict_description_t *);
 
- /* Escape separator chars while writing victim path. */
+ /* Victim path (escaping separator chars). */
       path = svn_path_basename(conflict->path, pool);
       SVN_ERR_ASSERT(strlen(path) > 0);
       write_string_field(buf, path);
 
       svn_stringbuf_appendbytes(buf, &field_separator, 1);
 
- switch (conflict->node_kind)
- {
- case svn_node_dir:
- svn_stringbuf_appendcstr(buf, SVN_WC__NODE_DIR);
- break;
- case svn_node_file:
- svn_stringbuf_appendcstr(buf, SVN_WC__NODE_FILE);
- break;
- default:
- SVN_ERR_MALFUNCTION();
- }
+ /* node_kind */
+ SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir
+ || conflict->node_kind == svn_node_file);
+ SVN_ERR(write_enum_field(buf, node_kind_map, conflict->node_kind));
 
       svn_stringbuf_appendbytes(buf, &field_separator, 1);
 
- switch (conflict->operation)
- {
- case svn_wc_operation_update:
- svn_stringbuf_appendcstr(buf, SVN_WC__OPERATION_UPDATE);
- break;
- case svn_wc_operation_switch:
- svn_stringbuf_appendcstr(buf, SVN_WC__OPERATION_SWITCH);
- break;
- case svn_wc_operation_merge:
- svn_stringbuf_appendcstr(buf, SVN_WC__OPERATION_MERGE);
- break;
- default:
- SVN_ERR_MALFUNCTION();
- }
+ /* operation */
+ SVN_ERR(write_enum_field(buf, operation_map, conflict->operation));
 
       svn_stringbuf_appendbytes(buf, &field_separator, 1);
 
- switch (conflict->action)
- {
- case svn_wc_conflict_action_edit:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_ACTION_EDITED);
- break;
- case svn_wc_conflict_action_delete:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_ACTION_DELETED);
- break;
- case svn_wc_conflict_action_add:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_ACTION_ADDED);
- break;
- default:
- SVN_ERR_MALFUNCTION();
- }
+ /* action */
+ SVN_ERR(write_enum_field(buf, action_map, conflict->action));
 
       svn_stringbuf_appendbytes(buf, &field_separator, 1);
 
- switch (conflict->reason)
- {
- case svn_wc_conflict_reason_edited:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_REASON_EDITED);
- break;
- case svn_wc_conflict_reason_deleted:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_REASON_DELETED);
- break;
- case svn_wc_conflict_reason_added:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_REASON_ADDED);
- break;
- case svn_wc_conflict_reason_missing:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_REASON_MISSING);
- break;
- case svn_wc_conflict_reason_obstructed:
- svn_stringbuf_appendcstr(buf, SVN_WC__CONFLICT_REASON_OBSTRUCTED);
- break;
- default:
- SVN_ERR_MALFUNCTION();
- }
+ /* reason */
+ SVN_ERR(write_enum_field(buf, reason_map, conflict->reason));
 
+ svn_stringbuf_appendbytes(buf, &field_separator, 1);
+
+ /* older_version */
+ if (conflict->older_version)
+ SVN_ERR(write_node_version_info(buf, conflict->older_version, pool));
+
+ svn_stringbuf_appendbytes(buf, &field_separator, 1);
+
+ /* their_version */
+ if (conflict->their_version)
+ SVN_ERR(write_node_version_info(buf, conflict->their_version, pool));
+
       if (i < (conflicts->nelts - 1))
         svn_stringbuf_appendbytes(buf, &desc_separator, 1);
     }
Index: subversion/libsvn_wc/log.c
===================================================================
--- subversion/libsvn_wc/log.c (.../trunk) (revision 34356)
+++ subversion/libsvn_wc/log.c (.../branches/tc_url_rev) (revision 34365)
@@ -571,10 +571,10 @@ log_do_merge(struct log_runner *loggy,
 
   /* Now do the merge with our full paths. */
   err = svn_wc__merge_internal(&log_accum, &merge_outcome,
- left, right, name, NULL, loggy->adm_access,
- left_label, right_label, target_label,
- FALSE, loggy->diff3_cmd, NULL, NULL,
- NULL, NULL, loggy->pool);
+ left, NULL, right, NULL, name, NULL,
+ loggy->adm_access, left_label, right_label,
+ target_label, FALSE, loggy->diff3_cmd, NULL,
+ NULL, NULL, NULL, loggy->pool);
   if (err && loggy->rerun && APR_STATUS_IS_ENOENT(err->apr_err))
     {
       svn_error_clear(err);
Index: subversion/libsvn_client/merge.c
===================================================================
--- subversion/libsvn_client/merge.c (.../trunk) (revision 34356)
+++ subversion/libsvn_client/merge.c (.../branches/tc_url_rev) (revision 34365)
@@ -330,8 +330,10 @@ is_path_conflicted_by_merge(merge_cmd_baton_t *mer
  * ADM_ACCESS corresponds to the tree-conflicted directory
  * This directory must be the victim's parent directory.
  *
- * NODE_KIND, ACTION, and REASON correspond to the fields
- * of the same names in svn_wc_conflict_description_t.
+ * NODE_KIND must be the node kind of "old" and "theirs" and "mine";
+ * this function cannot cope with node kind clashes.
+ * ACTION and REASON correspond to the fields
+ * of the same names in svn_wc_tree_conflict_description_t.
  */
 static svn_error_t*
 tree_conflict(merge_cmd_baton_t *merge_b,
@@ -342,14 +344,42 @@ tree_conflict(merge_cmd_baton_t *merge_b,
               svn_wc_conflict_reason_t reason)
 {
   svn_wc_conflict_description_t *conflict;
+ const char *src_repos_url; /* root URL of source repository */
 
   if (merge_b->record_only || merge_b->dry_run)
     return SVN_NO_ERROR;
 
   conflict = svn_wc_conflict_description_create_tree(
- victim_path, adm_access, node_kind, svn_wc_operation_merge, merge_b->pool);
+ victim_path, adm_access, node_kind, svn_wc_operation_merge,
+ apr_palloc(merge_b->pool, sizeof(svn_wc_conflict_version_t)),
+ apr_palloc(merge_b->pool, sizeof(svn_wc_conflict_version_t)),
+ merge_b->pool);
   conflict->action = action;
   conflict->reason = reason;
+
+ SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session1, &src_repos_url,
+ merge_b->pool));
+
+ if (conflict->older_version)
+ {
+ conflict->older_version->repos_url = src_repos_url;
+ conflict->older_version->peg_rev = merge_b->merge_source.rev1;
+ conflict->older_version->path_in_repos
+ = svn_path_is_child(src_repos_url, merge_b->merge_source.url1,
+ merge_b->pool);
+ conflict->older_version->node_kind = node_kind;
+ }
+
+ if (conflict->their_version)
+ {
+ conflict->their_version->repos_url = src_repos_url;
+ conflict->their_version->peg_rev = merge_b->merge_source.rev2;
+ conflict->their_version->path_in_repos
+ = svn_path_is_child(src_repos_url, merge_b->merge_source.url2,
+ merge_b->pool);
+ conflict->their_version->node_kind = node_kind;
+ }
+
   SVN_ERR(svn_wc__add_tree_conflict(conflict, adm_access, merge_b->pool));
   return SVN_NO_ERROR;
 }

Property changes on: .
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /trunk:r34277-34355

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe_at_subversion.tigris.org
For additional commands, e-mail: dev-help_at_subversion.tigris.org
Received on 2008-11-23 18:37:10 CET

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

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.