Provide a unified API for reading WC versions of a given node: the "base" and "working" versions for which inconsistent APIs were already available, and the three conflict artifact versions "old" and "new" and "mine" for which no APIs were available other than getting the names of the files. Start storing property conflict artifacts (the old/new/mine versions) in the WC admin area when conflicts occur, and provide access through this mechanism. ### WORK IN PROGRESS. Far from complete, and some tests are broken. Index: subversion/include/svn_opt.h =================================================================== --- subversion/include/svn_opt.h (revision 32216) +++ subversion/include/svn_opt.h (working copy) @@ -346,7 +346,16 @@ enum svn_opt_revision_kind { svn_opt_revision_working, /** repository youngest */ - svn_opt_revision_head + svn_opt_revision_head, + + /** conflict artifact: left side of incoming change */ + svn_opt_revision_old, + + /** conflict artifact: right side of incoming change */ + svn_opt_revision_new, + + /** conflict artifact: what the working version was before the conflict */ + svn_opt_revision_mine }; /** Index: subversion/include/svn_wc.h =================================================================== --- subversion/include/svn_wc.h (revision 32216) +++ subversion/include/svn_wc.h (working copy) @@ -1145,6 +1145,8 @@ typedef struct svn_wc_conflict_descripti * property values are technically binary values, and thus can't * always be merged.) */ + /* ### These three may be obsoleted by ability to request + * wrk/base/old/theirs/mine from any WC functions. */ const char *base_file; /* common ancestor of the two files being merged */ /** their version of the file */ @@ -1592,6 +1594,7 @@ svn_wc_check_wc(const char *path, svn_error_t * svn_wc_has_binary_prop(svn_boolean_t *has_binary_prop, const char *path, + /* ### want svn_wc_rev_kind_t? */ svn_wc_adm_access_t *adm_access, apr_pool_t *pool); @@ -2667,6 +2670,7 @@ svn_wc_status_set_repos_locks(void *set_ */ svn_error_t * svn_wc_copy2(const char *src, + /* ### want svn_wc_rev_kind_t? */ svn_wc_adm_access_t *dst_parent, const char *dst_basename, svn_cancel_func_t cancel_func, @@ -3658,12 +3662,42 @@ svn_wc_get_switch_editor(svn_revnum_t *t * difference. */ +/* A type to select one of the versions that the WC holds of a node. Types + * 'pristine' and 'working' are meaningful unless the node is schedule-add + * or schedule-delete respectively. The three 'conflict_*' types are + * meaningful only while the node is in a state of conflict. */ +typedef enum svn_wc_rev_kind_t +{ + svn_wc_rev_kind_pristine, /* the 'pristine' or 'base' version */ + svn_wc_rev_kind_working, /* the version that can be locally edited */ + svn_wc_rev_kind_conflict_old, /* the incoming change's 'old' or 'left' side */ + svn_wc_rev_kind_conflict_new, /* the incoming change's 'new' or 'right' side */ + svn_wc_rev_kind_conflict_mine /* what was 'working' before the conflict */ +} svn_wc_rev_kind_t; + /** Set @a *props to a hash table mapping char * names onto * svn_string_t * values for all the regular properties of - * @a path. Allocate the table, names, and values in @a pool. If - * the node has no properties, or does not exist in the working copy, - * then an empty hash is returned. @a adm_access is an access baton - * set that contains @a path. + * @a path. Allocate the table, names, and values in @a pool. + * If the @a rev_kind version of the node exists and has no properties, + * return an empty hash. + * If the @a rev_kind version of the node does not logically exist (for + * example, is scheduled for deletion, or is the base of an item scheduled + * for addition, or is a conflict artifact of an item not in conflict), + * return an error. + * @a adm_access is an access baton set that contains @a path. + * @a rev_kind specifies which version of the node to access: the pristine + * base, the working version, or one of the conflict artifacts. + */ +svn_error_t * +svn_wc_prop_list2(apr_hash_t **props, + const char *path, + svn_wc_rev_kind_t rev_kind, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool); + +/** Like svn_wc_prop_list2() except that only the working properties can be + * obtained, and if the node does not exist in the working copy then an + * empty hash is returned. */ svn_error_t * svn_wc_prop_list(apr_hash_t **props, @@ -3679,6 +3713,17 @@ svn_wc_prop_list(apr_hash_t **props, * baton set that contains @a path. */ svn_error_t * +svn_wc_prop_get2(const svn_string_t **value, + const char *name, + const char *path, + svn_wc_rev_kind_t rev_kind, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool); + +/** Like svn_wc_prop_get2() except only the working properties can be + * obtained. + */ +svn_error_t * svn_wc_prop_get(const svn_string_t **value, const char *name, const char *path, @@ -3826,8 +3871,8 @@ svn_wc_canonicalize_svn_prop(const svn_s * @a ignore_ancestry is @c FALSE, then any discontinuous node ancestry will * result in the diff given as a full delete followed by an add. * - * If @a use_text_base is TRUE, then compare the repository against - * the working copy's text-base files, rather than the working files. + * Compare the repository against the working copy version specified by + * @a wc_rev_kind. * * Normally, the difference from repository->working_copy is shown. * If @a reverse_order is TRUE, then show working_copy->repository diffs. @@ -3844,6 +3889,22 @@ svn_wc_canonicalize_svn_prop(const svn_s * @since New in 1.6. */ svn_error_t * +svn_wc_get_diff_editor5a(svn_wc_adm_access_t *anchor, + const char *target, + const svn_wc_diff_callbacks3_t *callbacks, + void *callback_baton, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_wc_rev_kind_t rev_kind, + svn_boolean_t reverse_order, + svn_cancel_func_t cancel_func, + void *cancel_baton, + const apr_array_header_t *changelists, + const svn_delta_editor_t **editor, + void **edit_baton, + apr_pool_t *pool); +/* ### In v1.6, combine svn_wc_get_diff_editor5a() with ...5() as ...5(). */ +svn_error_t * svn_wc_get_diff_editor5(svn_wc_adm_access_t *anchor, const char *target, const svn_wc_diff_callbacks3_t *callbacks, @@ -3950,10 +4011,14 @@ svn_wc_get_diff_editor(svn_wc_adm_access /** - * Compare working copy against the text-base. + * Compare one version of a WC node against another version of it, sending + * the results through the svn_wc_diff_callbacks3_t callback interface. * * @a anchor/@a target represent the base of the hierarchy to be compared. * + * @a rev_kind_from and @a rev_kind_to specify the left and right side of + * the diff respectively. + * * @a callbacks/@a callback_baton is the callback table to use when two * files are to be compared. * @@ -3979,6 +4044,18 @@ svn_wc_get_diff_editor(svn_wc_adm_access * @since New in 1.6. */ svn_error_t * +svn_wc_diff5a(svn_wc_adm_access_t *anchor, + const char *target, + svn_wc_rev_kind_t rev_kind_from, + svn_wc_rev_kind_t rev_kind_to, + const svn_wc_diff_callbacks3_t *callbacks, + void *callback_baton, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + const apr_array_header_t *changelists, + apr_pool_t *pool); +/* ### In v1.6, combine svn_wc_diff5a() with ...5() as ...5(). */ +svn_error_t * svn_wc_diff5(svn_wc_adm_access_t *anchor, const char *target, const svn_wc_diff_callbacks3_t *callbacks, @@ -3990,7 +4067,8 @@ svn_wc_diff5(svn_wc_adm_access_t *anchor /** * Similar to svn_wc_diff5(), but with a @c svn_wc_diff_callbacks2_t argument - * instead of @c svn_wc_diff_callbacks3_t. + * instead of @c svn_wc_diff_callbacks3_t, and the left and right side of + * the diff being BASE and WORKING respectively. * * @deprecated Provided for backward compatibility with the 1.5 API. */ @@ -4070,6 +4148,7 @@ svn_error_t * svn_wc_get_prop_diffs(apr_array_header_t **propchanges, apr_hash_t **original_props, const char *path, + /* ### want svn_wc_rev_kind_t? */ svn_wc_adm_access_t *adm_access, apr_pool_t *pool); @@ -4301,6 +4380,19 @@ svn_wc_get_pristine_copy_path(const char const char **pristine_path, apr_pool_t *pool); +/** Given a @a path to a wc file, set @a text_path to the path of the + * version of the file specified by @a rev_kind. + * If the specified version does not exist, set @a text_path to NULL. + * @a adm_access is an access baton for @a path. + * Perform all allocations in @a pool. + */ +svn_error_t * +svn_wc_get_text_path(const char **text_path, + const char *path, + svn_wc_rev_kind_t rev_kind, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool); + /** * Recurse from @a path, cleaning up unfinished log business. Perform Index: subversion/libsvn_client/cat.c =================================================================== --- subversion/libsvn_client/cat.c (revision 32216) +++ subversion/libsvn_client/cat.c (working copy) @@ -39,10 +39,9 @@ /*** Code. ***/ -/* Helper function to handle copying a potentially translated version of - local file PATH to OUTPUT. REVISION must be one of the following: BASE, - COMMITTED, WORKING, or UNSPECIFIED. If the revision is UNSPECIFIED, it - will default to BASE. Uses POOL for temporary allocations. */ +/* Copy a potentially translated version of local file PATH to OUTPUT. + * REVISION must be of a kind that the WC can provide locally, and must not + * be UNSPECIFIED. Use POOL for temporary allocations. */ static svn_error_t * cat_local_file(const char *path, svn_stream_t *output, @@ -61,11 +60,10 @@ cat_local_file(const char *path, apr_time_t tm; apr_file_t *input_file; svn_stream_t *input; + svn_wc_rev_kind_t wc_rev_kind; - SVN_ERR_ASSERT(revision->kind == svn_opt_revision_working || - revision->kind == svn_opt_revision_base || - revision->kind == svn_opt_revision_committed || - revision->kind == svn_opt_revision_unspecified); + SVN_ERR_ASSERT(SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision)); + wc_rev_kind = svn_cl__wc_rev_kind_from_rev(revision); SVN_ERR(svn_wc__entry_versioned(&entry, path, adm_access, FALSE, pool)); @@ -74,17 +72,17 @@ cat_local_file(const char *path, _("'%s' refers to a directory"), svn_path_local_style(path, pool)); - if (revision->kind != svn_opt_revision_working) - { - SVN_ERR(svn_wc_get_pristine_copy_path(path, &base, pool)); - SVN_ERR(svn_wc_get_prop_diffs(NULL, &props, path, adm_access, pool)); - } - else + /* Get the text and properties */ + SVN_ERR(svn_wc_get_text_path(&base, path, wc_rev_kind, adm_access, pool)); + SVN_ERR(svn_wc_prop_list2(&props, path, wc_rev_kind, adm_access, pool)); + + /* Find out whether we're looking at a locally modified version. (If so, + * then we won't know the author or revision number.) */ + /* ### Update for svn_opt_revision_old/new/mine */ + if (revision->kind == svn_opt_revision_working) { svn_wc_status2_t *status; - base = path; - SVN_ERR(svn_wc_prop_list(&props, path, adm_access, pool)); SVN_ERR(svn_wc_status2(&status, path, adm_access, pool)); if (status->text_status != svn_wc_status_normal) local_mod = TRUE; @@ -102,7 +100,7 @@ cat_local_file(const char *path, if (local_mod && (! special)) { - /* Use the modified time from the working copy if + /* Use the modified time from the working copy of the file */ SVN_ERR(svn_io_file_affected_time(&tm, path, pool)); } @@ -169,15 +167,12 @@ svn_client_cat2(svn_stream_t *out, const char *url; svn_stream_t *output = out; + peg_revision = svn_cl__rev_default_to_head_or_base(peg_revision, path_or_url); + revision = svn_cl__rev_default_to_head_or_base(revision, path_or_url); + if (! svn_path_is_url(path_or_url) - && (peg_revision->kind == svn_opt_revision_base - || peg_revision->kind == svn_opt_revision_working - || peg_revision->kind == svn_opt_revision_committed - || peg_revision->kind == svn_opt_revision_unspecified) - && (revision->kind == svn_opt_revision_base - || revision->kind == svn_opt_revision_working - || revision->kind == svn_opt_revision_committed - || revision->kind == svn_opt_revision_unspecified)) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision)) { svn_wc_adm_access_t *adm_access; Index: subversion/libsvn_client/client.h =================================================================== --- subversion/libsvn_client/client.h (revision 32216) +++ subversion/libsvn_client/client.h (working copy) @@ -333,7 +333,7 @@ svn_error_t * svn_client__get_prop_from_wc(apr_hash_t *props, const char *propname, const char *target, - svn_boolean_t pristine, + const svn_opt_revision_t *revision, const svn_wc_entry_t *entry, svn_wc_adm_access_t *adm_access, svn_depth_t depth, @@ -1069,9 +1069,48 @@ svn_client__ensure_revprop_table(apr_has (((kind) == svn_opt_revision_base || \ (kind) == svn_opt_revision_previous || \ (kind) == svn_opt_revision_working || \ - (kind) == svn_opt_revision_committed) \ + (kind) == svn_opt_revision_committed || \ + (kind) == svn_opt_revision_old || \ + (kind) == svn_opt_revision_new || \ + (kind) == svn_opt_revision_mine) \ ? TRUE : FALSE) +/* Return true iff *REVISION is of a kind that the WC can supply without + * reference to the repository. + * The result is unspecified if REVISION->kind is 'unspecified'. */ +#define SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision) \ + ((revision)->kind == svn_opt_revision_base || \ + (revision)->kind == svn_opt_revision_working || \ + (revision)->kind == svn_opt_revision_committed || \ + (revision)->kind == svn_opt_revision_old || \ + (revision)->kind == svn_opt_revision_new || \ + (revision)->kind == svn_opt_revision_mine) + +/* Return REVISION unless its kind is 'unspecified' in which case return + * a pointer to a statically allocated revision structure of kind 'head' + * if PATH_OR_URL is a URL or 'base' if it is a WC path. */ +const svn_opt_revision_t * +svn_cl__rev_default_to_head_or_base(const svn_opt_revision_t *revision, + const char *path_or_url); + +/* Return REVISION unless its kind is 'unspecified' in which case return + * a pointer to a statically allocated revision structure of kind 'head' + * if PATH_OR_URL is a URL or 'working' if it is a WC path. */ +const svn_opt_revision_t * +svn_cl__rev_default_to_head_or_working(const svn_opt_revision_t *revision, + const char *path_or_url); + +/* Return the WC revision kind corresponding to *REVISION which must be of a + * kind that the WC can supply, as determined by SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(). */ +svn_wc_rev_kind_t +svn_cl__wc_rev_kind_from_rev(const svn_opt_revision_t *revision); + +/* Return true iff the item *ENTRY is scheduled to logically exists in the + * version specified by *REVISION (which must be of a "local" kind). */ +svn_boolean_t +svn_cl__item_exists_in_local_revision(const svn_wc_entry_t *entry, + const svn_opt_revision_t *revision); + #ifdef __cplusplus } Index: subversion/libsvn_client/diff.c =================================================================== --- subversion/libsvn_client/diff.c (revision 32216) +++ subversion/libsvn_client/diff.c (working copy) @@ -906,11 +906,9 @@ check_paths(const struct diff_parameters /* Revisions can be said to be local or remote. BASE and WORKING, for example, are local. */ is_local_rev1 = - ((params->revision1->kind == svn_opt_revision_base) - || (params->revision1->kind == svn_opt_revision_working)); + SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(params->revision1); is_local_rev2 = - ((params->revision2->kind == svn_opt_revision_base) - || (params->revision2->kind == svn_opt_revision_working)); + SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(params->revision2); if (params->peg_revision->kind != svn_opt_revision_unspecified) { @@ -1132,14 +1130,12 @@ diff_wc_wc(const char *path1, /* Currently we support only the case where path1 and path2 are the same path. */ - if ((strcmp(path1, path2) != 0) - || (! ((revision1->kind == svn_opt_revision_base) - && (revision2->kind == svn_opt_revision_working)))) + if (strcmp(path1, path2) != 0) return unsupported_diff_error (svn_error_create (SVN_ERR_INCORRECT_PARAMS, NULL, - _("Only diffs between a path's text-base " - "and its working files are supported at this time"))); + _("Diffs between different working copy paths " + "are not supported at this time"))); SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &target_access, &target, path1, FALSE, levels_to_lock, @@ -1151,8 +1147,11 @@ diff_wc_wc(const char *path1, (&callback_baton->revnum1, NULL, NULL, revision1, path1, pool)); callback_baton->revnum2 = SVN_INVALID_REVNUM; /* WC */ - SVN_ERR(svn_wc_diff5(adm_access, target, callbacks, callback_baton, - depth, ignore_ancestry, changelists, pool)); + SVN_ERR(svn_wc_diff5a(adm_access, target, + svn_cl__wc_rev_kind_from_rev(revision1), + svn_cl__wc_rev_kind_from_rev(revision2), + callbacks, callback_baton, + depth, ignore_ancestry, changelists, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); return SVN_NO_ERROR; } Index: subversion/libsvn_client/export.c =================================================================== --- subversion/libsvn_client/export.c (revision 32216) +++ subversion/libsvn_client/export.c (working copy) @@ -103,42 +103,31 @@ copy_one_versioned_file(const char *from svn_subst_eol_style_t style; apr_hash_t *props; const char *base; - svn_string_t *eol_style, *keywords, *executable, *externals, *special; + svn_string_t *eol_style, *keywords, *executable, *special; const char *eol = NULL; svn_boolean_t local_mod = FALSE; apr_time_t tm; + svn_wc_rev_kind_t wc_rev_kind; + + wc_rev_kind = svn_cl__wc_rev_kind_from_rev(revision); SVN_ERR(svn_wc_entry(&entry, from, adm_access, FALSE, pool)); - /* Only export 'added' files when the revision is WORKING. - Otherwise, skip the 'added' files, since they didn't exist - in the BASE revision and don't have an associated text-base. - - Don't export 'deleted' files and directories unless it's a - revision other than WORKING. These files and directories - don't really exist in WORKING. */ - if ((revision->kind != svn_opt_revision_working && - entry->schedule == svn_wc_schedule_add) || - (revision->kind == svn_opt_revision_working && - entry->schedule == svn_wc_schedule_delete)) + /* Don't export the 'working' version of an item that is scheduled for + deletion, or the 'base' version of an item that is scheduled for + addition, as these versions are not deemed to really exist. */ + if (! svn_cl__item_exists_in_local_revision(entry, revision)) return SVN_NO_ERROR; - if (revision->kind != svn_opt_revision_working) - { - SVN_ERR(svn_wc_get_pristine_copy_path(from, &base, - pool)); - SVN_ERR(svn_wc_get_prop_diffs(NULL, &props, from, - adm_access, pool)); - } - else + /* Get the text and properties */ + SVN_ERR(svn_wc_get_text_path(&base, from, wc_rev_kind, adm_access, pool)); + SVN_ERR(svn_wc_prop_list2(&props, from, wc_rev_kind, adm_access, pool)); + /* ### Update for svn_opt_revision_old/new/mine */ + if (revision->kind == svn_opt_revision_working) { svn_wc_status2_t *status; - base = from; - SVN_ERR(svn_wc_prop_list(&props, from, - adm_access, pool)); - SVN_ERR(svn_wc_status2(&status, from, - adm_access, pool)); + SVN_ERR(svn_wc_status2(&status, from, adm_access, pool)); if (status->text_status != svn_wc_status_normal) local_mod = TRUE; } @@ -149,8 +138,6 @@ copy_one_versioned_file(const char *from APR_HASH_KEY_STRING); executable = apr_hash_get(props, SVN_PROP_EXECUTABLE, APR_HASH_KEY_STRING); - externals = apr_hash_get(props, SVN_PROP_EXTERNALS, - APR_HASH_KEY_STRING); special = apr_hash_get(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING); @@ -233,17 +220,10 @@ copy_versioned_files(const char *from, SVN_ERR(svn_wc__entry_versioned(&entry, from, adm_access, FALSE, pool)); - /* Only export 'added' files when the revision is WORKING. - Otherwise, skip the 'added' files, since they didn't exist - in the BASE revision and don't have an associated text-base. - - Don't export 'deleted' files and directories unless it's a - revision other than WORKING. These files and directories - don't really exist in WORKING. */ - if ((revision->kind != svn_opt_revision_working && - entry->schedule == svn_wc_schedule_add) || - (revision->kind == svn_opt_revision_working && - entry->schedule == svn_wc_schedule_delete)) + /* Don't export the 'working' version of an item that is scheduled for + deletion, or the 'base' version of an item that is scheduled for + addition, as these versions are not deemed to really exist. */ + if (! svn_cl__item_exists_in_local_revision(entry, revision)) return SVN_NO_ERROR; if (entry->kind == svn_node_dir) @@ -799,10 +779,8 @@ svn_client_export4(svn_revnum_t *result_ const char *url; if (svn_path_is_url(from) || - ! (revision->kind == svn_opt_revision_base || - revision->kind == svn_opt_revision_committed || - revision->kind == svn_opt_revision_working || - revision->kind == svn_opt_revision_unspecified)) + ! (SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision) + || revision->kind == svn_opt_revision_unspecified)) { svn_revnum_t revnum; svn_ra_session_t *ra_session; Index: subversion/libsvn_client/mergeinfo.c =================================================================== --- subversion/libsvn_client/mergeinfo.c (revision 32216) +++ subversion/libsvn_client/mergeinfo.c (working copy) @@ -53,12 +53,16 @@ svn_client__parse_mergeinfo(svn_mergeinf { apr_hash_t *props = apr_hash_make(pool); const svn_string_t *propval; + /* ### Shouldn't be here. Make the caller pass in a revision. */ + static const svn_opt_revision_t base_rev = { svn_opt_revision_base, { 0 }}; + static const svn_opt_revision_t wrk_rev = { svn_opt_revision_working, { 0 }}; /* ### Use svn_wc_prop_get() would actually be sufficient for now. ### DannyB thinks that later we'll need behavior more like ### svn_client__get_prop_from_wc(). */ SVN_ERR(svn_client__get_prop_from_wc(props, SVN_PROP_MERGEINFO, - wcpath, pristine, entry, adm_access, + wcpath, pristine ? &base_rev : &wrk_rev, + entry, adm_access, svn_depth_empty, NULL, ctx, pool)); propval = apr_hash_get(props, wcpath, APR_HASH_KEY_STRING); if (propval) @@ -370,7 +374,9 @@ svn_client__get_wc_or_repos_mergeinfo(sv assume the mergeinfo to be NULL. */ SVN_ERR(svn_client__get_prop_from_wc(props, SVN_PROP_MERGEINFO, - target_wcpath, TRUE, entry, + target_wcpath, + svn_wc_rev_kind_pristine, + entry, adm_access, svn_depth_empty, NULL, ctx, pool)); if (apr_hash_get(props, target_wcpath, APR_HASH_KEY_STRING) == NULL) Index: subversion/libsvn_client/prop_commands.c =================================================================== --- subversion/libsvn_client/prop_commands.c (revision 32216) +++ subversion/libsvn_client/prop_commands.c (working copy) @@ -22,6 +22,8 @@ /*** Includes. ***/ +#include + #define APR_WANT_STRFUNC #include @@ -552,8 +554,9 @@ svn_client_revprop_set(const char *propn } -/* Set *PROPS to the pristine (base) properties at PATH, if PRISTINE - * is true, or else the working value if PRISTINE is false. +/* Set *PROPS to the pristine/working/conflicted version of the properties + * at PATH, according to the value of WC_REV_KIND. If PATH has no such version, + * then set *PROPS to null ### or return an error. * * The keys of *PROPS will be 'const char *' property names, and the * values 'const svn_string_t *' property values. Allocate *PROPS @@ -562,43 +565,33 @@ svn_client_revprop_set(const char *propn static svn_error_t * pristine_or_working_props(apr_hash_t **props, const char *path, + const svn_opt_revision_t *revision, svn_wc_adm_access_t *adm_access, - svn_boolean_t pristine, apr_pool_t *pool) { - if (pristine) - SVN_ERR(svn_wc_get_prop_diffs(NULL, props, path, adm_access, pool)); - else - SVN_ERR(svn_wc_prop_list(props, path, adm_access, pool)); - - return SVN_NO_ERROR; + svn_wc_rev_kind_t wc_rev_kind = svn_cl__wc_rev_kind_from_rev(revision); + return svn_wc_prop_list2(props, path, wc_rev_kind, adm_access, pool); } -/* Set *PROPVAL to the pristine (base) value of property PROPNAME at - * PATH, if PRISTINE is true, or else the working value if PRISTINE is - * false. Allocate *PROPVAL in POOL. +/* Set *PROPVAL to the pristine/working/conflicted value of property PROPNAME + * at PATH, according to the value of WC_REV_KIND. If PATH does not exist at the + * revision kind WC_REV_KIND then set *PROPVAL to null ### or return an error. + * Allocate *PROPVAL in POOL. */ static svn_error_t * pristine_or_working_propval(const svn_string_t **propval, const char *propname, const char *path, svn_wc_adm_access_t *adm_access, - svn_boolean_t pristine, + const svn_opt_revision_t *revision, apr_pool_t *pool) { - if (pristine) - { - apr_hash_t *pristine_props; + apr_hash_t *props; - SVN_ERR(svn_wc_get_prop_diffs(NULL, &pristine_props, path, adm_access, + SVN_ERR(pristine_or_working_props(&props, path, revision, adm_access, pool)); - *propval = apr_hash_get(pristine_props, propname, APR_HASH_KEY_STRING); - } - else /* get the working revision */ - { - SVN_ERR(svn_wc_prop_get(propval, propname, path, adm_access, pool)); - } + *propval = apr_hash_get(props, propname, APR_HASH_KEY_STRING); return SVN_NO_ERROR; } @@ -608,7 +601,7 @@ pristine_or_working_propval(const svn_st struct propget_walk_baton { const char *propname; /* The name of the property to get. */ - svn_boolean_t pristine; /* Select base rather than working props. */ + const svn_opt_revision_t *revision; /* Which version of the props. */ svn_wc_adm_access_t *base_access; /* Access for the tree being walked. */ apr_hash_t *changelist_hash; /* Keys are changelists to filter on. */ apr_hash_t *props; /* Out: mapping of (path:propval). */ @@ -619,7 +612,7 @@ struct propget_walk_baton * For the path given by PATH and ENTRY, * populate wb->PROPS with the values of property wb->PROPNAME, * where "wb" is the WALK_BATON of type "struct propget_walk_baton *". - * If wb->PRISTINE is true, use the base value, else use the working value. + * Get the pristine/working/conflict values, according to wb->WC_REV_KIND. * * The keys of wb->PROPS will be 'const char *' paths, rooted at the * path svn_wc_adm_access_path(ADM_ACCESS), and the values are @@ -642,8 +635,7 @@ propget_walk_cb(const char *path, return SVN_NO_ERROR; /* Ignore the entry if it does not exist at the time of interest. */ - if (entry->schedule - == (wb->pristine ? svn_wc_schedule_add : svn_wc_schedule_delete)) + if (! svn_cl__item_exists_in_local_revision(entry, wb->revision)) return SVN_NO_ERROR; /* If our entry doesn't pass changelist filtering, get outta here. */ @@ -651,7 +643,7 @@ propget_walk_cb(const char *path, return SVN_NO_ERROR; SVN_ERR(pristine_or_working_propval(&propval, wb->propname, path, - wb->base_access, wb->pristine, + wb->base_access, wb->revision, pool)); if (propval) @@ -811,7 +803,7 @@ svn_error_t * svn_client__get_prop_from_wc(apr_hash_t *props, const char *propname, const char *target, - svn_boolean_t pristine, + const svn_opt_revision_t *revision, const svn_wc_entry_t *entry, svn_wc_adm_access_t *adm_access, svn_depth_t depth, @@ -836,7 +828,7 @@ svn_client__get_prop_from_wc(apr_hash_t depth = svn_depth_infinity; wb.propname = propname; - wb.pristine = pristine; + wb.revision = revision; wb.base_access = adm_access; wb.changelist_hash = changelist_hash; wb.props = props; @@ -857,7 +849,7 @@ svn_client__get_prop_from_wc(apr_hash_t return SVN_NO_ERROR; } -/* Note: this implementation is very similar to svn_client_proplist. */ +/* Note: this implementation is very similar to svn_client_proplist(). */ svn_error_t * svn_client_propget3(apr_hash_t **props, const char *propname, @@ -874,21 +866,17 @@ svn_client_propget3(apr_hash_t **props, SVN_ERR(error_if_wcprop_name(propname)); + peg_revision = svn_cl__rev_default_to_head_or_base(peg_revision, path_or_url); + revision = svn_cl__rev_default_to_head_or_base(revision, path_or_url); + *props = apr_hash_make(pool); if (! svn_path_is_url(path_or_url) - && (peg_revision->kind == svn_opt_revision_base - || peg_revision->kind == svn_opt_revision_working - || peg_revision->kind == svn_opt_revision_committed - || peg_revision->kind == svn_opt_revision_unspecified) - && (revision->kind == svn_opt_revision_base - || revision->kind == svn_opt_revision_working - || revision->kind == svn_opt_revision_committed - || revision->kind == svn_opt_revision_unspecified)) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision)) { svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *node; - svn_boolean_t pristine; int adm_lock_level = SVN_WC__LEVELS_TO_LOCK_FROM_DEPTH(depth); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path_or_url, @@ -901,12 +889,8 @@ svn_client_propget3(apr_hash_t **props, SVN_ERR(svn_client__get_revision_number (&revnum, NULL, NULL, revision, path_or_url, pool)); - /* If FALSE, we must want the working revision. */ - pristine = (revision->kind == svn_opt_revision_committed - || revision->kind == svn_opt_revision_base); - SVN_ERR(svn_client__get_prop_from_wc(*props, propname, path_or_url, - pristine, node, adm_access, + revision, node, adm_access, depth, changelists, ctx, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); @@ -1157,7 +1141,7 @@ remote_proplist(const char *target_prefi /* A baton for proplist_walk_cb. */ struct proplist_walk_baton { - svn_boolean_t pristine; /* Select base rather than working props. */ + const svn_opt_revision_t *revision; /* Which version of the props. */ svn_wc_adm_access_t *base_access; /* Access for the tree being walked. */ apr_hash_t *changelist_hash; /* Keys are changelists to filter on. */ svn_proplist_receiver_t receiver; /* Proplist receiver to call. */ @@ -1169,7 +1153,7 @@ struct proplist_walk_baton * For the path given by PATH and ENTRY, * populate wb->PROPS with a svn_client_proplist_item_t for each path, * where "wb" is the WALK_BATON of type "struct proplist_walk_baton *". - * If wb->PRISTINE is true, use the base values, else use the working values. + * Get the pristine/working/conflict values, according to wb->WC_REV_KIND. */ static svn_error_t * proplist_walk_cb(const char *path, @@ -1188,8 +1172,7 @@ proplist_walk_cb(const char *path, return SVN_NO_ERROR; /* Ignore the entry if it does not exist at the time of interest. */ - if (entry->schedule - == (wb->pristine ? svn_wc_schedule_add : svn_wc_schedule_delete)) + if (! svn_cl__item_exists_in_local_revision(entry, wb->revision)) return SVN_NO_ERROR; /* If our entry doesn't pass changelist filtering, get outta here. */ @@ -1198,8 +1181,8 @@ proplist_walk_cb(const char *path, path = apr_pstrdup(pool, path); - SVN_ERR(pristine_or_working_props(&hash, path, wb->base_access, - wb->pristine, pool)); + SVN_ERR(pristine_or_working_props(&hash, path, wb->revision, + wb->base_access, pool)); SVN_ERR(call_receiver(path, hash, wb->receiver, wb->receiver_baton, pool)); @@ -1225,17 +1208,13 @@ svn_client_proplist3(const char *path_or if (depth == svn_depth_unknown) depth = svn_depth_empty; + peg_revision = svn_cl__rev_default_to_head_or_base(peg_revision, path_or_url); + revision = svn_cl__rev_default_to_head_or_base(revision, path_or_url); + if (! svn_path_is_url(path_or_url) - && (peg_revision->kind == svn_opt_revision_base - || peg_revision->kind == svn_opt_revision_working - || peg_revision->kind == svn_opt_revision_committed - || peg_revision->kind == svn_opt_revision_unspecified) - && (revision->kind == svn_opt_revision_base - || revision->kind == svn_opt_revision_working - || revision->kind == svn_opt_revision_committed - || revision->kind == svn_opt_revision_unspecified)) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision)) { - svn_boolean_t pristine; int levels_to_lock = SVN_WC__LEVELS_TO_LOCK_FROM_DEPTH(depth); const svn_wc_entry_t *entry; apr_hash_t *changelist_hash = NULL; @@ -1247,16 +1226,6 @@ svn_client_proplist3(const char *path_or SVN_ERR(svn_wc__entry_versioned(&entry, path_or_url, adm_access, FALSE, pool)); - if ((revision->kind == svn_opt_revision_committed) - || (revision->kind == svn_opt_revision_base)) - { - pristine = TRUE; - } - else /* must be the working revision */ - { - pristine = FALSE; - } - if (changelists && changelists->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); @@ -1269,7 +1238,7 @@ svn_client_proplist3(const char *path_or struct proplist_walk_baton wb; wb.base_access = adm_access; - wb.pristine = pristine; + wb.revision = revision; wb.changelist_hash = changelist_hash; wb.receiver = receiver; wb.receiver_baton = receiver_baton; @@ -1283,8 +1252,8 @@ svn_client_proplist3(const char *path_or { apr_hash_t *hash; - SVN_ERR(pristine_or_working_props(&hash, path_or_url, adm_access, - pristine, pool)); + SVN_ERR(pristine_or_working_props(&hash, path_or_url, revision, + adm_access, pool)); SVN_ERR(call_receiver(path_or_url, hash, receiver, receiver_baton, pool)); Index: subversion/libsvn_client/revisions.c =================================================================== --- subversion/libsvn_client/revisions.c (revision 32216) +++ subversion/libsvn_client/revisions.c (working copy) @@ -73,6 +73,9 @@ svn_client__get_revision_number(svn_revn case svn_opt_revision_working: case svn_opt_revision_base: case svn_opt_revision_previous: + case svn_opt_revision_old: /* ### ? */ + case svn_opt_revision_new: /* ### ? */ + case svn_opt_revision_mine: /* ### ? */ { svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *ent; @@ -88,7 +91,10 @@ svn_client__get_revision_number(svn_revn SVN_ERR(svn_wc_adm_close(adm_access)); if ((revision->kind == svn_opt_revision_base) - || (revision->kind == svn_opt_revision_working)) + || (revision->kind == svn_opt_revision_working) + || (revision->kind == svn_opt_revision_old) /* ### ? */ + || (revision->kind == svn_opt_revision_new) /* ### ? */ + || (revision->kind == svn_opt_revision_mine)) /* ### ? */ { *revnum = ent->revision; } Index: subversion/libsvn_client/util.c =================================================================== --- subversion/libsvn_client/util.c (revision 32216) +++ subversion/libsvn_client/util.c (working copy) @@ -314,3 +314,81 @@ svn_client__default_walker_error_handler { return err; } + + +svn_wc_rev_kind_t +svn_cl__wc_rev_kind_from_rev(const svn_opt_revision_t *revision) +{ + switch (revision->kind) + { + case svn_opt_revision_committed: +/* printf("## COMMITTED => conflict_new, for testing.\n"); + fflush(stdout); + return svn_wc_rev_kind_conflict_new;*/ /* ### not really, just for testing */ + case svn_opt_revision_base: + return svn_wc_rev_kind_pristine; + + case svn_opt_revision_working: + case svn_opt_revision_unspecified: + return svn_wc_rev_kind_working; + + case svn_opt_revision_old: + return svn_wc_rev_kind_conflict_old; + + case svn_opt_revision_new: + return svn_wc_rev_kind_conflict_new; + + case svn_opt_revision_mine: + return svn_wc_rev_kind_conflict_mine; + + default: + assert(FALSE); + } +} + +const svn_opt_revision_t * +svn_cl__rev_default_to_head_or_base(const svn_opt_revision_t *revision, + const char *path_or_url) +{ + static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; + static svn_opt_revision_t base_rev = { svn_opt_revision_base, { 0 } }; + + if (revision->kind == svn_opt_revision_unspecified) + return svn_path_is_url(path_or_url) + ? &head_rev : &base_rev; + return revision; +} + +const svn_opt_revision_t * +svn_cl__rev_default_to_head_or_working(const svn_opt_revision_t *revision, + const char *path_or_url) +{ + static svn_opt_revision_t head_rev = { svn_opt_revision_head, { 0 } }; + static svn_opt_revision_t wrk_rev = { svn_opt_revision_working, { 0 } }; + + if (revision->kind == svn_opt_revision_unspecified) + return svn_path_is_url(path_or_url) + ? &head_rev : &wrk_rev; + return revision; +} + +svn_boolean_t +svn_cl__item_exists_in_local_revision(const svn_wc_entry_t *entry, + const svn_opt_revision_t *revision) +{ + switch (revision->kind) + { + case svn_opt_revision_working: + return (entry->schedule != svn_wc_schedule_delete); + case svn_opt_revision_base: + return (entry->schedule != svn_wc_schedule_add); + case svn_opt_revision_old: + return (entry->conflict_old != NULL); + case svn_opt_revision_new: + return (entry->conflict_new != NULL); + case svn_opt_revision_mine: + return (entry->conflict_wrk != NULL); + default: + return FALSE; + } +} Index: subversion/libsvn_subr/opt.c =================================================================== --- subversion/libsvn_subr/opt.c (revision 32216) +++ subversion/libsvn_subr/opt.c (working copy) @@ -550,6 +550,12 @@ svn_opt_subcommand_help(const char *subc * * - For "committed", set REVISION->kind to svn_opt_revision_committed. * + * - For "old", set REVISION->kind to svn_opt_revision_old. + * + * - For "new", set REVISION->kind to svn_opt_revision_new. + * + * - For "mine", set REVISION->kind to svn_opt_revision_mine. + * * If match, return 0, else return -1 and don't touch REVISION. */ static int @@ -571,6 +577,18 @@ revision_from_word(svn_opt_revision_t *r { revision->kind = svn_opt_revision_committed; } + else if (svn_cstring_casecmp(word, "old") == 0) + { + revision->kind = svn_opt_revision_old; + } + else if (svn_cstring_casecmp(word, "new") == 0) + { + revision->kind = svn_opt_revision_new; + } + else if (svn_cstring_casecmp(word, "mine") == 0) + { + revision->kind = svn_opt_revision_mine; + } else return -1; Index: subversion/libsvn_wc/adm_files.c =================================================================== --- subversion/libsvn_wc/adm_files.c (revision 32216) +++ subversion/libsvn_wc/adm_files.c (working copy) @@ -366,20 +366,56 @@ svn_wc__sync_text_base(const char *path, } const char * -svn_wc__text_base_path(const char *path, - svn_boolean_t tmp, - apr_pool_t *pool) +svn_wc__text_path(const char *path, + svn_wc_adm_access_t *adm_access, + svn_wc__props_kind_t kind, + svn_boolean_t tmp, + apr_pool_t *pool) { const char *newpath, *base_name; + const svn_wc_entry_t *entry; + const char *extension; + + switch (kind) + { + case svn_wc__props_base: + extension = SVN_WC__BASE_EXT; + break; + case svn_wc__props_revert: + extension = SVN_WC__REVERT_EXT; + break; + + case svn_wc__props_working: + assert(! tmp); + return path; + + case svn_wc__props_conflict_old: + svn_wc_entry(&entry, path, adm_access, TRUE, pool); + return entry->conflict_old; + case svn_wc__props_conflict_new: + svn_wc_entry(&entry, path, adm_access, TRUE, pool); + return entry->conflict_new; + case svn_wc__props_conflict_mine: + svn_wc_entry(&entry, path, adm_access, TRUE, pool); + return entry->conflict_wrk; + + case svn_wc__props_wcprop: + default: + assert(FALSE); + } svn_path_split(path, &newpath, &base_name, pool); - return extend_with_adm_name(newpath, - SVN_WC__BASE_EXT, - tmp, - pool, - SVN_WC__ADM_TEXT_BASE, - base_name, - NULL); + + return extend_with_adm_name(newpath, extension, tmp, pool, + SVN_WC__ADM_TEXT_BASE, base_name, NULL); +} + +const char * +svn_wc__text_base_path(const char *path, + svn_boolean_t tmp, + apr_pool_t *pool) +{ + return svn_wc__text_path(path, NULL, svn_wc__props_base, tmp, pool); } const char * @@ -387,16 +423,7 @@ svn_wc__text_revert_path(const char *pat svn_boolean_t tmp, apr_pool_t *pool) { - const char *newpath, *base_name; - - svn_path_split(path, &newpath, &base_name, pool); - return extend_with_adm_name(newpath, - SVN_WC__REVERT_EXT, - tmp, - pool, - SVN_WC__ADM_TEXT_BASE, - base_name, - NULL); + return svn_wc__text_path(path, NULL, svn_wc__props_revert, tmp, pool); } svn_error_t * @@ -413,7 +440,10 @@ svn_wc__prop_path(const char **prop_path SVN_WC__ADM_DIR_PROP_BASE, /* svn_wc__props_base */ SVN_WC__ADM_DIR_PROP_REVERT, /* svn_wc__props_revert */ SVN_WC__ADM_DIR_WCPROPS, /* svn_wc__props_wcprop */ - SVN_WC__ADM_DIR_PROPS /* svn_wc__props_working */ + SVN_WC__ADM_DIR_PROPS, /* svn_wc__props_working */ + SVN_WC__ADM_DIR_PROP_CNFL_OLD, /* svn_wc__props_conflict_old */ + SVN_WC__ADM_DIR_PROP_CNFL_NEW, /* svn_wc__props_conflict_new */ + SVN_WC__ADM_DIR_PROP_CNFL_MINE /* svn_wc__props_conflict_mine */ }; *prop_path = extend_with_adm_name @@ -430,14 +460,20 @@ svn_wc__prop_path(const char **prop_path SVN_WC__BASE_EXT, /* svn_wc__props_base */ SVN_WC__REVERT_EXT, /* svn_wc__props_revert */ SVN_WC__WORK_EXT, /* svn_wc__props_wcprop */ - SVN_WC__WORK_EXT /* svn_wc__props_working */ + SVN_WC__WORK_EXT, /* svn_wc__props_working */ + SVN_WC__CNFL_OLD_EXT, /* svn_wc__props_conflict_old */ + SVN_WC__CNFL_NEW_EXT, /* svn_wc__props_conflict_new */ + SVN_WC__CNFL_MINE_EXT /* svn_wc__props_conflict_mine */ }; static const char * dirs[] = { SVN_WC__ADM_PROP_BASE, /* svn_wc__props_base */ SVN_WC__ADM_PROP_BASE, /* svn_wc__props_revert */ SVN_WC__ADM_WCPROPS, /* svn_wc__props_wcprop */ - SVN_WC__ADM_PROPS /* svn_wc__props_working */ + SVN_WC__ADM_PROPS, /* svn_wc__props_working */ + SVN_WC__ADM_PROPS, /* svn_wc__props_conflict_old */ + SVN_WC__ADM_PROPS, /* svn_wc__props_conflict_new */ + SVN_WC__ADM_PROPS, /* svn_wc__props_conflict_mine */ }; const char *base_name; Index: subversion/libsvn_wc/adm_files.h =================================================================== --- subversion/libsvn_wc/adm_files.h (revision 32216) +++ subversion/libsvn_wc/adm_files.h (working copy) @@ -100,6 +100,19 @@ svn_wc__text_revert_path(const char *pat apr_pool_t *pool); +/* Return the path to PATH's KIND file. KIND may not be the 'wcprop' kind. + * If TMP is true, return a path to a temporary file of this kind rather than + * the permanent one. + * If KIND is the 'working' kind, then TMP may not be true. + * ### TODO: KIND is designed for properties, and being abused for this. */ +const char * +svn_wc__text_path(const char *path, + svn_wc_adm_access_t *adm_access, + svn_wc__props_kind_t kind, + svn_boolean_t tmp, + apr_pool_t *pool); + + /* Set *PROP_PATH to PATH's PROPS_KIND properties file. If TMP is set, return a path to the tmp working property file. PATH can be a directory or file, and even have changed w.r.t. the Index: subversion/libsvn_wc/adm_ops.c =================================================================== --- subversion/libsvn_wc/adm_ops.c (revision 32216) +++ subversion/libsvn_wc/adm_ops.c (working copy) @@ -1900,7 +1900,8 @@ revert_admin_things(svn_wc_adm_access_t } /* Remove the property conflict file if the entry lists one (and it - exists) */ + exists). Also in this case remove the property conflict artifact + files. */ if (entry->prejfile) { flags |= SVN_WC__ENTRY_MODIFY_PREJFILE; @@ -1909,6 +1910,14 @@ revert_admin_things(svn_wc_adm_access_t (&log_accum, adm_access, svn_path_join(svn_wc_adm_access_path(adm_access), entry->prejfile, pool), pool)); +#if 0 /* ### unfinished */ + SVN_ERR(svn_wc__loggy_remove + (&log_accum, adm_access, + svn_path_join(svn_wc_adm_access_path(adm_access), + entry->pconflict_old, pool), pool)); + ..._new + ..._wrk +#endif } /* Clean up the copied state if this is a replacement. */ @@ -2348,6 +2357,26 @@ svn_wc_get_pristine_copy_path(const char return SVN_NO_ERROR; } +svn_error_t * +svn_wc_get_text_path(const char **text_path, + const char *path, + svn_wc_rev_kind_t rev_kind, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool) +{ + static svn_wc__props_kind_t kinds[] = + { + svn_wc__props_base, /* svn_wc_rev_kind_pristine */ + svn_wc__props_working, /* svn_wc_rev_kind_working */ + svn_wc__props_conflict_old, /* svn_wc_rev_kind_conflict_old */ + svn_wc__props_conflict_new, /* svn_wc_rev_kind_conflict_new */ + svn_wc__props_conflict_mine /* svn_wc_rev_kind_conflict_mine */ + }; + + *text_path = svn_wc__text_path(path, adm_access, kinds[rev_kind], FALSE, pool); + return SVN_NO_ERROR; +} + svn_error_t * svn_wc_remove_from_revision_control(svn_wc_adm_access_t *adm_access, Index: subversion/libsvn_wc/diff.c =================================================================== --- subversion/libsvn_wc/diff.c (revision 32216) +++ subversion/libsvn_wc/diff.c (working copy) @@ -146,7 +146,8 @@ struct edit_baton { svn_boolean_t ignore_ancestry; /* Possibly diff repos against text-bases instead of working files. */ - svn_boolean_t use_text_base; + svn_boolean_t use_text_base; /* ### deprecated in favour of: */ + svn_wc_rev_kind_t wc_rev_kind; /* Possibly show the diffs backwards. */ svn_boolean_t reverse_order; @@ -260,7 +261,7 @@ make_editor_baton(struct edit_baton **ed void *callback_baton, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t use_text_base, + svn_wc_rev_kind_t wc_rev_kind, svn_boolean_t reverse_order, const apr_array_header_t *changelists, apr_pool_t *pool) @@ -279,7 +280,9 @@ make_editor_baton(struct edit_baton **ed eb->callback_baton = callback_baton; eb->depth = depth; eb->ignore_ancestry = ignore_ancestry; - eb->use_text_base = use_text_base; + eb->use_text_base = (wc_rev_kind == svn_wc_rev_kind_pristine); /* ### deprecated */ + SVN_ERR_ASSERT(wc_rev_kind == svn_wc_rev_kind_pristine || wc_rev_kind == svn_wc_rev_kind_working); /* ### */ + eb->wc_rev_kind = wc_rev_kind, eb->reverse_order = reverse_order; eb->changelist_hash = changelist_hash; eb->pool = pool; @@ -1776,13 +1779,13 @@ callbacks2_wrap(const svn_wc_diff_callba /* Create a diff editor and baton. */ svn_error_t * -svn_wc_get_diff_editor5(svn_wc_adm_access_t *anchor, +svn_wc_get_diff_editor5a(svn_wc_adm_access_t *anchor, const char *target, const svn_wc_diff_callbacks3_t *callbacks, void *callback_baton, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t use_text_base, + svn_wc_rev_kind_t wc_rev_kind, svn_boolean_t reverse_order, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -1797,7 +1800,7 @@ svn_wc_get_diff_editor5(svn_wc_adm_acces const svn_delta_editor_t *inner_editor; SVN_ERR(make_editor_baton(&eb, anchor, target, callbacks, callback_baton, - depth, ignore_ancestry, use_text_base, + depth, ignore_ancestry, wc_rev_kind, reverse_order, changelists, pool)); tree_editor = svn_delta_default_editor(eb->pool); @@ -1838,6 +1841,29 @@ svn_wc_get_diff_editor5(svn_wc_adm_acces return SVN_NO_ERROR; } +svn_error_t * +svn_wc_get_diff_editor5(svn_wc_adm_access_t *anchor, + const char *target, + const svn_wc_diff_callbacks3_t *callbacks, + void *callback_baton, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t use_text_base, + svn_boolean_t reverse_order, + svn_cancel_func_t cancel_func, + void *cancel_baton, + const apr_array_header_t *changelists, + const svn_delta_editor_t **editor, + void **edit_baton, + apr_pool_t *pool) +{ + return svn_wc_get_diff_editor5a(anchor, target, callbacks, callback_baton, + depth, ignore_ancestry, + use_text_base ? svn_wc_rev_kind_pristine + : svn_wc_rev_kind_working, + reverse_order, cancel_func, cancel_baton, + changelists, editor, edit_baton, pool); +} svn_error_t * svn_wc_get_diff_editor4(svn_wc_adm_access_t *anchor, @@ -1948,10 +1974,12 @@ svn_wc_get_diff_editor(svn_wc_adm_access } -/* Compare working copy against the text-base. */ +/* Compare one WC version against another WC version of the same node. */ svn_error_t * -svn_wc_diff5(svn_wc_adm_access_t *anchor, +svn_wc_diff5a(svn_wc_adm_access_t *anchor, const char *target, + svn_wc_rev_kind_t rev_kind_from, + svn_wc_rev_kind_t rev_kind_to, const svn_wc_diff_callbacks3_t *callbacks, void *callback_baton, svn_depth_t depth, @@ -1965,9 +1993,14 @@ svn_wc_diff5(svn_wc_adm_access_t *anchor const char *target_path; svn_wc_adm_access_t *adm_access; + if (rev_kind_from != svn_wc_rev_kind_pristine + || rev_kind_to != svn_wc_rev_kind_working) + return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + "Diff in WC can only be BASE->WORKING as yet"); + SVN_ERR(make_editor_baton(&eb, anchor, target, callbacks, callback_baton, - depth, ignore_ancestry, FALSE, FALSE, - changelists, pool)); + depth, ignore_ancestry, svn_wc_rev_kind_working, + FALSE, changelists, pool)); target_path = svn_path_join(svn_wc_adm_access_path(anchor), target, eb->pool); @@ -1987,6 +2020,22 @@ svn_wc_diff5(svn_wc_adm_access_t *anchor return SVN_NO_ERROR; } +/* Compare working copy against the text-base. */ +svn_error_t * +svn_wc_diff5(svn_wc_adm_access_t *anchor, + const char *target, + const svn_wc_diff_callbacks3_t *callbacks, + void *callback_baton, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + const apr_array_header_t *changelists, + apr_pool_t *pool) +{ + return svn_wc_diff5a(anchor, target, svn_wc_rev_kind_pristine, + svn_wc_rev_kind_working, callbacks, callback_baton, + depth, ignore_ancestry, changelists, pool); +} + svn_error_t * svn_wc_diff4(svn_wc_adm_access_t *anchor, const char *target, Index: subversion/libsvn_wc/props.c =================================================================== --- subversion/libsvn_wc/props.c (revision 32216) +++ subversion/libsvn_wc/props.c (working copy) @@ -254,12 +254,15 @@ build_present_props(apr_hash_t *props, a /*** Loading regular properties. ***/ svn_error_t * -svn_wc__load_props(apr_hash_t **base_props_p, - apr_hash_t **props_p, - apr_hash_t **revert_props_p, - svn_wc_adm_access_t *adm_access, - const char *path, - apr_pool_t *pool) +svn_wc__load_props2(apr_hash_t **base_props_p, + apr_hash_t **props_p, + apr_hash_t **revert_props_p, + apr_hash_t **conflict_old_props_p, + apr_hash_t **conflict_new_props_p, + apr_hash_t **conflict_mine_props_p, + svn_wc_adm_access_t *adm_access, + const char *path, + apr_pool_t *pool) { svn_node_kind_t kind; svn_boolean_t has_propcaching = @@ -270,6 +273,8 @@ svn_wc__load_props(apr_hash_t **base_pro SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, pool)); /* If there is no entry, we just return empty hashes, since the property merging can use this function when there is no entry. */ + /* ### Huh? Explain in terms of API promise, not by + * allusion to what something else does. */ if (! entry) { if (base_props_p) @@ -278,6 +283,12 @@ svn_wc__load_props(apr_hash_t **base_pro *props_p = apr_hash_make(pool); if (revert_props_p) *revert_props_p = apr_hash_make(pool); + if (conflict_old_props_p) + *conflict_old_props_p = apr_hash_make(pool); + if (conflict_new_props_p) + *conflict_new_props_p = apr_hash_make(pool); + if (conflict_mine_props_p) + *conflict_mine_props_p = apr_hash_make(pool); return SVN_NO_ERROR; } @@ -331,13 +342,115 @@ svn_wc__load_props(apr_hash_t **base_pro } } + if (conflict_old_props_p) + { + const char *prop_file_path; + + *conflict_old_props_p = apr_hash_make(pool); + SVN_ERR(svn_wc__prop_path(&prop_file_path, path, kind, + svn_wc__props_conflict_old, FALSE, pool)); + SVN_ERR(load_prop_file(prop_file_path, *conflict_old_props_p, pool)); + } + + if (conflict_new_props_p) + { + const char *prop_file_path; + + *conflict_new_props_p = apr_hash_make(pool); + SVN_ERR(svn_wc__prop_path(&prop_file_path, path, kind, + svn_wc__props_conflict_new, FALSE, pool)); + SVN_ERR(load_prop_file(prop_file_path, *conflict_new_props_p, pool)); + } + + if (conflict_mine_props_p) + { + const char *prop_file_path; + + *conflict_mine_props_p = apr_hash_make(pool); + SVN_ERR(svn_wc__prop_path(&prop_file_path, path, kind, + svn_wc__props_conflict_mine, FALSE, pool)); + SVN_ERR(load_prop_file(prop_file_path, *conflict_mine_props_p, pool)); + } + return SVN_NO_ERROR; } +svn_error_t * +svn_wc__load_props(apr_hash_t **base_props_p, + apr_hash_t **props_p, + apr_hash_t **revert_props_p, + svn_wc_adm_access_t *adm_access, + const char *path, + apr_pool_t *pool) +{ + return svn_wc__load_props2(base_props_p, props_p, revert_props_p, + NULL, NULL, NULL, + adm_access, path, pool); +} + /*---------------------------------------------------------------------*/ /*** Installing new properties. ***/ + +/* Extend LOG_ACCUM with log commands to write the properties PROPS into + * the admin file specified by WC_PROP_KIND; or to delete this file if + * PROPS is empty and FILE_WAS_PRESENT is true. ADM_ACCESS and PATH + * specify the WC item with which this file should be associated. */ +static svn_error_t * +install_or_delete_props_file(svn_stringbuf_t **log_accum, + svn_wc_adm_access_t *adm_access, + const char *path, + apr_hash_t *props, + svn_wc__props_kind_t wc_prop_kind, + svn_boolean_t file_was_present, + apr_pool_t *pool) +{ + svn_node_kind_t node_kind; + const char *propfile_path; + + if (! svn_path_is_child(svn_wc_adm_access_path(adm_access), path, NULL)) + node_kind = svn_node_dir; + else + node_kind = svn_node_file; + + SVN_ERR(svn_wc__prop_path(&propfile_path, path, + node_kind, wc_prop_kind, FALSE, pool)); + + if (apr_hash_count(props) > 0) + { + const char *propfile_tmp_path; + + /* Write log commands to create this file. */ + + /* Write the property hash into a temporary file. */ + SVN_ERR(svn_wc__prop_path(&propfile_tmp_path, path, + node_kind, wc_prop_kind, TRUE, pool)); + SVN_ERR(save_prop_file(propfile_tmp_path, props, + FALSE, pool)); + + /* Write a log entry to move tmp file to real file. */ + SVN_ERR(svn_wc__loggy_move(log_accum, NULL, adm_access, + propfile_tmp_path, + propfile_path, + FALSE, pool)); + + /* Make the props file read-only */ + SVN_ERR(svn_wc__loggy_set_readonly(log_accum, adm_access, + propfile_path, pool)); + } + else if (file_was_present) + { + /* Write log commands to remove this file. */ + SVN_ERR(svn_wc__prop_path(&propfile_path, path, + node_kind, wc_prop_kind, FALSE, pool)); + SVN_ERR(svn_wc__loggy_remove(log_accum, adm_access, + propfile_path, pool)); + } + + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__install_props(svn_stringbuf_t **log_accum, svn_wc_adm_access_t *adm_access, @@ -347,26 +460,20 @@ svn_wc__install_props(svn_stringbuf_t ** svn_boolean_t write_base_props, apr_pool_t *pool) { - const char *working_propfile_path, *working_prop_tmp_path; apr_array_header_t *prop_diffs; const svn_wc_entry_t *entry; svn_wc_entry_t tmp_entry; - svn_node_kind_t kind; svn_boolean_t has_propcaching = svn_wc__adm_wc_format(adm_access) > SVN_WC__NO_PROPCACHING_VERSION; - if (! svn_path_is_child(svn_wc_adm_access_path(adm_access), path, NULL)) - kind = svn_node_dir; - else - kind = svn_node_file; - /* Check if the props are modified. */ SVN_ERR(svn_prop_diffs(&prop_diffs, working_props, base_props, pool)); + + /* Update the entry */ tmp_entry.has_prop_mods = (prop_diffs->nelts > 0); tmp_entry.has_props = (apr_hash_count(working_props) > 0); tmp_entry.cachable_props = SVN_WC__CACHABLE_PROPS; tmp_entry.present_props = build_present_props(working_props, pool); - SVN_ERR(svn_wc__loggy_entry_modify(log_accum, adm_access, path, &tmp_entry, SVN_WC__ENTRY_MODIFY_HAS_PROPS @@ -380,69 +487,26 @@ svn_wc__install_props(svn_stringbuf_t ** else entry = NULL; - /* Write our property hashes into temporary files. Notice that the - paths computed are ABSOLUTE pathnames, which is what our disk - routines require. */ - SVN_ERR(svn_wc__prop_path(&working_propfile_path, path, - kind, svn_wc__props_working, FALSE, pool)); - if (tmp_entry.has_prop_mods) - { - SVN_ERR(svn_wc__prop_path(&working_prop_tmp_path, path, - kind, svn_wc__props_working, TRUE, pool)); - - /* Write the working prop hash to path/.svn/tmp/props/name or - path/.svn/tmp/dir-props */ - SVN_ERR(save_prop_file(working_prop_tmp_path, working_props, - FALSE, pool)); - - /* Write log entry to move working tmp copy to real working area. */ - SVN_ERR(svn_wc__loggy_move(log_accum, NULL, - adm_access, - working_prop_tmp_path, - working_propfile_path, - FALSE, pool)); - - /* Make props read-only */ - SVN_ERR(svn_wc__loggy_set_readonly(log_accum, adm_access, - working_propfile_path, pool)); - } - else + /* Save the working properties file if it differs from the base. */ + if (! tmp_entry.has_prop_mods) { - /* No property modifications, remove the file instead. */ - if (! has_propcaching || (entry && entry->has_prop_mods)) - SVN_ERR(svn_wc__loggy_remove(log_accum, adm_access, - working_propfile_path, pool)); + /* In this case we want to make the disk file be absent. */ + working_props = apr_hash_make(pool); } + SVN_ERR(install_or_delete_props_file(log_accum, adm_access, path, + working_props, svn_wc__props_working, + (! has_propcaching + || (entry && entry->has_prop_mods)), + pool)); /* Repeat the above steps for the base properties if required. */ if (write_base_props) { - const char *base_propfile_path, *base_prop_tmp_path; - - SVN_ERR(svn_wc__prop_path(&base_propfile_path, path, - kind, svn_wc__props_base, FALSE, pool)); - - if (apr_hash_count(base_props) > 0) - { - SVN_ERR(svn_wc__prop_path(&base_prop_tmp_path, path, - kind, svn_wc__props_base, TRUE, pool)); - - SVN_ERR(save_prop_file(base_prop_tmp_path, base_props, FALSE, pool)); - - SVN_ERR(svn_wc__loggy_move(log_accum, NULL, adm_access, - base_prop_tmp_path, - base_propfile_path, - FALSE, pool)); - - SVN_ERR(svn_wc__loggy_set_readonly(log_accum, adm_access, - base_propfile_path, pool)); - } - else - { - if (! has_propcaching || (entry && entry->has_props)) - SVN_ERR(svn_wc__loggy_remove(log_accum, adm_access, - base_propfile_path, pool)); - } + SVN_ERR(install_or_delete_props_file(log_accum, adm_access, path, + base_props, svn_wc__props_base, + (! has_propcaching + || (entry && entry->has_props)), + pool)); } return SVN_NO_ERROR; @@ -1210,10 +1274,15 @@ write_tmp_file(const char **new_path, /* Helper function for the three apply_* functions below, used when - * merging properties together. + * merging properties together. Handle a conflict on one property by using + * the resolver callback function (if provided) to get the desired result + * into WORKING_PROPS. If successful, set *CONFLICT_REMAINS to false. If the + * resolver function is not provided or does not resolve the conflict then + * set *CONFLICT_REMAINS to true. * * Given property PROPNAME on PATH, and four possible property values, - * generate four tmpfiles and pass them to CONFLICT_FUNC callback. + * put three of them in temporary files and pass those and a temporary file + * for the merged result to the CONFLICT_FUNC callback. * This gives the client an opportunity to interactively resolve the * property conflict. (ADM_ACCESS provides the ability to examine * PATH's entries.) @@ -1227,7 +1296,9 @@ write_tmp_file(const char **new_path, * 'choose_postpone', then set *CONFLICT_REMAINS to true and return. * * If the callback responds with a choice of 'base', 'theirs', 'mine', - * or 'merged', then install the proper value into WORKING_PROPS and + * or 'merged', then install the proper value (OLD_VAL, NEW_VAL, + * WORKING_VAL, or the merged value provided by the callback, respectively; + * note two different meanings of "base") into WORKING_PROPS and * set *CONFLICT_REMAINS to false. * */ @@ -1307,7 +1378,7 @@ maybe_generate_propconflict(svn_boolean_ HOWEVER: we can still pass one of the two base values as 'base_file' to the callback anyway. It's still useful to present the working and new values to the user to - compare. */ + compare. ### AARGH! Vague wooly spec. */ if (working_val && svn_string_compare(base_val, working_val)) the_val = old_val; @@ -1821,6 +1892,37 @@ apply_single_prop_change(svn_wc_notify_s } +/* Install into the administrative area given by ADM_ACCESS three property + * hash files containing the conflict artifact property sets OLD_PROPHASH, + * NEW_PROPHASH and MINE_PROPHASH for the WC item PATH. + * Use POOL for temporary allocations. */ +static svn_error_t * +install_prop_conflict_files(svn_wc_adm_access_t *adm_access, + const char *path, + apr_hash_t *old_prophash, + apr_hash_t *new_prophash, + apr_hash_t *mine_prophash, + apr_pool_t *pool) +{ + svn_stringbuf_t *log_accum; + + SVN_ERR(install_or_delete_props_file(&log_accum, adm_access, path, + old_prophash, + svn_wc__props_conflict_old, + FALSE, pool)); + SVN_ERR(install_or_delete_props_file(&log_accum, adm_access, path, + new_prophash, + svn_wc__props_conflict_new, + FALSE, pool)); + SVN_ERR(install_or_delete_props_file(&log_accum, adm_access, path, + mine_prophash, + svn_wc__props_conflict_mine, + FALSE, pool)); + SVN_ERR(svn_wc__write_log(adm_access, 0, log_accum, pool)); + SVN_ERR(svn_wc__run_log(adm_access, NULL, pool)); + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__merge_props(svn_wc_notify_state_t *state, svn_wc_adm_access_t *adm_access, @@ -1842,6 +1944,7 @@ svn_wc__merge_props(svn_wc_notify_state_ const char *reject_path = NULL; apr_file_t *reject_tmp_fp = NULL; /* the temporary conflicts file */ const char *reject_tmp_path = NULL; + apr_hash_t *server_newprops; /* the new, complete set on the server side */ if (! svn_path_is_child(svn_wc_adm_access_path(adm_access), path, NULL)) is_dir = TRUE; @@ -1854,7 +1957,10 @@ svn_wc__merge_props(svn_wc_notify_state_ working_props ? NULL : &working_props, NULL, adm_access, path, pool)); if (!server_baseprops) - server_baseprops = base_props; + server_baseprops = apr_hash_copy(pool, base_props); /* shallow copy */ + + /* Make SERVER_NEWPROPS from the old ones, and modify by each of the changes. */ + server_newprops = apr_hash_copy(pool, server_baseprops); /* shallow copy */ if (state) { @@ -1865,7 +1971,12 @@ svn_wc__merge_props(svn_wc_notify_state_ *state = svn_wc_notify_state_unchanged; } - /* Looping over the array of incoming propchanges we want to apply: */ + /* Looping over the array of incoming propchanges we want to apply: + * Don't modify SERVER_BASEPROPS. + * Populate SERVER_NEWPROPS. + * Update BASE_PROPS if this is a "base merge". + * Merge changes into WORKING_PROPS. + */ for (i = 0; i < propchanges->nelts; i++) { const char *propname; @@ -1885,6 +1996,8 @@ svn_wc__merge_props(svn_wc_notify_state_ working_val = apr_hash_get(working_props, propname, APR_HASH_KEY_STRING); base_val = apr_hash_get(base_props, propname, APR_HASH_KEY_STRING); + apr_hash_set(server_newprops, propname, APR_HASH_KEY_STRING, to_val); + if (base_merge) apr_hash_set(base_props, propname, APR_HASH_KEY_STRING, to_val); @@ -2005,6 +2118,10 @@ svn_wc__merge_props(svn_wc_notify_state_ pool)); } + /* Save the old/new/mine conflict artifact files. */ + SVN_ERR(install_prop_conflict_files(adm_access, path, + server_baseprops, server_newprops, + working_props, pool)); } /* if (reject_tmp_fp) */ return SVN_NO_ERROR; @@ -2189,10 +2306,11 @@ svn_wc__wcprop_set(const char *name, svn_error_t * -svn_wc_prop_list(apr_hash_t **props, - const char *path, - svn_wc_adm_access_t *adm_access, - apr_pool_t *pool) +svn_wc_prop_list2(apr_hash_t **props, + const char *path, + svn_wc_rev_kind_t rev_kind, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool) { const svn_wc_entry_t *entry; @@ -2212,7 +2330,42 @@ svn_wc_prop_list(apr_hash_t **props, SVN_ERR(svn_wc_adm_retrieve(&adm_access, adm_access, svn_path_dirname(path, pool), pool)); - return svn_wc__load_props(NULL, props, NULL, adm_access, path, pool); + switch (rev_kind) + { + case svn_wc_rev_kind_pristine: + SVN_ERR(svn_wc__load_props2(props, NULL, NULL, NULL, NULL, NULL, + adm_access, path, pool)); + break; + case svn_wc_rev_kind_working: + SVN_ERR(svn_wc__load_props2(NULL, props, NULL, NULL, NULL, NULL, + adm_access, path, pool)); + break; + case svn_wc_rev_kind_conflict_old: + SVN_ERR(svn_wc__load_props2(NULL, NULL, NULL, props, NULL, NULL, + adm_access, path, pool)); + break; + case svn_wc_rev_kind_conflict_new: + SVN_ERR(svn_wc__load_props2(NULL, NULL, NULL, NULL, props, NULL, + adm_access, path, pool)); + break; + case svn_wc_rev_kind_conflict_mine: + SVN_ERR(svn_wc__load_props2(NULL, NULL, NULL, NULL, NULL, props, + adm_access, path, pool)); + break; + default: + SVN_ERR_MALFUNCTION(); + } + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc_prop_list(apr_hash_t **props, + const char *path, + svn_wc_adm_access_t *adm_access, + apr_pool_t *pool) +{ + return svn_wc_prop_list2(props, path, svn_wc_rev_kind_working, adm_access, + pool); } /* Determine if PROPNAME is contained in the list of space separated Index: subversion/libsvn_wc/props.h =================================================================== --- subversion/libsvn_wc/props.h (revision 32216) +++ subversion/libsvn_wc/props.h (working copy) @@ -35,7 +35,10 @@ typedef enum svn_wc__props_kind_t svn_wc__props_base = 0, svn_wc__props_revert, svn_wc__props_wcprop, - svn_wc__props_working + svn_wc__props_working, + svn_wc__props_conflict_old, + svn_wc__props_conflict_new, + svn_wc__props_conflict_mine } svn_wc__props_kind_t; @@ -216,6 +219,23 @@ svn_wc__load_props(apr_hash_t **base_pro const char *path, apr_pool_t *pool); +/* Load the base, working, revert, old, new, mine props for PATH in ADM_ACCESS, + * returning them in *BASE_PROPS_P, *PROPS_P, *REVERT_PROPS_P, + * *CONFLICT_OLD_PROPS_, *CONFLICT_NEW_PROPS_PP, *CONFLICT_MINE_PROPS_P + * respectively. Any of these may be null. + * If any requested set of properties is not available, ###? + * Do all allocations in POOL. */ +svn_error_t * +svn_wc__load_props2(apr_hash_t **base_props_p, + apr_hash_t **props_p, + apr_hash_t **revert_props_p, + apr_hash_t **conflict_old_props_p, + apr_hash_t **conflict_new_props_p, + apr_hash_t **conflict_mine_props_p, + svn_wc_adm_access_t *adm_access, + const char *path, + apr_pool_t *pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_wc/wc.h =================================================================== --- subversion/libsvn_wc/wc.h (revision 32216) +++ subversion/libsvn_wc/wc.h (working copy) @@ -38,6 +38,9 @@ extern "C" { #define SVN_WC__WORK_EXT ".svn-work" /* for working propfiles */ #define SVN_WC__REVERT_EXT ".svn-revert" /* for reverting a replaced file */ +#define SVN_WC__CNFL_OLD_EXT ".svn-old" +#define SVN_WC__CNFL_NEW_EXT ".svn-new" +#define SVN_WC__CNFL_MINE_EXT ".svn-mine" @@ -145,6 +148,9 @@ struct svn_wc_traversal_info_t #define SVN_WC__ADM_DIR_PROP_REVERT "dir-prop-revert" #define SVN_WC__ADM_WCPROPS "wcprops" #define SVN_WC__ADM_DIR_WCPROPS "dir-wcprops" +#define SVN_WC__ADM_DIR_PROP_CNFL_OLD "dir-prop-cnfl-old" +#define SVN_WC__ADM_DIR_PROP_CNFL_NEW "dir-prop-cnfl-new" +#define SVN_WC__ADM_DIR_PROP_CNFL_MINE "dir-prop-cnfl-mine" #define SVN_WC__ADM_ALL_WCPROPS "all-wcprops" #define SVN_WC__ADM_LOG "log" #define SVN_WC__ADM_KILLME "KILLME" Index: subversion/svn/diff-cmd.c =================================================================== --- subversion/svn/diff-cmd.c (revision 32216) +++ subversion/svn/diff-cmd.c (working copy) @@ -295,10 +295,15 @@ svn_cl__diff(apr_getopt_t *os, ? svn_opt_revision_working : svn_opt_revision_head; /* Determine if we need to do pegged diffs. */ - if ((opt_state->start_revision.kind != svn_opt_revision_base - && opt_state->start_revision.kind != svn_opt_revision_working) - || (opt_state->end_revision.kind != svn_opt_revision_base - && opt_state->end_revision.kind != svn_opt_revision_working)) +#define SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision) \ + ((revision)->kind == svn_opt_revision_base || \ + (revision)->kind == svn_opt_revision_working || \ + (revision)->kind == svn_opt_revision_committed || \ + (revision)->kind == svn_opt_revision_old || \ + (revision)->kind == svn_opt_revision_new || \ + (revision)->kind == svn_opt_revision_mine) + if (! (SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(&opt_state->start_revision) + && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(&opt_state->end_revision))) pegged_diff = TRUE; }