Provide a unified API for reading any locally stored version of a given node's text or properties: the "base" or "working" version, or one of three conflict artifact versions "old"/"theirs"/"mine". Formerly, inconsistent APIs were available for reading a file's text and no APIs were available for the conflict artifacts other than to get the names of the files. Store property conflict artifacts (the old/theirs/mine versions) in the WC admin area when conflicts occur, and provide access through this mechanism. ### WORK IN PROGRESS. Some tests are broken: at least update_tests 34. *** Interface via svn_opt_revision_t *** * subversion/include/svn_opt.h (svn_opt_revision_kind): Add revision kinds 'old', 'theirs' and 'mine'. * subversion/libsvn_subr/opt.c (revision_from_word): Add revision kinds 'old', 'theirs' and 'mine'. * subversion/libsvn_client/revisions.c (svn_client__get_revision_number): Add revision kinds 'old', 'theirs' and 'mine'. ### Unfinished: rev numbers not yet stored in entry. *** WC interface via svn_wc_rev_kind_t *** * subversion/include/svn_wc.h (svn_wc_rev_kind_t): New type. (svn_wc_prop_list2, svn_wc_prop_get2, svn_wc_get_diff_editor5a, svn_wc_diff5a): New functions, like their predecessors but with svn_wc_rev_kind_t arguments specifying which version of the source file to read from. (svn_wc_get_text_path): New function. *** WC Library *** * subversion/libsvn_wc/adm_files.c (svn_wc__text_path): New function. (svn_wc__text_base_path, svn_wc__text_revert_path): Use svn_wc__text_path(). (svn_wc__prop_path): Support old/theirs/mine. * subversion/libsvn_wc/adm_files.h (svn_wc__text_path): New function. * subversion/libsvn_wc/adm_ops.c (revert_admin_things): ### Placeholder for where we should remove prop conflict artifact files. (svn_wc_get_text_path): New function. * subversion/libsvn_wc/diff.c (edit_baton): Add a wc_rev_kind field to support old/theirs/mine. (make_editor_baton): Support old/theirs/mine. ### The previous field is deprecated. Check fully supported, or remove it. (svn_wc_get_diff_editor5a): New version of svn_wc_get_diff_editor5 to support old/theirs/mine. ### Combine with svn_wc_get_diff_editor5(). (svn_wc_diff5a): New version of svn_wc_diff5 to support old/theirs/mine. ### Combine with svn_wc_diff5(). * subversion/libsvn_wc/props.c (svn_wc__load_props2): New function, like svn_wc__load_props() but supporting old/theirs/mine. (svn_wc__load_props): Re-implement as a wrapper around svn_wc__load_props2(). (maybe_generate_propconflict): Improve doc string. (install_prop_conflict_files): New function. (svn_wc__merge_props): When raising a conflict, save the old/theirs/mine versions of the properties. (svn_wc_prop_list2): New function, like svn_wc_prop_list() but supporting old/theirs/mine. (svn_wc_prop_list): Re-implement as a wrapper around svn_wc_prop_list2(). * subversion/libsvn_wc/props.h (svn_wc__props_kind_t): Add enumerators for old/theirs/mine. (svn_wc__load_props2): New function. ### Should just update svn_wc__load_props. No need to rev it. * subversion/libsvn_wc/wc.h (SVN_WC__CNFL_OLD_EXT, SVN_WC__CNFL_NEW_EXT, SVN_WC__CNFL_MINE_EXT, SVN_WC__ADM_DIR_PROP_CNFL_OLD, SVN_WC__ADM_DIR_PROP_CNFL_NEW, SVN_WC__ADM_DIR_PROP_CNFL_MINE): New macros. *** Client Library *** * subversion/libsvn_client/cat.c (cat_local_file): Support old/theirs/mine. ### Doesn't use the right status and mod-time for old/theirs/mine. * subversion/libsvn_client/diff.c (check_paths): Allow old/theirs/mine. (diff_wc_wc): Allow diffs between pairs of WC revisions other than just base:working. * subversion/libsvn_client/export.c (copy_one_versioned_file): Allow copying from old/theirs/mine. ### Doesn't use the right status and mod-time for old/theirs/mine. * subversion/libsvn_client/mergeinfo.c (svn_client__parse_mergeinfo): Allow old/theirs/mine. (svn_client__get_wc_or_repos_mergeinfo): Adjust the call accordingly. * subversion/libsvn_client/prop_commands.c (pristine_or_working_props, pristine_or_working_propval): Allow old/theirs/mine. ### Function names are now poor. (propget_walk_baton, proplist_walk_baton, propget_walk_cb, proplist_walk_cb, svn_client__get_prop_from_wc): Allow old/theirs/mine. (svn_client_propget3, svn_client_proplist3): Use svn_cl__rev_default_to_head_or_base() and SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(), and thereby allow old/theirs/mine. * subversion/libsvn_client/util.c (svn_cl__item_exists_in_local_revision, svn_cl__wc_rev_kind_from_rev): New functions. * subversion/libsvn_client/client.h (svn_client__get_prop_from_wc): Change "pristine" parameter to "revision". (SVN_CLIENT__REVKIND_NEEDS_WC, SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC): Add revision kinds 'old', 'theirs' and 'mine'. (svn_cl__item_exists_in_local_revision, svn_cl__wc_rev_kind_from_rev): New functions. *** Command-Line Client *** * subversion/svn/diff-cmd.c (svn_cl__diff): Support old/theirs/mine. Index: subversion/include/svn_opt.h =================================================================== --- subversion/include/svn_opt.h (revision 32317) +++ 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: old/left version of incoming change */ + svn_opt_revision_old, + + /** conflict artifact: new/right version of incoming change */ + svn_opt_revision_theirs, + + /** conflict artifact: pre-conflict working version */ + svn_opt_revision_mine }; /** Index: subversion/include/svn_wc.h =================================================================== --- subversion/include/svn_wc.h (revision 32317) +++ 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/left/base side */ + svn_wc_rev_kind_conflict_new, /* the incoming change's new/right/theirs */ + svn_wc_rev_kind_conflict_wrk /* the pre-conflict working/mine version */ +} 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, @@ -3677,6 +3711,19 @@ svn_wc_prop_list(apr_hash_t **props, * @a name may be a regular or wc property; if it is an entry property, * return the error @c SVN_ERR_BAD_PROP_KIND. @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_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, @@ -3826,8 +3873,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 +3891,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 +4013,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 +4046,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 +4069,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 +4150,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 +4382,23 @@ 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 is a conflict version, and the file is not in + * conflict, return an error. + * 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. + * + * ### Need to specify which way the file should be translated? + */ +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 32317) +++ subversion/libsvn_client/cat.c (working copy) @@ -39,9 +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. 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, @@ -60,8 +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(SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)); + wc_rev_kind = svn_cl__wc_rev_kind_from_rev(revision); SVN_ERR(svn_wc__entry_versioned(&entry, path, adm_access, FALSE, pool)); @@ -70,17 +72,21 @@ 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)); + if (base == NULL) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("'%s' does not exist at that version"), + path); + 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; @@ -98,7 +104,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)); } Index: subversion/libsvn_client/client.h =================================================================== --- subversion/libsvn_client/client.h (revision 32317) +++ subversion/libsvn_client/client.h (working copy) @@ -322,7 +322,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, @@ -1057,14 +1057,20 @@ 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_theirs || \ + (kind) == svn_opt_revision_mine) /* Return true if KIND is a revision kind that the WC can supply without * contacting the repository. Otherwise, return false. */ #define SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(kind) \ ((kind) == svn_opt_revision_base || \ (kind) == svn_opt_revision_working || \ - (kind) == svn_opt_revision_committed) + (kind) == svn_opt_revision_committed || \ + (kind) == svn_opt_revision_old || \ + (kind) == svn_opt_revision_theirs || \ + (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' @@ -1086,6 +1092,18 @@ const svn_opt_revision_t * svn_cl__rev_default_to_peg(const svn_opt_revision_t *revision, const svn_opt_revision_t *peg_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); + +/* 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); + #ifdef __cplusplus } Index: subversion/libsvn_client/diff.c =================================================================== --- subversion/libsvn_client/diff.c (revision 32317) +++ subversion/libsvn_client/diff.c (working copy) @@ -908,9 +908,11 @@ check_paths(const struct diff_parameters 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->kind) ? */ 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->kind) ? */ if (params->peg_revision->kind != svn_opt_revision_unspecified) { @@ -1132,14 +1134,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 +1151,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 32317) +++ subversion/libsvn_client/export.c (working copy) @@ -107,38 +107,31 @@ copy_one_versioned_file(const char *from 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)); + if (base == NULL) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("'%s' does not exist at that version"), + from); + 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; } @@ -231,17 +224,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) Index: subversion/libsvn_client/mergeinfo.c =================================================================== --- subversion/libsvn_client/mergeinfo.c (revision 32317) +++ 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) @@ -363,6 +367,8 @@ svn_client__get_wc_or_repos_mergeinfo(sv if (entry->schedule != svn_wc_schedule_add) { apr_hash_t *props = apr_hash_make(pool); + static const svn_opt_revision_t base_rev + = { svn_opt_revision_base, { 0 }}; /* Get the pristine SVN_PROP_MERGEINFO. If it exists, then it should have been deleted by the local @@ -370,7 +376,7 @@ 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, &base_rev, 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 32317) +++ 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; @@ -886,7 +878,6 @@ svn_client_propget3(apr_hash_t **props, { 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, @@ -899,12 +890,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)); @@ -1155,7 +1142,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. */ @@ -1167,7 +1154,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, @@ -1186,8 +1173,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. */ @@ -1196,8 +1182,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)); @@ -1231,7 +1217,6 @@ svn_client_proplist3(const char *path_or && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind) && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)) { - 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; @@ -1243,16 +1228,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)); @@ -1265,7 +1240,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; @@ -1279,8 +1254,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 32317) +++ 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_theirs: + case svn_opt_revision_mine: { svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *ent; @@ -92,7 +95,40 @@ svn_client__get_revision_number(svn_revn { *revnum = ent->revision; } - else + else if (revision->kind == svn_opt_revision_old) + { + SVN_ERR_MALFUNCTION(); + /* ### + if (! SVN_IS_VALID_REVNUM(ent->old_rev)) + return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, + _("Path '%s' has no conflict-old " + "revision"), path); + *revnum = ent->old_rev; + */ + } + else if (revision->kind == svn_opt_revision_theirs) + { + SVN_ERR_MALFUNCTION(); + /* ### + if (! SVN_IS_VALID_REVNUM(ent->new_rev)) + return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, + _("Path '%s' has no conflict-theirs " + "revision"), path); + *revnum = ent->new_rev; + */ + } + else if (revision->kind == svn_opt_revision_mine) + { + SVN_ERR_MALFUNCTION(); + /* ### + if (! SVN_IS_VALID_REVNUM(ent->wrk_rev)) + return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, + _("Path '%s' has no conflict-mine " + "revision"), path); + *revnum = ent->wrk_rev; + */ + } + else /* committed / previous */ { if (! SVN_IS_VALID_REVNUM(ent->cmt_rev)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, Index: subversion/libsvn_client/util.c =================================================================== --- subversion/libsvn_client/util.c (revision 32317) +++ subversion/libsvn_client/util.c (working copy) @@ -348,3 +348,51 @@ svn_cl__rev_default_to_peg(const svn_opt return peg_revision; 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_theirs: + return (entry->conflict_new != NULL); + case svn_opt_revision_mine: + return (entry->conflict_wrk != NULL); + default: + return FALSE; + } +} + +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: + 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_theirs: + return svn_wc_rev_kind_conflict_new; + + case svn_opt_revision_mine: + return svn_wc_rev_kind_conflict_wrk; + + default: + assert(FALSE); + } +} Index: subversion/libsvn_subr/opt.c =================================================================== --- subversion/libsvn_subr/opt.c (revision 32317) +++ 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 "theirs", set REVISION->kind to svn_opt_revision_theirs. + * + * - 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, "theirs") == 0) + { + revision->kind = svn_opt_revision_theirs; + } + 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 32317) +++ 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_ERR( */ svn_wc_entry(&entry, path, adm_access, TRUE, pool); + return entry->conflict_old; + case svn_wc__props_conflict_new: + /* SVN_ERR( */ svn_wc_entry(&entry, path, adm_access, TRUE, pool); + return entry->conflict_new; + case svn_wc__props_conflict_wrk: + /* SVN_ERR( */ 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_WRK /* svn_wc__props_conflict_wrk */ }; *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_WRK_EXT /* svn_wc__props_conflict_wrk */ }; 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_wrk */ }; const char *base_name; Index: subversion/libsvn_wc/adm_files.h =================================================================== --- subversion/libsvn_wc/adm_files.h (revision 32317) +++ 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 32317) +++ 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,27 @@ 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_wrk /* svn_wc_rev_kind_conflict_wrk */ + }; + + *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 32317) +++ 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 32317) +++ 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,9 +342,52 @@ 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_wrk, 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); +} + /*---------------------------------------------------------------------*/ @@ -1227,10 +1281,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.) @@ -1244,7 +1303,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. * */ @@ -1324,7 +1385,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; @@ -1838,6 +1899,31 @@ 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_props_file(&log_accum, adm_access, path, old_prophash, + svn_wc__props_conflict_old, pool)); + SVN_ERR(install_props_file(&log_accum, adm_access, path, new_prophash, + svn_wc__props_conflict_new, pool)); + SVN_ERR(install_props_file(&log_accum, adm_access, path, mine_prophash, + svn_wc__props_conflict_wrk, 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, @@ -1859,6 +1945,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; @@ -1871,7 +1958,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) { @@ -1882,7 +1972,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; @@ -1902,6 +1997,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); @@ -2022,6 +2119,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; @@ -2206,10 +2307,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; @@ -2229,7 +2331,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_wrk: + 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 32317) +++ 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_wrk } 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 32317) +++ 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-c-old" +#define SVN_WC__CNFL_NEW_EXT ".svn-c-new" +#define SVN_WC__CNFL_WRK_EXT ".svn-c-wrk" @@ -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-c-old" +#define SVN_WC__ADM_DIR_PROP_CNFL_NEW "dir-prop-c-new" +#define SVN_WC__ADM_DIR_PROP_CNFL_WRK "dir-prop-c-wrk" #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 32317) +++ subversion/svn/diff-cmd.c (working copy) @@ -296,9 +296,15 @@ svn_cl__diff(apr_getopt_t *os, /* 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->start_revision.kind != svn_opt_revision_working + && opt_state->start_revision.kind != svn_opt_revision_old + && opt_state->start_revision.kind != svn_opt_revision_theirs + && opt_state->start_revision.kind != svn_opt_revision_mine) || (opt_state->end_revision.kind != svn_opt_revision_base - && opt_state->end_revision.kind != svn_opt_revision_working)) + && opt_state->end_revision.kind != svn_opt_revision_working + && opt_state->end_revision.kind != svn_opt_revision_old + && opt_state->end_revision.kind != svn_opt_revision_theirs + && opt_state->end_revision.kind != svn_opt_revision_mine)) pegged_diff = TRUE; }