| This branch implements the 'invoke-diff3-cmd' feature and is located |
| at: |
| |
| https://svn.apache.org/repos/asf/subversion/branches/invoke-diff3-feature/ |
| |
| It is a continuation of the invoke-diff-cmd-feature branch r1541740, |
| which is located here: |
| |
| https://svn.apache.org/repos/asf/subversion/branches/invoke-diff-cmd-feature/ |
| |
| It is a feature branch, receiving regular sync merges from /trunk, and |
| expected to be reintegrated back thereto. |
| |
| See: http://subversion.tigris.org/issues/show_bug.cgi?id=2044 for the |
| original motivation for this project. |
| |
| Index: |
| ====== |
| |
| 1. Introduction |
| 2. What --invoke-diff3-cmd provides |
| 3. Structure of the feature |
| API components |
| UI components |
| 4. Changes to the existing code structure |
| 5. Known Problems |
| 6. Tests |
| Automatic tests |
| Manual tests passed |
| Working copy functionality test |
| 7. TODO |
| 8. Log messages |
| 9. DIFF from invoke-diff-cmd branch 1541740 |
| |
| |
| 1. Introduction: |
| ================ |
| |
| --invoke-diff3-cmd allows command line, config file and interactive |
| selection of an external diff3 program. |
| |
| Currently this capability is provided by user written shell scripts |
| which are passed as the merge program via the svn config file, and on |
| the command line via the --diff3_cmd and the -x extensions. |
| |
| |
| 2. What --invoke-diff3-cmd provides |
| =================================== |
| |
| Users can type 'free-style' command lines for their selected diff3 |
| program, define a default command in the config file and also, |
| interactively select a diff3 program during the merge conflict |
| dialogue. |
| |
| |
| 3. Structure of the feature: |
| ============================ |
| |
| API components |
| -------------- |
| |
| * svn_merge5() has been deprecated to svn_merge6(). |
| |
| |
| UI components |
| ------------- |
| |
| --invoke-diff-cmd and its user interface components for the command |
| line have been installed everywhere where --diff-cmd is available. |
| |
| |
| Users can specify the following mutually exclusive options: |
| |
| --invoke-merge-cmd, --diff3-cmd , and also define what diff3 program |
| to use via the ./svn/config file, using the new config file |
| parameter "invoke-merge-cmd".. |
| |
| The selection first defaults to the config file entry (if defined) |
| which in turn is overridden when one of --invoke-diff3-cmd, |
| --diff-cmd are invoked. |
| |
| The interactive merge section has 2 new options: Interactive |
| selection of the config file define, and an input option for a |
| invoke-diff3-cmd command style instruction. |
| |
| |
| 4. Changes to the existing code structure: |
| ========================================== |
| |
| None. |
| |
| The existing --diff3-cmd has been in use for years and is best left |
| untouched, since many users will not require the extra services |
| offered by the --invoke-diff3-cmd feature, and any errors could cause |
| many users a lot of problems. |
| |
| Moreover, the nature of --invoke-diff3-cmd and --diff-cmd are quite |
| different; --diff-3 is tailored for GNU diff3 and carefully checks the |
| user's input of switches, whereas --invoke-diff3-cmd is a |
| free-style-anything-goes-including-shooting-your-foot creation. |
| |
| |
| Known Problems |
| ============== |
| |
| The error trace back in subversion/svn/conflict-callbacks.c is |
| defective in general since the response to a user error is 'traced |
| call' instead of the actual error being displayed. I've kept the |
| current shape to match the 'l' option, since I think that is a |
| seperate bug outside the scope of this patch. |
| |
| see line 902 ('l' option) |
| and line 940 ('i' option) |
| |
| Merge of 'subversion/libsvn_client/diff.c' aborted. |
| Select: (p) postpone, (df) show diff, (e) edit file, (m) merge, |
| (i) interactive invoke-diff3-cmd selection, |
| (mc) my side of conflict, (tc) their side of conflict, |
| (s) show all options: l |
| l |
| traced call |
| Select: (p) postpone, (df) show diff, (e) edit file, (m) merge, |
| (i) interactive invoke-diff3-cmd selection, |
| (mc) my side of conflict, (tc) their side of conflict, |
| (s) show all options: i |
| i |
| Enter the invoke-diff3-cmd: flobble |
| flobble |
| traced call |
| Select: (p) postpone, (df) show diff, (e) edit file, (m) merge, |
| (i) interactive invoke-diff3-cmd selection, |
| (mc) my side of conflict, (tc) their side of conflict, |
| (s) show all options: |
| |
| |
| 6. Tests |
| ======== |
| |
| Automatic tests |
| --------------- |
| |
| I could not find a pre-existing test for the --diff3-cmd option and so |
| I omitted writing an automated test for now. |
| |
| |
| Manual tests passed |
| ------------------- |
| |
| * config file selection '--invoke-diff3-cmd' invocation works. |
| overrides interactive invoke-diff3-cmd selection. |
| |
| * --invoke-diff3-cmd commandline invocation works. |
| overrides config file selection. |
| |
| * interactive --invoke-diff3-cmd commandline invocation works. |
| |
| * --diff3-cmd and --invoke-diff3-cmd mututally exclusive. |
| |
| |
| Working copy functionality test |
| ------------------------------- |
| |
| Create a suitable conflict to test the UI part of the feature: |
| |
| mkdir conflict |
| svn co -r r1526439 https://svn.apache.org/repos/asf/subversion/trunk/ trunk |
| svn co -r r1502389 https://svn.apache.org/repos/asf/subversion/branches//invoke-diff-cmd-feature branch |
| cd branch |
| svn /home/g/conflict/trunk |
| revert -R . |
| |
| The above produces 2 conflicts using the native svn merge command, and |
| 30 conflicts when kdiff3 is used. |
| |
| $ kdiff3 --version |
| Qt: 4.8.4 |
| KDE Development Platform: 4.10.5 |
| kdiff3: 0.9.97 (32 bit) |
| |
| |
| 7. TODO: |
| ======== |
| |
| * Dear Reviwer: please ensure I got the left and right merges the |
| correct way round. I think I have, but given that the left thumb |
| points to where the right hand is... %-) |
| |
| * Decide which smiley character to choose: ./subversion/svn/conflict-callbacks.c:122 |
| |
| |
| 8. Log messages |
| =============== |
| |
| * BRANCH-README |
| (*) Description of Branch. |
| |
| |
| * subversion/include/private/svn_wc_private.h |
| |
| (svn_wc__get_file_external_editor, |
| (svn_wc__get_update_editor, |
| (svn_wc__get_switch_editor): New parameter: |
| const char *invoke_diff3_cmd. |
| |
| |
| * subversion/include/svn_config.h |
| |
| (): New definition: SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD. |
| |
| |
| * subversion/include/svn_error_codes.h |
| |
| (command-line client errors): New definition: |
| SVN_ERR_CL_NO_EXTERNAL_DIFF3_TOOL. |
| |
| (SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS): Adjust numbering in order |
| to retain a logical grouping. |
| |
| * subversion/include/svn_io.h |
| |
| (svn_io_run_invoke_diff3): New function. |
| |
| |
| * subversion/include/svn_wc.h |
| |
| (svn_wc_merge6): New function. |
| |
| (svn_wc_merge5): Deprecate. |
| |
| |
| * subversion/libsvn_client/externals.c |
| |
| (switch_file_external): New variable: const char *invoke_diff3_cmd. |
| Initialize diff3_cmd to NULL. Add precedence logic and read the |
| config file to populate invoke_diff3_cmd. Add invoke_diff3_cmd in |
| call to svn_wc__get_file_external_editor(). |
| |
| |
| * subversion/libsvn_client/merge.c |
| |
| (merge_cmd_baton_t): New member *invoke_diff3_cmd. Adjust comment. |
| |
| (merge_file_changed): Replace call to svn_wc_merge5 with call to |
| svn_wc_merge6. |
| |
| (do_merge): New variable: const char *invoke_diff3_cmd. Initialize |
| diff3_cmd to NULL. Adjust comment. Add precedence logic and read |
| the config file to populate invoke_diff3_cmd. Add invoke_diff3_cmd |
| to merge_baton. |
| |
| |
| * subversion/libsvn_client/switch.c |
| |
| (switch_internal): New variable: const char *invoke_diff3_cmd. Add |
| precedence logic and read the config file to populate |
| invoke_diff3_cmd. Add invoke_diff3_cmd in call to |
| svn_wc__get_switch_editor(). |
| |
| |
| * subversion/libsvn_client/update.c |
| |
| (update_internal): Initialize diff3_cmd to NULL. New variable: |
| *invoke_diff3_cmd. Adjust comment, add precedence logic and read |
| the config file to populate invoke_diff3_cmd. Add |
| invoke_diff3_cmd in call to svn_wc__get_update_editor(). |
| |
| |
| * subversion/libsvn_subr/config_file.c |
| |
| (svn_config_ensure): Add entry for invoke-diff3-cmd. |
| |
| |
| * subversion/libsvn_subr/io.c |
| |
| (svn_io_run_invoke_diff3): New function. |
| |
| |
| * subversion/libsvn_wc/deprecated.c |
| |
| (svn_wc_get_update_editor4): Add invoke-diff3-cmd as NULL to call to |
| svn_wc__get_update_editor(). |
| |
| (svn_wc_get_switch_editor4): Add invoke-diff3-cmd as NULL to call to |
| svn_wc__get_switch_editor(). |
| |
| (svn_wc_merge5): New function. |
| |
| |
| * subversion/libsvn_wc/externals.c |
| |
| (struct edit_baton): Rename diff3cmd to diff3_cmd. New member |
| variable: const char *invoke_diff3_cmd. |
| |
| (close_file): Rename diff3cmd to diff3_cmd and add invoke_diff3_cmd |
| in call to svn_wc__perform_file_merge(). |
| |
| (svn_wc__get_file_external_editor): New parameter: const char |
| *invoke_diff3_cmd. Rename diff3cmd to diff3_cmd in eb |
| assignment and assign invoke_diff3_cmd to struct edit_baton eb. |
| |
| |
| * subversion/libsvn_wc/merge.c |
| |
| (merge_target_t): Adjust comment for old_actual_props. New member |
| variable: invoke_diff3_cmd. |
| |
| (do_text_merge_external): New parameter: const char |
| invoke_diff3_cmd. Add if condition to route call to newly added |
| svn_io_run_invoke_diff3(). Add clarifying braces around |
| A = (B == C) type assignment. |
| |
| (merge_text_file): Adjust if condition to route call to |
| do_text_merge_external() and add invoke_diff3_cmd to call to |
| do_text_merge_external(). |
| |
| (svn_wc__internal_merge): New parameter: const char |
| *invoke_diff3_cmd. Assign invoke_diff3_cmd to mt. Add |
| invoke_diff3_cmd as a conditional variable in to call to |
| detranslate_wc_file(). |
| |
| (svn_wc_merge6): Declare new API. |
| |
| |
| * subversion/libsvn_wc/update_editor.c |
| |
| (struct edit_baton): New member const char *invoke_diff3_cmd. |
| |
| (svn_wc__perform_file_merge): New parameter: const char |
| *invoke_diff3_cmd. Add invoke_diff3_cmd to |
| svn_wc__internal_merge(). |
| |
| (merge_file): Add invoke_diff3_cmd to svn_wc__internal_merge(). |
| |
| (make_editor): New parameter: const char *invoke_diff3_cmd. Assign |
| invoke_diff3_cmd to eb. |
| |
| (svn_wc__get_update_editor, svn_wc__get_switch_editor): New |
| parameter: const char *invoke_diff3_cmd. Add invoke_diff3_cmd to |
| call to make_editor(). |
| |
| |
| * subversion/libsvn_wc/wc.h |
| |
| (svn_wc__internal_merge): Adjust function comment. New parameter: |
| const char *invoke_diff3_cmd. Add newline to conform to general |
| formatting in this file. |
| |
| (svn_wc__perform_file_merge): New parameter: const char |
| *invoke_diff3_cmd. |
| |
| |
| * subversion/libsvn_wc/wc_db_update_move.c |
| |
| (update_working_file): Add invoke_diff3_cmd in call to |
| svn_wc__perform_file_merge() as NULL. Adjust other comments for |
| neatness. |
| |
| |
| * subversion/svn/cl.h |
| |
| (svn_cl__accept_t): New commented member: |
| svn_cl__accept_invoke_diff3_config. |
| |
| (): New definition: SVN_CL__ACCEPT_INVOKE_DIFF3_CONFIG. |
| |
| (svn_cl__opt_state_t): New member const char* invoke_diff3_cmd. |
| |
| (svn_cl__invoke_diff3_cmd_externally): New function. |
| |
| |
| * subversion/svn/conflict-callbacks.c |
| |
| (svn_cl__accept_from_word): Add SVN_CL__ACCEPT_INVOKE_DIFF3_CONFIG |
| to if condition returns svn_cl__accept_launch. |
| |
| (invoke_diff3_resolver): New routing function that calls |
| svn_cl__invoke_diff3_cmd_externally(). |
| |
| (text_conflict_options): Add "3f" option for invoke-diff3-cmd tool |
| selection and add 'i' option for interactive invoke-diff3-cmd |
| input. |
| |
| (handle_text_conflict): Add interactive selection code for |
| invoke-diff3-cmd input ("i" section) and invoke-diff3-cmd tool |
| ("3f" section) to if condition. Add '3f' and 'i' to next_option. |
| Adjust general indentation(2 entries). |
| |
| (conflict_func_interactive): Add cases for "i" and "3f" selection. |
| Adjust general indentation. Add case |
| 'svn_cl__accept_invoke_diff3_config'. |
| |
| |
| * subversion/svn/svn.c |
| |
| (svn_cl__longopt_t): New member 'opt_diff3_cmd'. |
| |
| (svn_cl__options): Add 'invoke-diff3-cmd' entry plus help text. |
| |
| (svn_cl__cmd_table,"cleanup","merge","switch","update"): Add |
| opt_invoke_diff3_cmd. |
| |
| (sub_main): Add case opt_invoke_diff3_cmd. Ensure murutal |
| exclusiveness of diff3-cmd and invoke-diff3-cmd. Add call to |
| svn_config_set(). Add error check for |
| svn_cl__accept_invoke_diff3_config. |
| |
| |
| * subversion/svn/util.c |
| |
| (): Include svn_io_private.h |
| |
| (svn_cl__invoke_diff3_cmd_externally): New function. |
| |
| |
| * subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout |
| |
| (): Update help output. |
| |
| |
| |
| 9. DIFF from invoke-diff-cmd branch 1541740: |
| ============================================ |
| |
| Index: subversion/include/private/svn_wc_private.h |
| =================================================================== |
| --- subversion/include/private/svn_wc_private.h (revision 1542103) |
| +++ subversion/include/private/svn_wc_private.h (working copy) |
| @@ -77,6 +77,7 @@ svn_wc__get_file_external_editor(const svn_delta_e |
| apr_array_header_t *iprops, |
| svn_boolean_t use_commit_times, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| const char *record_ancestor_abspath, |
| const char *recorded_url, |
| @@ -1507,6 +1508,7 @@ svn_wc__get_update_editor(const svn_delta_editor_t |
| svn_boolean_t server_performs_filtering, |
| svn_boolean_t clean_checkout, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| svn_wc_dirents_func_t fetch_dirents_func, |
| void *fetch_dirents_baton, |
| @@ -1549,6 +1551,7 @@ svn_wc__get_switch_editor(const svn_delta_editor_t |
| svn_boolean_t allow_unver_obstructions, |
| svn_boolean_t server_performs_filtering, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| svn_wc_dirents_func_t fetch_dirents_func, |
| void *fetch_dirents_baton, |
| Index: subversion/include/svn_config.h |
| =================================================================== |
| --- subversion/include/svn_config.h (revision 1542103) |
| +++ subversion/include/svn_config.h (working copy) |
| @@ -116,6 +116,8 @@ typedef struct svn_config_t svn_config_t; |
| #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG "diff3-has-program-arg" |
| /** @since New in 1.9. */ |
| #define SVN_CONFIG_OPTION_INVOKE_DIFF_CMD "invoke-diff-cmd" |
| +/** @since New in 1.9. */ |
| +#define SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD "invoke-diff3-cmd" |
| #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD "merge-tool-cmd" |
| #define SVN_CONFIG_SECTION_MISCELLANY "miscellany" |
| #define SVN_CONFIG_OPTION_GLOBAL_IGNORES "global-ignores" |
| Index: subversion/include/svn_error_codes.h |
| =================================================================== |
| --- subversion/include/svn_error_codes.h (revision 1542103) |
| +++ subversion/include/svn_error_codes.h (working copy) |
| @@ -1451,8 +1451,13 @@ SVN_ERROR_START |
| SVN_ERR_CL_CATEGORY_START + 10, |
| "No external merge tool available") |
| |
| + /** @since New in 1.9. */ |
| + SVN_ERRDEF(SVN_ERR_CL_NO_EXTERNAL_DIFF3_TOOL, |
| + SVN_ERR_CL_CATEGORY_START + 11, |
| + "No external invoke-diff3 tool available") |
| + |
| SVN_ERRDEF(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, |
| - SVN_ERR_CL_CATEGORY_START + 11, |
| + SVN_ERR_CL_CATEGORY_START + 12, |
| "Failed processing one or more externals definitions") |
| |
| /* ra_svn errors */ |
| Index: subversion/include/svn_io.h |
| =================================================================== |
| --- subversion/include/svn_io.h (revision 1542103) |
| +++ subversion/include/svn_io.h (working copy) |
| @@ -2378,6 +2378,25 @@ svn_io_run_external_diff(const char *dir, |
| const char *external_diff_cmd, |
| apr_pool_t *scratch_pool); |
| |
| +/** Run the external merge command defined by the invoke-diff3-cmd |
| + * option. |
| + * |
| + * @since New in 1.9. |
| + */ |
| +svn_error_t * |
| +svn_io_run_invoke_diff3(int *exitcode, |
| + const char *dir, |
| + const char *mine, |
| + const char *older, |
| + const char *yours, |
| + const char *mine_label, |
| + const char *older_label, |
| + const char *yours_label, |
| + apr_file_t *merged, |
| + const char *diff3_cmd, |
| + apr_pool_t *pool); |
| + |
| + |
| #ifdef __cplusplus |
| } |
| #endif /* __cplusplus */ |
| Index: subversion/include/svn_wc.h |
| =================================================================== |
| --- subversion/include/svn_wc.h (revision 1542103) |
| +++ subversion/include/svn_wc.h (working copy) |
| @@ -7011,6 +7011,35 @@ typedef enum svn_wc_merge_outcome_t |
| * @since New in 1.8. |
| */ |
| svn_error_t * |
| +svn_wc_merge6(enum svn_wc_merge_outcome_t *merge_content_outcome, |
| + enum svn_wc_notify_state_t *merge_props_state, |
| + svn_wc_context_t *wc_ctx, |
| + const char *left_abspath, |
| + const char *right_abspath, |
| + const char *target_abspath, |
| + const char *left_label, |
| + const char *right_label, |
| + const char *target_label, |
| + const svn_wc_conflict_version_t *left_version, |
| + const svn_wc_conflict_version_t *right_version, |
| + svn_boolean_t dry_run, |
| + const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| + const apr_array_header_t *merge_options, |
| + apr_hash_t *original_props, |
| + const apr_array_header_t *prop_diff, |
| + svn_wc_conflict_resolver_func2_t conflict_func, |
| + void *conflict_baton, |
| + svn_cancel_func_t cancel_func, |
| + void *cancel_baton, |
| + apr_pool_t *scratch_pool); |
| + |
| +/** Similar to svn_wc_merge6() but with @a invoke_diff3_cmd. |
| + * |
| + * @since New in 1.9. |
| + */ |
| +SVN_DEPRECATED |
| +svn_error_t * |
| svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, |
| enum svn_wc_notify_state_t *merge_props_state, |
| svn_wc_context_t *wc_ctx, |
| Index: subversion/libsvn_client/externals.c |
| =================================================================== |
| --- subversion/libsvn_client/externals.c (revision 1542103) |
| +++ subversion/libsvn_client/externals.c (working copy) |
| @@ -364,7 +364,8 @@ switch_file_external(const char *local_abspath, |
| ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) |
| : NULL; |
| svn_boolean_t use_commit_times; |
| - const char *diff3_cmd; |
| + const char *diff3_cmd = NULL; |
| + const char *invoke_diff3_cmd = NULL; |
| const char *preserved_exts_str; |
| const apr_array_header_t *preserved_exts; |
| svn_node_kind_t kind, external_kind; |
| @@ -376,13 +377,23 @@ switch_file_external(const char *local_abspath, |
| SVN_CONFIG_SECTION_MISCELLANY, |
| SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); |
| |
| - /* Get the external diff3, if any. */ |
| + /* Get the external *_diff3_cmd, if any. |
| + Precedence: If there is no invoke_diff3_cmd on the cmd line, |
| + check if there is a diff3-cmd in the config file. If there is, |
| + do not check invoke_diff3_cmd in the config file.*/ |
| svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| SVN_CONFIG_OPTION_DIFF3_CMD, NULL); |
| |
| if (diff3_cmd != NULL) |
| SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); |
| - |
| + else |
| + { |
| + svn_config_get(cfg, &invoke_diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL); |
| + if (invoke_diff3_cmd != NULL) |
| + SVN_ERR(svn_path_cstring_to_utf8(&invoke_diff3_cmd, |
| + invoke_diff3_cmd, scratch_pool)); |
| + } |
| /* See which files the user wants to preserve the extension of when |
| conflict files are made. */ |
| svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, |
| @@ -483,7 +494,8 @@ switch_file_external(const char *local_abspath, |
| switch_loc->repos_uuid, |
| inherited_props, |
| use_commit_times, |
| - diff3_cmd, preserved_exts, |
| + diff3_cmd, invoke_diff3_cmd, |
| + preserved_exts, |
| def_dir_abspath, |
| url, peg_revision, revision, |
| ctx->conflict_func2, |
| Index: subversion/libsvn_client/merge.c |
| =================================================================== |
| --- subversion/libsvn_client/merge.c (revision 1542103) |
| +++ subversion/libsvn_client/merge.c (working copy) |
| @@ -316,10 +316,13 @@ typedef struct merge_cmd_baton_t { |
| /* A list of tree conflict victim absolute paths which may be NULL. */ |
| apr_hash_t *tree_conflicted_abspaths; |
| |
| - /* The diff3_cmd in ctx->config, if any, else null. We could just |
| - extract this as needed, but since more than one caller uses it, |
| - we just set it up when this baton is created. */ |
| + /* The diff3_cmd and invoke_diff3_cmd in ctx->config, if any, else |
| + null. We could just extract this as needed, but since more than |
| + one caller uses it, we just set it up when this baton is |
| + created. */ |
| const char *diff3_cmd; |
| + const char *invoke_diff3_cmd; |
| + |
| const apr_array_header_t *merge_options; |
| |
| /* RA sessions used throughout a merge operation. Opened/re-parented |
| @@ -2040,11 +2043,13 @@ merge_file_changed(const char *relpath, |
| |
| /* Do property merge and text merge in one step so that keyword expansion |
| takes into account the new property values. */ |
| - SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx, |
| + SVN_ERR(svn_wc_merge6(&content_outcome, &property_state, ctx->wc_ctx, |
| left_file, right_file, local_abspath, |
| left_label, right_label, target_label, |
| left, right, |
| - merge_b->dry_run, merge_b->diff3_cmd, |
| + merge_b->dry_run, |
| + merge_b->diff3_cmd, |
| + merge_b->invoke_diff3_cmd, |
| merge_b->merge_options, |
| left_props, prop_changes, |
| NULL, NULL, |
| @@ -9667,7 +9672,8 @@ do_merge(apr_hash_t **modified_subtrees, |
| { |
| merge_cmd_baton_t merge_cmd_baton = { 0 }; |
| svn_config_t *cfg; |
| - const char *diff3_cmd; |
| + const char *diff3_cmd = NULL; |
| + const char *invoke_diff3_cmd = NULL; |
| int i; |
| svn_boolean_t checked_mergeinfo_capability = FALSE; |
| svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL; |
| @@ -9718,7 +9724,11 @@ do_merge(apr_hash_t **modified_subtrees, |
| if (depth == svn_depth_unknown) |
| depth = svn_depth_infinity; |
| |
| - /* Set up the diff3 command, so various callers don't have to. */ |
| + /* Get the external *_diff3_cmd, if any. |
| + Precedence: If there is no invoke_diff3_cmd on the cmd line, |
| + check if there is a diff3-cmd in the config file. If there is, |
| + do not check invoke_diff3_cmd in the config file.*/ |
| + |
| cfg = ctx->config |
| ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) |
| : NULL; |
| @@ -9727,6 +9737,14 @@ do_merge(apr_hash_t **modified_subtrees, |
| |
| if (diff3_cmd != NULL) |
| SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); |
| + else |
| + { |
| + svn_config_get(cfg, &invoke_diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL); |
| + if (invoke_diff3_cmd != NULL) |
| + SVN_ERR(svn_path_cstring_to_utf8(&invoke_diff3_cmd, |
| + invoke_diff3_cmd, scratch_pool)); |
| + } |
| |
| /* Build the merge context baton (or at least the parts of it that |
| don't need to be reset for each merge source). */ |
| @@ -9743,6 +9761,7 @@ do_merge(apr_hash_t **modified_subtrees, |
| merge_cmd_baton.pool = iterpool; |
| merge_cmd_baton.merge_options = merge_options; |
| merge_cmd_baton.diff3_cmd = diff3_cmd; |
| + merge_cmd_baton.invoke_diff3_cmd = invoke_diff3_cmd; |
| merge_cmd_baton.use_sleep = use_sleep; |
| |
| /* Do we already know the specific subtrees with mergeinfo we want |
| Index: subversion/libsvn_client/switch.c |
| =================================================================== |
| --- subversion/libsvn_client/switch.c (revision 1542103) |
| +++ subversion/libsvn_client/switch.c (working copy) |
| @@ -106,6 +106,7 @@ switch_internal(svn_revnum_t *result_rev, |
| svn_ra_session_t *ra_session; |
| svn_revnum_t revnum; |
| const char *diff3_cmd; |
| + const char *invoke_diff3_cmd; |
| apr_hash_t *wcroot_iprops; |
| apr_array_header_t *inherited_props; |
| svn_boolean_t use_commit_times; |
| @@ -134,7 +135,18 @@ switch_internal(svn_revnum_t *result_rev, |
| |
| if (diff3_cmd != NULL) |
| SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); |
| + else |
| + /* Get the external invoke_diff3_cmd, if any. */ |
| + svn_config_get(cfg, &invoke_diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL); |
| |
| + if (diff3_cmd != NULL) |
| + SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); |
| + else |
| + if (invoke_diff3_cmd != NULL) |
| + SVN_ERR(svn_path_cstring_to_utf8(&invoke_diff3_cmd, invoke_diff3_cmd, pool)); |
| + |
| + |
| /* See if the user wants last-commit timestamps instead of current ones. */ |
| SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, |
| SVN_CONFIG_SECTION_MISCELLANY, |
| @@ -312,7 +324,8 @@ switch_internal(svn_revnum_t *result_rev, |
| use_commit_times, depth, |
| depth_is_sticky, allow_unver_obstructions, |
| server_supports_depth, |
| - diff3_cmd, preserved_exts, |
| + diff3_cmd, invoke_diff3_cmd, |
| + preserved_exts, |
| svn_client__dirent_fetcher, &dfb, |
| conflicted_paths ? record_conflict : NULL, |
| conflicted_paths, |
| Index: subversion/libsvn_client/update.c |
| =================================================================== |
| --- subversion/libsvn_client/update.c (revision 1542103) |
| +++ subversion/libsvn_client/update.c (working copy) |
| @@ -226,7 +226,8 @@ update_internal(svn_revnum_t *result_rev, |
| svn_revnum_t revnum; |
| svn_boolean_t use_commit_times; |
| svn_boolean_t clean_checkout = FALSE; |
| - const char *diff3_cmd; |
| + const char *diff3_cmd = NULL; |
| + const char *invoke_diff3_cmd = NULL; |
| apr_hash_t *wcroot_iprops; |
| svn_opt_revision_t opt_rev; |
| svn_ra_session_t *ra_session; |
| @@ -332,13 +333,24 @@ update_internal(svn_revnum_t *result_rev, |
| /* check whether the "clean c/o" optimization is applicable */ |
| SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, pool)); |
| |
| - /* Get the external diff3, if any. */ |
| + /* Get the external *_diff3_cmd, if any. |
| + Precedence: If there is no invoke_diff3_cmd on the cmd line, |
| + check if there is a diff3-cmd in the config file. If there is, |
| + do not check invoke_diff3_cmd in the config file.*/ |
| svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| SVN_CONFIG_OPTION_DIFF3_CMD, NULL); |
| |
| if (diff3_cmd != NULL) |
| SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); |
| - |
| + else |
| + { |
| + svn_config_get(cfg, &invoke_diff3_cmd, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL); |
| + if (invoke_diff3_cmd != NULL) |
| + SVN_ERR(svn_path_cstring_to_utf8(&invoke_diff3_cmd, |
| + invoke_diff3_cmd, pool)); |
| + } |
| + |
| /* See if the user wants last-commit timestamps instead of current ones. */ |
| SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, |
| SVN_CONFIG_SECTION_MISCELLANY, |
| @@ -429,7 +441,8 @@ update_internal(svn_revnum_t *result_rev, |
| adds_as_modification, |
| server_supports_depth, |
| clean_checkout, |
| - diff3_cmd, preserved_exts, |
| + diff3_cmd, invoke_diff3_cmd, |
| + preserved_exts, |
| svn_client__dirent_fetcher, &dfb, |
| conflicted_paths ? record_conflict : NULL, |
| conflicted_paths, |
| Index: subversion/libsvn_subr/config_file.c |
| =================================================================== |
| --- subversion/libsvn_subr/config_file.c (revision 1542103) |
| +++ subversion/libsvn_subr/config_file.c (working copy) |
| @@ -1192,6 +1192,11 @@ svn_config_ensure(const char *config_dir, apr_pool |
| "### This will override the compile-time default, which is to use" NL |
| "### Subversion's internal diff implementation." NL |
| "# invoke-diff-cmd = (see svn help diff for examples)" NL |
| + "### Set invoke-diff3-cmd to the absolute path of your 'diff'" NL |
| + "### program." NL |
| + "### This will override the compile-time default, which is to use" NL |
| + "### Subversion's internal merge implementation." NL |
| + "# invoke-diff3-cmd = (see svn help merge for examples)" NL |
| "### Set merge-tool-cmd to the command used to invoke your external" NL |
| "### merging tool of choice. Subversion will pass 5 arguments to" NL |
| "### the specified command: base theirs mine merged wcfile" NL |
| Index: subversion/libsvn_subr/io.c |
| =================================================================== |
| --- subversion/libsvn_subr/io.c (revision 1542103) |
| +++ subversion/libsvn_subr/io.c (working copy) |
| @@ -3371,7 +3371,60 @@ svn_io_run_diff3_3(int *exitcode, |
| return SVN_NO_ERROR; |
| } |
| |
| +svn_error_t * |
| +svn_io_run_invoke_diff3(int *exitcode, |
| + const char *dir, |
| + const char *mine, |
| + const char *older, |
| + const char *yours, |
| + const char *mine_label, |
| + const char *older_label, |
| + const char *yours_label, |
| + apr_file_t *merged, |
| + const char *invoke_diff3_cmd, |
| + apr_pool_t *pool) |
| +{ |
| |
| + const char ** cmd; |
| + |
| + apr_pool_t *scratch_pool = svn_pool_create(pool); |
| + |
| + if (0 == strlen(invoke_diff3_cmd)) |
| + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL); |
| + |
| + cmd = svn_io__create_custom_diff_cmd(mine_label, yours_label, older_label, |
| + mine, yours, older, |
| + invoke_diff3_cmd, scratch_pool); |
| + |
| + SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, exitcode, NULL, TRUE, |
| + NULL, merged, NULL, scratch_pool)); |
| + |
| + |
| + /* According to the diff3 docs, a '0' means the merge was clean, and |
| + '1' means conflict markers were found. Anything else is real |
| + error. */ |
| + if ((*exitcode != 0) && (*exitcode != 1)) |
| + { |
| + |
| + int i; |
| + const char *failed_command = ""; |
| + |
| + for (i = 0; cmd[i]; ++i) |
| + failed_command = apr_pstrcat(pool, failed_command, |
| + cmd[i], " ", (char*) NULL); |
| + svn_pool_destroy(scratch_pool); |
| + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, |
| + _("'%s' was expanded to '%s' and returned %d"), |
| + invoke_diff3_cmd, |
| + failed_command, |
| + *exitcode); |
| + } |
| + else |
| + svn_pool_destroy(scratch_pool); |
| + |
| + return SVN_NO_ERROR; |
| +} |
| + |
| /* Canonicalize a string for hashing. Modifies KEY in place. */ |
| static APR_INLINE char * |
| fileext_tolower(char *key) |
| Index: subversion/libsvn_wc/deprecated.c |
| =================================================================== |
| --- subversion/libsvn_wc/deprecated.c (revision 1542103) |
| +++ subversion/libsvn_wc/deprecated.c (working copy) |
| @@ -3492,6 +3492,7 @@ svn_wc_get_update_editor4(const svn_delta_editor_t |
| server_performs_filtering, |
| clean_checkout, |
| diff3_cmd, |
| + NULL, |
| preserved_exts, |
| fetch_dirents_func, fetch_dirents_baton, |
| conflict_func, conflict_baton, |
| @@ -3676,6 +3677,7 @@ svn_wc_get_switch_editor4(const svn_delta_editor_t |
| allow_unver_obstructions, |
| server_performs_filtering, |
| diff3_cmd, |
| + NULL, |
| preserved_exts, |
| fetch_dirents_func, fetch_dirents_baton, |
| conflict_func, conflict_baton, |
| @@ -4402,6 +4404,53 @@ svn_wc_copy(const char *src_path, |
| /*** From merge.c ***/ |
| |
| svn_error_t * |
| +svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, |
| + enum svn_wc_notify_state_t *merge_props_outcome, |
| + svn_wc_context_t *wc_ctx, |
| + const char *left_abspath, |
| + const char *right_abspath, |
| + const char *target_abspath, |
| + const char *left_label, |
| + const char *right_label, |
| + const char *target_label, |
| + const svn_wc_conflict_version_t *left_version, |
| + const svn_wc_conflict_version_t *right_version, |
| + svn_boolean_t dry_run, |
| + const char *diff3_cmd, |
| + const apr_array_header_t *merge_options, |
| + apr_hash_t *original_props, |
| + const apr_array_header_t *prop_diff, |
| + svn_wc_conflict_resolver_func2_t conflict_func, |
| + void *conflict_baton, |
| + svn_cancel_func_t cancel_func, |
| + void *cancel_baton, |
| + apr_pool_t *scratch_pool) |
| +{ |
| + return svn_wc_merge6(merge_content_outcome, |
| + merge_props_outcome, |
| + wc_ctx, |
| + left_abspath, |
| + right_abspath, |
| + target_abspath, |
| + left_label, |
| + right_label, |
| + target_label, |
| + left_version, |
| + right_version, |
| + dry_run, |
| + diff3_cmd, |
| + NULL, |
| + merge_options, |
| + original_props, |
| + prop_diff, |
| + conflict_func, |
| + conflict_baton, |
| + cancel_func, |
| + cancel_baton, |
| + scratch_pool); |
| +} |
| + |
| +svn_error_t * |
| svn_wc_merge4(enum svn_wc_merge_outcome_t *merge_outcome, |
| svn_wc_context_t *wc_ctx, |
| const char *left_abspath, |
| @@ -4816,3 +4865,4 @@ svn_wc__conflict_description2_dup(const svn_wc_con |
| |
| return new_conflict; |
| } |
| + |
| Index: subversion/libsvn_wc/externals.c |
| =================================================================== |
| --- subversion/libsvn_wc/externals.c (revision 1542103) |
| +++ subversion/libsvn_wc/externals.c (working copy) |
| @@ -402,7 +402,8 @@ struct edit_baton |
| /* Information from the caller */ |
| svn_boolean_t use_commit_times; |
| const apr_array_header_t *ext_patterns; |
| - const char *diff3cmd; |
| + const char *diff3_cmd; |
| + const char *invoke_diff3_cmd; |
| |
| const char *url; |
| const char *repos_root_url; |
| @@ -813,7 +814,8 @@ close_file(void *file_baton, |
| eb->original_revision, |
| *eb->target_revision, |
| eb->propchanges, |
| - eb->diff3cmd, |
| + eb->diff3_cmd, |
| + eb->invoke_diff3_cmd, |
| eb->cancel_func, |
| eb->cancel_baton, |
| pool, pool)); |
| @@ -984,6 +986,7 @@ svn_wc__get_file_external_editor(const svn_delta_e |
| apr_array_header_t *iprops, |
| svn_boolean_t use_commit_times, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| const char *record_ancestor_abspath, |
| const char *recorded_url, |
| @@ -1021,7 +1024,8 @@ svn_wc__get_file_external_editor(const svn_delta_e |
| |
| eb->use_commit_times = use_commit_times; |
| eb->ext_patterns = preserved_exts; |
| - eb->diff3cmd = diff3_cmd; |
| + eb->diff3_cmd = diff3_cmd; |
| + eb->invoke_diff3_cmd = invoke_diff3_cmd; |
| |
| eb->record_ancestor_abspath = apr_pstrdup(edit_pool,record_ancestor_abspath); |
| eb->recorded_repos_relpath = svn_uri_skip_ancestor(repos_root_url, recorded_url, |
| Index: subversion/libsvn_wc/merge.c |
| =================================================================== |
| --- subversion/libsvn_wc/merge.c (revision 1542103) |
| +++ subversion/libsvn_wc/merge.c (working copy) |
| @@ -45,11 +45,12 @@ typedef struct merge_target_t |
| const char *local_abspath; /* The absolute path to target */ |
| const char *wri_abspath; /* The working copy of target */ |
| |
| - apr_hash_t *old_actual_props; /* The set of actual properties |
| + apr_hash_t *old_actual_props; /* The set of actual properties |
| before merging */ |
| const apr_array_header_t *prop_diff; /* The property changes */ |
| |
| const char *diff3_cmd; /* The diff3 command and options */ |
| + const char *invoke_diff3_cmd; /* The invoke_diff3_cmd command */ |
| const apr_array_header_t *merge_options; |
| |
| } merge_target_t; |
| @@ -437,6 +438,7 @@ static svn_error_t * |
| do_text_merge_external(svn_boolean_t *contains_conflicts, |
| apr_file_t *result_f, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *merge_options, |
| const char *detranslated_target, |
| const char *left_abspath, |
| @@ -448,17 +450,26 @@ do_text_merge_external(svn_boolean_t *contains_con |
| { |
| int exit_code; |
| |
| - SVN_ERR(svn_io_run_diff3_3(&exit_code, ".", |
| - detranslated_target, left_abspath, right_abspath, |
| - target_label, left_label, right_label, |
| - result_f, diff3_cmd, |
| - merge_options, scratch_pool)); |
| + if (diff3_cmd) |
| + SVN_ERR(svn_io_run_diff3_3(&exit_code, ".", |
| + detranslated_target, left_abspath, right_abspath, |
| + target_label, left_label, right_label, |
| + result_f, diff3_cmd, |
| + merge_options, scratch_pool)); |
| + else |
| + SVN_ERR(svn_io_run_invoke_diff3(&exit_code, ".", |
| + detranslated_target, |
| + left_abspath, right_abspath, |
| + target_label, left_label, right_label, |
| + result_f, invoke_diff3_cmd, |
| + scratch_pool)); |
| + |
| + *contains_conflicts = (exit_code == 1); |
| |
| - *contains_conflicts = exit_code == 1; |
| - |
| return SVN_NO_ERROR; |
| } |
| |
| + |
| /* Preserve the three pre-merge files. |
| |
| Create three empty files, with unique names that each include the |
| @@ -843,11 +854,13 @@ merge_text_file(svn_skel_t **work_items, |
| temp_dir, base_name, ".tmp", |
| svn_io_file_del_none, pool, pool)); |
| |
| - /* Run the external or internal merge, as requested. */ |
| - if (mt->diff3_cmd) |
| + /* Run the external (old-style or new-style) or internal merge, as |
| + requested. */ |
| + if (mt->diff3_cmd || mt->invoke_diff3_cmd) |
| SVN_ERR(do_text_merge_external(&contains_conflicts, |
| result_f, |
| mt->diff3_cmd, |
| + mt->invoke_diff3_cmd, |
| mt->merge_options, |
| detranslated_target_abspath, |
| left_abspath, |
| @@ -1080,6 +1093,7 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| apr_hash_t *old_actual_props, |
| svn_boolean_t dry_run, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *merge_options, |
| const apr_array_header_t *prop_diff, |
| svn_cancel_func_t cancel_func, |
| @@ -1106,6 +1120,7 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| mt.old_actual_props = old_actual_props; |
| mt.prop_diff = prop_diff; |
| mt.diff3_cmd = diff3_cmd; |
| + mt.invoke_diff3_cmd = invoke_diff3_cmd; |
| mt.merge_options = merge_options; |
| |
| /* Decide if the merge target is a text or binary file. */ |
| @@ -1121,7 +1136,9 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| } |
| |
| SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt, |
| - (! is_binary) && diff3_cmd != NULL, |
| + (! is_binary) |
| + && diff3_cmd != NULL |
| + && invoke_diff3_cmd != NULL, |
| target_abspath, |
| cancel_func, cancel_baton, |
| scratch_pool, scratch_pool)); |
| @@ -1193,7 +1210,7 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| |
| |
| svn_error_t * |
| -svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, |
| +svn_wc_merge6(enum svn_wc_merge_outcome_t *merge_content_outcome, |
| enum svn_wc_notify_state_t *merge_props_outcome, |
| svn_wc_context_t *wc_ctx, |
| const char *left_abspath, |
| @@ -1206,6 +1223,7 @@ svn_error_t * |
| const svn_wc_conflict_version_t *right_version, |
| svn_boolean_t dry_run, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *merge_options, |
| apr_hash_t *original_props, |
| const apr_array_header_t *prop_diff, |
| @@ -1345,6 +1363,7 @@ svn_error_t * |
| old_actual_props, |
| dry_run, |
| diff3_cmd, |
| + invoke_diff3_cmd, |
| merge_options, |
| prop_diff, |
| cancel_func, cancel_baton, |
| Index: subversion/libsvn_wc/update_editor.c |
| =================================================================== |
| --- subversion/libsvn_wc/update_editor.c (revision 1542103) |
| +++ subversion/libsvn_wc/update_editor.c (working copy) |
| @@ -223,6 +223,10 @@ struct edit_baton |
| internal merge code is used). */ |
| const char *diff3_cmd; |
| |
| + /* External custom invoke diff3 to use for merges (can be null, in |
| + which case internal merge code is used). */ |
| + const char *invoke_diff3_cmd; |
| + |
| /* Externals handler */ |
| svn_wc_external_update_t external_func; |
| void *external_baton; |
| @@ -3900,6 +3904,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items |
| svn_revnum_t target_revision, |
| const apr_array_header_t *propchanges, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| svn_cancel_func_t cancel_func, |
| void *cancel_baton, |
| apr_pool_t *result_pool, |
| @@ -3977,7 +3982,8 @@ svn_wc__perform_file_merge(svn_skel_t **work_items |
| oldrev_str, newrev_str, mine_str, |
| old_actual_props, |
| FALSE /* dry_run */, |
| - diff3_cmd, NULL, propchanges, |
| + diff3_cmd, invoke_diff3_cmd, |
| + NULL, propchanges, |
| cancel_func, cancel_baton, |
| result_pool, scratch_pool)); |
| |
| @@ -4138,6 +4144,7 @@ merge_file(svn_skel_t **work_items, |
| *eb->target_revision, |
| fb->propchanges, |
| eb->diff3_cmd, |
| + eb->invoke_diff3_cmd, |
| eb->cancel_func, eb->cancel_baton, |
| result_pool, scratch_pool)); |
| } /* end: working file exists and has mods */ |
| @@ -4831,6 +4838,7 @@ make_editor(svn_revnum_t *target_revision, |
| svn_wc_external_update_t external_func, |
| void *external_baton, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| const svn_delta_editor_t **editor, |
| void **edit_baton, |
| @@ -4901,6 +4909,7 @@ make_editor(svn_revnum_t *target_revision, |
| eb->external_func = external_func; |
| eb->external_baton = external_baton; |
| eb->diff3_cmd = diff3_cmd; |
| + eb->invoke_diff3_cmd = invoke_diff3_cmd; |
| eb->cancel_func = cancel_func; |
| eb->cancel_baton = cancel_baton; |
| eb->conflict_func = conflict_func; |
| @@ -5107,6 +5116,7 @@ svn_wc__get_update_editor(const svn_delta_editor_t |
| svn_boolean_t server_performs_filtering, |
| svn_boolean_t clean_checkout, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| svn_wc_dirents_func_t fetch_dirents_func, |
| void *fetch_dirents_baton, |
| @@ -5131,7 +5141,8 @@ svn_wc__get_update_editor(const svn_delta_editor_t |
| fetch_dirents_func, fetch_dirents_baton, |
| conflict_func, conflict_baton, |
| external_func, external_baton, |
| - diff3_cmd, preserved_exts, editor, edit_baton, |
| + diff3_cmd, invoke_diff3_cmd, |
| + preserved_exts, editor, edit_baton, |
| result_pool, scratch_pool); |
| } |
| |
| @@ -5150,6 +5161,7 @@ svn_wc__get_switch_editor(const svn_delta_editor_t |
| svn_boolean_t allow_unver_obstructions, |
| svn_boolean_t server_performs_filtering, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| const apr_array_header_t *preserved_exts, |
| svn_wc_dirents_func_t fetch_dirents_func, |
| void *fetch_dirents_baton, |
| @@ -5178,7 +5190,8 @@ svn_wc__get_switch_editor(const svn_delta_editor_t |
| fetch_dirents_func, fetch_dirents_baton, |
| conflict_func, conflict_baton, |
| external_func, external_baton, |
| - diff3_cmd, preserved_exts, |
| + diff3_cmd, invoke_diff3_cmd, |
| + preserved_exts, |
| editor, edit_baton, |
| result_pool, scratch_pool); |
| } |
| Index: subversion/libsvn_wc/wc.h |
| =================================================================== |
| --- subversion/libsvn_wc/wc.h (revision 1542103) |
| +++ subversion/libsvn_wc/wc.h (working copy) |
| @@ -413,8 +413,8 @@ svn_wc__internal_file_modified_p(svn_boolean_t *mo |
| |
| When DRY_RUN is true, no actual changes are made to the working copy. |
| |
| - If DIFF3_CMD is specified, the given external diff3 tool will |
| - be used instead of our built in diff3 routines. |
| + If DIFF3_CMD or INVOKE_DIFF3_CMD is specified, the given external |
| + diff3 tool will be used instead of our built in diff3 routines. |
| |
| When MERGE_OPTIONS are specified, they are used by the internal |
| diff3 routines, or passed to the external diff3 tool. |
| @@ -453,6 +453,7 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| const char *target_label, |
| apr_hash_t *old_actual_props, |
| svn_boolean_t dry_run, |
| + const char *invoke_diff3_cmd, |
| const char *diff3_cmd, |
| const apr_array_header_t *merge_options, |
| const apr_array_header_t *prop_diff, |
| @@ -461,6 +462,7 @@ svn_wc__internal_merge(svn_skel_t **work_items, |
| apr_pool_t *result_pool, |
| apr_pool_t *scratch_pool); |
| |
| + |
| /* A default error handler for svn_wc_walk_entries3(). Returns ERR in |
| all cases. */ |
| svn_error_t * |
| @@ -730,6 +732,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items |
| svn_revnum_t target_revision, |
| const apr_array_header_t *propchanges, |
| const char *diff3_cmd, |
| + const char *invoke_diff3_cmd, |
| svn_cancel_func_t cancel_func, |
| void *cancel_baton, |
| apr_pool_t *result_pool, |
| Index: subversion/libsvn_wc/wc_db_update_move.c |
| =================================================================== |
| --- subversion/libsvn_wc/wc_db_update_move.c (revision 1542103) |
| +++ subversion/libsvn_wc/wc_db_update_move.c (working copy) |
| @@ -961,8 +961,9 @@ update_working_file(const char *local_relpath, |
| NULL, NULL, NULL, /* diff labels */ |
| actual_props, |
| FALSE, /* dry-run */ |
| - NULL, /* diff3-cmd */ |
| - NULL, /* merge options */ |
| + NULL, /* diff3-cmd */ |
| + NULL, /* invoke-diff3-cmd */ |
| + NULL, /* merge options */ |
| propchanges, |
| NULL, NULL, /* cancel_func + baton */ |
| scratch_pool, scratch_pool)); |
| Index: subversion/svn/cl.h |
| =================================================================== |
| --- subversion/svn/cl.h (revision 1542103) |
| +++ subversion/svn/cl.h (working copy) |
| @@ -83,8 +83,12 @@ typedef enum svn_cl__accept_t |
| svn_cl__accept_edit, |
| |
| /* Launch user's resolver and resolve conflict with edited file. */ |
| - svn_cl__accept_launch |
| + svn_cl__accept_launch, |
| |
| + /* Launch user's resolver with the invoke_diff3_cmd in the config |
| + file and resolve conflict with edited file. */ |
| + svn_cl__accept_invoke_diff3_config |
| + |
| } svn_cl__accept_t; |
| |
| /* --accept action user input words */ |
| @@ -97,6 +101,7 @@ typedef enum svn_cl__accept_t |
| #define SVN_CL__ACCEPT_THEIRS_FULL "theirs-full" |
| #define SVN_CL__ACCEPT_EDIT "edit" |
| #define SVN_CL__ACCEPT_LAUNCH "launch" |
| +#define SVN_CL__ACCEPT_INVOKE_DIFF3_CONFIG "invoke-diff3-config" |
| |
| /* Return the svn_cl__accept_t value corresponding to WORD, using exact |
| * case-sensitive string comparison. Return svn_cl__accept_invalid if WORD |
| @@ -204,6 +209,9 @@ typedef struct svn_cl__opt_state_t |
| svn_boolean_t revprop; /* operate on a revision property */ |
| const char *merge_cmd; /* the external merge command to use |
| (not converted to UTF-8) */ |
| + const char *invoke_diff3_cmd; /* the format string for the external |
| + merge command to use (not |
| + converted to UTF-8) */ |
| const char *editor_cmd; /* the external editor command to use |
| (not converted to UTF-8) */ |
| svn_boolean_t record_only; /* whether to record mergeinfo */ |
| @@ -531,6 +539,21 @@ svn_cl__merge_file_externally(const char *base_pat |
| svn_boolean_t *remains_in_conflict, |
| apr_pool_t *pool); |
| |
| +/* As svn_cl__merge_file_externally, but for the invoke_diff3_cmd |
| + selected merge tool */ |
| +svn_error_t * |
| +svn_cl__invoke_diff3_cmd_externally(const char *base_label, |
| + const char *their_label, |
| + const char *my_label, |
| + const char *base_file, |
| + const char *their_file, |
| + const char *my_file, |
| + apr_hash_t *config, |
| + svn_boolean_t *remains_in_conflict, |
| + const char *opt_code, |
| + apr_pool_t *pool); |
| + |
| + |
| /* Like svn_cl__merge_file_externally, but using a built-in merge tool |
| * with help from an external editor specified by EDITOR_CMD. */ |
| svn_error_t * |
| Index: subversion/svn/conflict-callbacks.c |
| =================================================================== |
| --- subversion/svn/conflict-callbacks.c (revision 1542103) |
| +++ subversion/svn/conflict-callbacks.c (working copy) |
| @@ -118,6 +118,11 @@ svn_cl__accept_from_word(const char *word) |
| if (strcmp(word, SVN_CL__ACCEPT_LAUNCH) == 0 |
| || strcmp(word, "l") == 0 || strcmp(word, ":-l") == 0) |
| return svn_cl__accept_launch; |
| + if (strcmp(word, SVN_CL__ACCEPT_INVOKE_DIFF3_CONFIG) == 0 |
| + /* FIX ME: Which smiley to select? */ |
| + || strcmp(word, "3f") == 0 || strcmp(word, ":-?") == 0) |
| + return svn_cl__accept_launch; |
| + |
| /* word is an invalid action. */ |
| return svn_cl__accept_invalid; |
| } |
| @@ -409,7 +414,38 @@ launch_resolver(svn_boolean_t *performed_edit, |
| return SVN_NO_ERROR; |
| } |
| |
| +/* Run an external merge tool, passing it the 'base', 'their', 'my' and |
| + * 'merged' files in DESC. The tool to use is determined by B->config and |
| + * environment variables; see svn_cl__merge_file_externally() for details. |
| + * |
| + * If the tool runs, set *PERFORMED_EDIT to true; if a tool is not |
| + * configured or cannot run, do not touch *PERFORMED_EDIT, report the error |
| + * on stderr, and return SVN_NO_ERROR; if any other error is encountered, |
| + * return that error. */ |
| +static svn_error_t * |
| +invoke_diff3_resolver(svn_boolean_t *performed_edit, |
| + const svn_wc_conflict_description2_t *desc, |
| + svn_cl__interactive_conflict_baton_t *b, |
| + const char *opt_code, |
| + apr_pool_t *pool) |
| +{ |
| |
| + SVN_ERR(svn_cl__invoke_diff3_cmd_externally("BASE", |
| + "OLD", |
| + "NEW", |
| + desc->base_abspath, |
| + desc->their_abspath, |
| + desc->my_abspath, |
| + b->config, NULL, |
| + opt_code, |
| + pool)); |
| + if (performed_edit) |
| + *performed_edit = TRUE; |
| + |
| + return SVN_NO_ERROR; |
| +} |
| + |
| + |
| /* Maximum line length for the prompt string. */ |
| #define MAX_PROMPT_WIDTH 70 |
| |
| @@ -458,6 +494,15 @@ static const resolver_option_t text_conflict_optio |
| -1 }, |
| { "l", N_("launch tool"), N_("launch external tool to resolve " |
| "conflict [launch]"), -1 }, |
| + { "3f", N_("invoke-diff3-cmd given in config file"), |
| + N_("use invoke-diff3 command defined in " |
| + "the config file to resolve conflict " |
| + "[invoke-diff3-config]"), -1 }, |
| + |
| + { "i", N_("interactive invoke-diff3-cmd selection"), |
| + N_("interactively select tool now to " |
| + "resolve conflict"), -1 }, |
| + |
| { "p", N_("postpone"), N_("mark the conflict to be resolved later" |
| " [postpone]"), |
| svn_wc_conflict_choose_postpone }, |
| @@ -734,6 +779,8 @@ handle_text_conflict(svn_wc_conflict_result_t *res |
| *next_option++ = "df"; |
| *next_option++ = "e"; |
| *next_option++ = "m"; |
| + *next_option++ = "3f"; |
| + *next_option++ = "i"; |
| |
| if (knows_something) |
| *next_option++ = "r"; |
| @@ -798,8 +845,8 @@ handle_text_conflict(svn_wc_conflict_result_t *res |
| if (! diff_allowed) |
| { |
| SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, |
| - _("Invalid option; there's no " |
| - "merged version to diff.\n\n"))); |
| + _("Invalid option; there's no " |
| + "merged version to diff.\n\n"))); |
| continue; |
| } |
| |
| @@ -887,8 +934,8 @@ handle_text_conflict(svn_wc_conflict_result_t *res |
| { |
| SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", |
| err->message ? err->message : |
| - _("Error running merge tool, " |
| - "try '(m) merge' instead."))); |
| + _("Error running merge tool, " |
| + "try '(m) merge' instead."))); |
| svn_error_clear(err); |
| } |
| else if (err) |
| @@ -901,6 +948,77 @@ handle_text_conflict(svn_wc_conflict_result_t *res |
| SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, |
| _("Invalid option.\n\n"))); |
| } |
| + else if (strcmp(opt->code, "i") == 0) |
| + { /* interactively get the invoke-diff3-cmd */ |
| + |
| + if (desc->base_abspath && desc->their_abspath && |
| + desc->my_abspath && desc->merged_file) |
| + { |
| + const char *answer; |
| + svn_error_t *err; |
| + |
| + SVN_ERR(svn_cmdline_prompt_user2(&answer, |
| + "Enter the invoke-diff3-cmd: ", |
| + b->pb, iterpool)); |
| + err = invoke_diff3_resolver(&performed_edit, desc, b, answer, iterpool); |
| + |
| + if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) |
| + { |
| + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", |
| + err->message ? err->message : |
| + _("Error executing the interactive, " |
| + "invoke-diff3-cmd.\n"))); |
| + svn_error_clear(err); |
| + } |
| + else if (err) |
| + return svn_error_trace(err); |
| + |
| + if (performed_edit) |
| + knows_something = TRUE; |
| + } |
| + else |
| + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, |
| + _("Invalid option.\n\n"))); |
| + } |
| + else if (strcmp(opt->code, "3f") == 0) |
| + { |
| + if (desc->base_abspath && desc->their_abspath && |
| + desc->my_abspath && desc->merged_file) |
| + { |
| + |
| + svn_error_t *err; |
| + /* ### This check should be earlier as it's nasty to offer an option |
| + * and then when the user chooses it say 'Invalid option'. */ |
| + /* ### 'merged_file' shouldn't be necessary *before* we launch the |
| + * resolver: it should be the *result* of doing so. */ |
| + |
| + err = invoke_diff3_resolver(&performed_edit, desc, b, opt->code, iterpool); |
| + if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_DIFF3_TOOL) |
| + { |
| + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", |
| + err->message ? err->message : |
| + _("No invoke-diff3 tool found, " |
| + "try '(m) merge' instead.\n"))); |
| + svn_error_clear(err); |
| + } |
| + else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) |
| + { |
| + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", |
| + err->message ? err->message : |
| + _("Error running invoke-diff3 tool, " |
| + "try '(m) merge' instead."))); |
| + svn_error_clear(err); |
| + } |
| + else if (err) |
| + return svn_error_trace(err); |
| + |
| + if (performed_edit) |
| + knows_something = TRUE; |
| + } |
| + else |
| + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, |
| + _("Invalid option.\n\n"))); |
| + } |
| else if (opt->choice != -1) |
| { |
| if ((opt->choice == svn_wc_conflict_choose_mine_conflict |
| @@ -921,9 +1039,9 @@ handle_text_conflict(svn_wc_conflict_result_t *res |
| && ! knows_something) |
| { |
| SVN_ERR(svn_cmdline_fprintf( |
| - stderr, iterpool, |
| - _("Invalid option; use diff/edit/merge/launch " |
| - "before choosing 'mark resolved'.\n\n"))); |
| + stderr, iterpool, |
| + _("Invalid option; use diff/edit/merge/launch " |
| + "before choosing 'mark resolved'.\n\n"))); |
| continue; |
| } |
| |
| @@ -1294,6 +1412,57 @@ conflict_func_interactive(svn_wc_conflict_result_t |
| } |
| /* else, fall through to prompting. */ |
| break; |
| + case svn_cl__accept_invoke_diff3_config: |
| + if (desc->base_abspath && desc->their_abspath |
| + && desc->my_abspath && desc->merged_file) |
| + { |
| + svn_boolean_t remains_in_conflict; |
| + |
| + if (b->external_failed) |
| + { |
| + (*result)->choice = svn_wc_conflict_choose_postpone; |
| + return SVN_NO_ERROR; |
| + } |
| + err = svn_cl__invoke_diff3_cmd_externally("BASE", |
| + "NEW", |
| + "OLD", |
| + desc->base_abspath, |
| + desc->their_abspath, |
| + desc->my_abspath, |
| + b->config, |
| + &remains_in_conflict, |
| + "3f", |
| + scratch_pool); |
| + if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL) |
| + { |
| + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", |
| + err->message ? err->message : |
| + _("No invoke-diff3-cmd tool found;" |
| + " leaving all conflicts."))); |
| + b->external_failed = TRUE; |
| + return svn_error_trace(err); |
| + } |
| + else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) |
| + { |
| + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", |
| + err->message ? err->message : |
| + _("Error running invoke-diff3-cmd tool;" |
| + " leaving all conflicts."))); |
| + b->external_failed = TRUE; |
| + return svn_error_trace(err); |
| + } |
| + else if (err) |
| + return svn_error_trace(err); |
| + |
| + if (remains_in_conflict) |
| + (*result)->choice = svn_wc_conflict_choose_postpone; |
| + else |
| + (*result)->choice = svn_wc_conflict_choose_merged; |
| + return SVN_NO_ERROR; |
| + } |
| + /* else, fall through to prompting. */ |
| + break; |
| + |
| } |
| |
| /* Print a summary of conflicts before starting interactive resolution */ |
| Index: subversion/svn/svn.c |
| =================================================================== |
| --- subversion/svn/svn.c (revision 1542103) |
| +++ subversion/svn/svn.c (working copy) |
| @@ -95,6 +95,7 @@ typedef enum svn_cl__longopt_t { |
| opt_ignore_externals, |
| opt_incremental, |
| opt_merge_cmd, |
| + opt_invoke_diff3_cmd, |
| opt_native_eol, |
| opt_new_cmd, |
| opt_no_auth_cache, |
| @@ -250,6 +251,24 @@ const apr_getopt_option_t svn_cl__options[] = |
| {"ignore-externals", opt_ignore_externals, 0, |
| N_("ignore externals definitions")}, |
| {"diff3-cmd", opt_merge_cmd, 1, N_("use ARG as merge command")}, |
| + {"invoke-diff3-cmd", opt_invoke_diff3_cmd, 1, |
| + N_("use ARG as format string for external merge program\n" |
| + " " |
| + "invocation. Substitutions: \n" |
| + " " |
| + " %svn_mine 'mine' file\n" |
| + " " |
| + " %svn_yours 'yours' file\n" |
| + " " |
| + " %svn_base 'base' file\n" |
| + " " |
| + " %svn_label_mine label of the 'mine file\n" |
| + " " |
| + " %svn_label_yours label of the 'yours' file\n" |
| + " " |
| + " %svn_label_base label of the 'mine file\n" |
| + " " |
| + "See 'help diff' for example usage.")}, |
| {"editor-cmd", opt_editor_cmd, 1, N_("use ARG as external editor")}, |
| {"record-only", opt_record_only, 0, |
| N_("merge only mergeinfo differences")}, |
| @@ -546,8 +565,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table |
| " for writing by another Subversion client.\n" |
| " Note that the 'svn status' command shows unversioned items as '?',\n" |
| " and ignored items as 'I' if the --no-ignore option is given to it.\n"), |
| - {opt_merge_cmd, opt_remove_unversioned, opt_remove_ignored, |
| - opt_include_externals, 'q'} }, |
| + {opt_merge_cmd, opt_invoke_diff3_cmd, opt_remove_unversioned, |
| + opt_remove_ignored, opt_include_externals, 'q'} }, |
| |
| { "commit", svn_cl__commit, {"ci"}, |
| N_("Send changes from your working copy to the repository.\n" |
| @@ -1140,8 +1159,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table |
| " target. Also, merge-tracking is not supported for merges from foreign\n" |
| " repositories.\n"), |
| {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd, |
| - opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate, |
| - opt_allow_mixed_revisions, 'v'} }, |
| + opt_invoke_diff3_cmd, opt_record_only, 'x', opt_ignore_ancestry, |
| + opt_accept, opt_reintegrate, opt_allow_mixed_revisions, 'v'} }, |
| |
| { "mergeinfo", svn_cl__mergeinfo, {0}, N_ |
| ("Display merge-related information.\n" |
| @@ -1604,8 +1623,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table |
| " svn switch --relocate http:// svn://\n" |
| " svn switch --relocate http://www.example.com/repo/project \\\n" |
| " svn://svn.example.com/repo/project\n"), |
| - { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate, |
| - opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept}, |
| + { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, |
| + opt_invoke_diff3_cmd, opt_relocate, opt_ignore_externals, |
| + opt_ignore_ancestry, opt_force, opt_accept}, |
| {{opt_ignore_ancestry, |
| N_("allow switching to a node with no common ancestor")}} |
| }, |
| @@ -1663,9 +1683,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table |
| "\n" |
| " Use the --set-depth option to set a new working copy depth on the\n" |
| " targets of this operation.\n"), |
| - {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force, |
| - opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept, |
| - opt_parents} }, |
| + {'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, |
| + opt_invoke_diff3_cmd, opt_force, opt_ignore_externals, opt_changelist, |
| + opt_editor_cmd, opt_accept, opt_parents} }, |
| |
| { "upgrade", svn_cl__upgrade, {0}, N_ |
| ("Upgrade the metadata storage format for a working copy.\n" |
| @@ -2174,6 +2194,9 @@ sub_main(int argc, const char *argv[], apr_pool_t |
| case opt_merge_cmd: |
| opt_state.merge_cmd = apr_pstrdup(pool, opt_arg); |
| break; |
| + case opt_invoke_diff3_cmd: |
| + opt_state.invoke_diff3_cmd = apr_pstrdup(pool, opt_arg); |
| + break; |
| case opt_record_only: |
| opt_state.record_only = TRUE; |
| break; |
| @@ -2611,6 +2634,16 @@ sub_main(int argc, const char *argv[], apr_pool_t |
| return EXIT_ERROR(err); |
| } |
| |
| + /* Check for mutually exclusive args --diff3-cmd and |
| + --invoke-diff3-cmd */ |
| + if (opt_state.merge_cmd && opt_state.invoke_diff3_cmd) |
| + { |
| + err = svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, |
| + _("--diff3-cmd and --invoke-diff3-cmd are " |
| + "mutually exclusive")); |
| + return EXIT_ERROR(err); |
| + } |
| + |
| /* Ensure that 'revision_ranges' has at least one item, and make |
| 'start_revision' and 'end_revision' match that item. */ |
| if (opt_state.revision_ranges->nelts == 0) |
| @@ -2830,8 +2863,15 @@ sub_main(int argc, const char *argv[], apr_pool_t |
| svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, |
| SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, opt_state.diff.invoke_diff_cmd); |
| if (opt_state.merge_cmd) |
| - svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, |
| - SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd); |
| + { |
| + svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd); |
| + } |
| + if (opt_state.invoke_diff3_cmd) |
| + { |
| + svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, opt_state.invoke_diff3_cmd); |
| + } |
| if (opt_state.diff.internal_diff) |
| svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, |
| SVN_CONFIG_OPTION_DIFF_CMD, NULL); |
| @@ -2955,6 +2995,12 @@ sub_main(int argc, const char *argv[], apr_pool_t |
| _("--accept=%s incompatible with" |
| " --non-interactive"), |
| SVN_CL__ACCEPT_LAUNCH)); |
| + if (opt_state.accept_which == svn_cl__accept_invoke_diff3_config) |
| + return EXIT_ERROR( |
| + svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, |
| + _("--accept=%s incompatible with" |
| + " --non-interactive"), |
| + SVN_CL__ACCEPT_LAUNCH)); |
| |
| /* The default action when we're non-interactive is to postpone |
| * conflict resolution. */ |
| Index: subversion/svn/util.c |
| =================================================================== |
| --- subversion/svn/util.c (revision 1542103) |
| +++ subversion/svn/util.c (working copy) |
| @@ -66,6 +66,7 @@ |
| #include "private/svn_client_private.h" |
| #include "private/svn_cmdline_private.h" |
| #include "private/svn_string_private.h" |
| +#include "private/svn_io_private.h" |
| |
| |
| |
| @@ -173,7 +174,105 @@ svn_cl__merge_file_externally(const char *base_pat |
| return SVN_NO_ERROR; |
| } |
| |
| +svn_error_t * |
| +svn_cl__invoke_diff3_cmd_externally(const char *base_label, |
| + const char *their_label, |
| + const char *my_label, |
| + const char *base_file, |
| + const char *their_file, |
| + const char *my_file, |
| + apr_hash_t *config, |
| + svn_boolean_t *remains_in_conflict, |
| + const char *opt_code, |
| + apr_pool_t *pool) |
| +{ |
| + char *invoke_diff3_cmd; |
| + const char ** cmd; |
| + apr_pool_t *scratch_pool = svn_pool_create(pool); |
| + char *cwd; |
| + int exitcode; |
| + apr_status_t status = apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, pool); |
| |
| + if (status != 0) |
| + return svn_error_wrap_apr(status, NULL); |
| + |
| + if (0 == strcmp(opt_code,"3f")) /* command in config file */ |
| + { |
| + |
| + if (apr_env_get(&invoke_diff3_cmd, "SVN_INVOKE_DIFF3_CMD", pool) != APR_SUCCESS) |
| + { |
| + struct svn_config_t *cfg; |
| + invoke_diff3_cmd = NULL; |
| + cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; |
| + /* apr_env_get wants char **, this wants const char ** */ |
| + svn_config_get(cfg, (const char **)&invoke_diff3_cmd, |
| + SVN_CONFIG_SECTION_HELPERS, |
| + SVN_CONFIG_OPTION_INVOKE_DIFF3_CMD, NULL); |
| + } |
| + |
| + if (invoke_diff3_cmd) |
| + { |
| + const char *c; |
| + for (c = invoke_diff3_cmd; *c; c++) |
| + if (!svn_ctype_isspace(*c)) |
| + break; |
| + |
| + if (! *c) |
| + return svn_error_create |
| + (SVN_ERR_CL_NO_EXTERNAL_DIFF3_TOOL, NULL, |
| + _("The SVN_INVOKE_DIFF3_TOOL environment variable is empty or " |
| + "consists solely of whitespace. Expected a shell command.\n")); |
| + } |
| + else |
| + return svn_error_create |
| + (SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL, NULL, |
| + _("The environment variable SVN_INVOKE_DIFF3_TOOL and the invoke-diff3-cmd run-time " |
| + "configuration option were not set.\n")); |
| + } |
| + else |
| + { |
| + invoke_diff3_cmd = apr_pstrdup(pool, opt_code); |
| + } |
| + |
| + |
| + if (0 == strlen(invoke_diff3_cmd)) |
| + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL); |
| + |
| + cmd = svn_io__create_custom_diff_cmd(my_label, their_label, base_label, |
| + my_file, their_file, base_file, |
| + invoke_diff3_cmd, scratch_pool); |
| + |
| + SVN_ERR(svn_io_run_cmd(svn_dirent_internal_style(cwd, pool), |
| + cmd[0], cmd, &exitcode, NULL, TRUE, |
| + NULL, NULL, NULL, scratch_pool)); |
| + |
| + /* According to the diff3 docs, a '0' means the merge was clean, and |
| + '1' means conflict markers were found. Anything else is real |
| + error. */ |
| + if ((exitcode != 0) && (exitcode != 1)) |
| + { |
| + |
| + int i; |
| + const char *failed_command = ""; |
| + |
| + for (i = 0; cmd[i]; ++i) |
| + failed_command = apr_pstrcat(pool, failed_command, |
| + cmd[i], " ", (char*) NULL); |
| + svn_pool_destroy(scratch_pool); |
| + return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, |
| + _("'%s' was expanded to '%s' and returned %d"), |
| + invoke_diff3_cmd, |
| + failed_command, |
| + exitcode); |
| + } |
| + else if (remains_in_conflict) |
| + *remains_in_conflict = exitcode == 1; |
| + svn_pool_destroy(scratch_pool); |
| + |
| + return SVN_NO_ERROR; |
| +} |
| + |
| + |
| /* A svn_client_ctx_t's log_msg_baton3, for use with |
| svn_cl__make_log_msg_baton(). */ |
| struct log_msg_baton |
| Index: subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout |
| =================================================================== |
| --- subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (revision 1542103) |
| +++ subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (working copy) |
| @@ -201,6 +201,15 @@ Valid options: |
| 'empty', 'files', 'immediates', or 'infinity') |
| -q [--quiet] : print nothing, or only summary information |
| --diff3-cmd ARG : use ARG as merge command |
| + --invoke-diff3-cmd ARG : use ARG as format string for external merge program |
| + invocation. Substitutions: |
| + %svn_mine 'mine' file |
| + %svn_yours 'yours' file |
| + %svn_base 'base' file |
| + %svn_label_mine label of the 'mine file |
| + %svn_label_yours label of the 'yours' file |
| + %svn_label_base label of the 'mine file |
| + See 'help diff' for example usage. |
| --relocate : relocate via URL-rewriting |
| --ignore-externals : ignore externals definitions |
| --ignore-ancestry : allow switching to a node with no common ancestor |