Index: build.conf =================================================================== --- build.conf (revision 1053833) +++ build.conf (working copy) @@ -336,7 +336,7 @@ description = Subversion Working Copy Library type = lib path = subversion/libsvn_wc -libs = libsvn_delta libsvn_diff libsvn_subr aprutil apriconv apr +libs = libsvn_ra libsvn_delta libsvn_diff libsvn_subr aprutil apriconv apr install = lib msvc-export = svn_wc.h private\svn_wc_private.h Index: subversion/libsvn_ra/deprecated.c =================================================================== --- subversion/libsvn_ra/deprecated.c (revision 1053833) +++ subversion/libsvn_ra/deprecated.c (working copy) @@ -248,6 +248,30 @@ keep_locks, pool); } +svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session, + const svn_ra_reporter3_t **reporter, + void **report_baton, + svn_revnum_t revision, + const char *diff_target, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t text_deltas, + const char *versus_url, + const svn_delta_editor_t *diff_editor, + void *diff_baton, + apr_pool_t *pool) +{ + SVN_ERR_ASSERT(svn_path_is_empty(diff_target) + || svn_path_is_single_path_component(diff_target)); + return svn_ra_do_diff4(session, + reporter, report_baton, + revision, diff_target, + depth, ignore_ancestry, + text_deltas, FALSE/* send copyfrom args */, + versus_url, diff_editor, + diff_baton, pool); +} + svn_error_t *svn_ra_do_diff2(svn_ra_session_t *session, const svn_ra_reporter2_t **reporter, void **report_baton, @@ -266,12 +290,13 @@ || svn_path_is_single_path_component(diff_target)); *reporter = &reporter_3in2_wrapper; *report_baton = b; - return session->vtable->do_diff(session, - &(b->reporter3), &(b->reporter3_baton), - revision, diff_target, - SVN_DEPTH_INFINITY_OR_FILES(recurse), - ignore_ancestry, text_deltas, versus_url, - diff_editor, diff_baton, pool); + return svn_ra_do_diff3(session, + &(b->reporter3), &(b->reporter3_baton), + revision, diff_target, + SVN_DEPTH_INFINITY_OR_FILES(recurse), + ignore_ancestry, text_deltas, + versus_url, diff_editor, + diff_baton, pool); } svn_error_t *svn_ra_do_diff(svn_ra_session_t *session, Index: subversion/libsvn_ra/wrapper_template.h =================================================================== --- subversion/libsvn_ra/wrapper_template.h (revision 1053833) +++ subversion/libsvn_ra/wrapper_template.h (working copy) @@ -361,7 +361,7 @@ svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); SVN_ERR(VTBL.do_diff(session_baton, &reporter3, &baton3, revision, - diff_target, depth, ignore_ancestry, TRUE, + diff_target, depth, ignore_ancestry, TRUE, FALSE, versus_url, diff_editor, diff_baton, pool)); compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool); Index: subversion/libsvn_ra/ra_loader.c =================================================================== --- subversion/libsvn_ra/ra_loader.c (revision 1053833) +++ subversion/libsvn_ra/ra_loader.c (working copy) @@ -845,7 +845,7 @@ status_editor, status_baton, pool); } -svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session, +svn_error_t *svn_ra_do_diff4(svn_ra_session_t *session, const svn_ra_reporter3_t **reporter, void **report_baton, svn_revnum_t revision, @@ -853,6 +853,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *diff_editor, void *diff_baton, @@ -864,7 +865,8 @@ reporter, report_baton, revision, diff_target, depth, ignore_ancestry, - text_deltas, versus_url, diff_editor, + text_deltas, send_copyfrom_args, + versus_url, diff_editor, diff_baton, pool); } Index: subversion/libsvn_ra/ra_loader.h =================================================================== --- subversion/libsvn_ra/ra_loader.h (revision 1053833) +++ subversion/libsvn_ra/ra_loader.h (working copy) @@ -159,6 +159,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *diff_editor, void *diff_baton, Index: subversion/libsvn_ra_local/ra_plugin.c =================================================================== --- subversion/libsvn_ra_local/ra_plugin.c (revision 1053833) +++ subversion/libsvn_ra_local/ra_plugin.c (working copy) @@ -822,6 +822,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *switch_url, const svn_delta_editor_t *update_editor, void *update_baton, @@ -835,7 +836,7 @@ switch_url, text_deltas, depth, - FALSE, + send_copyfrom_args, ignore_ancestry, update_editor, update_baton, Index: subversion/tests/cmdline/diff_tests.py =================================================================== --- subversion/tests/cmdline/diff_tests.py (revision 1053833) +++ subversion/tests/cmdline/diff_tests.py (working copy) @@ -3121,7 +3121,7 @@ ] svntest.actions.run_and_verify_svn(None, diff_repos_wc, [], - 'diff', '-r' , '2') + 'diff', '-r' , '2', '--sca') #---------------------------------------------------------------------- @@ -3455,7 +3455,7 @@ expected = svntest.verify.UnorderedOutput(expected_output) svntest.actions.run_and_verify_svn(None, expected, [], 'diff', - '--git', + '--git', '--sca', '--old', repo_url + '@1', '--new', wc_dir) @@ -3497,7 +3497,7 @@ expected = svntest.verify.UnorderedOutput(expected_output) svntest.actions.run_and_verify_svn(None, expected, [], 'diff', - '--git', + '--git', '--sca', '--old', repo_url + '@1', '--new', repo_url + '@2') @@ -3750,6 +3750,92 @@ svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff', '-c2', '--git') os.chdir(was_cwd) + +#---------------------------------------------------------------------- +# +# In this test we perform 4 checks for repos-repos diff and +# 2 checks for repos-wc diff. +# Initially the diff is done against the target url of the file path. +# The next two diffs are done against the repo root url. +# The fourth check is done to see that the default behaviour +# is not broken. +# The repos-wc checks are done in similar ways. + +def diff_against_copysource_by_default(sbox): + "do diff against the copyfrom source by default" + sbox.build() + wc_dir = sbox.wc_dir + + file_path1 = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') + +# Remove the 'alpha' file and commit it in the repository + svntest.main.run_svn(None, 'rm', file_path1) + svntest.main.run_svn(None, 'ci', '-m', 'file removed', '--quiet', file_path1) + +# copy 'alpha' from revision 1 and modify contents + copyfrom_path = sbox.repo_url+"/A/B/E/alpha@1" + full_path = sbox.repo_url+"/A/B/E/alpha" + svntest.main.run_svn(None, 'cp', copyfrom_path, file_path1) + svntest.main.file_append(file_path1, + "new line added\n") + svntest.main.run_svn(None, 'ci', '-m', 'file copied content modified', + '--quiet', file_path1) + + svntest.main.run_svn(None, 'up') + + expected_output1 = [ + "Index: alpha\n", + "===================================================================\n", + "--- alpha (revision 1)\n", + "+++ alpha (revision 3)\n", + "@@ -1 +1,2 @@\n", + " This is the file 'alpha'.\n", + "+new line added\n" ] + + expected_output2 = [ + "Index: A/B/E/alpha\n", + "===================================================================\n", + "--- A/B/E/alpha (revision 1)\n", + "+++ A/B/E/alpha (revision 3)\n", + "@@ -1 +1,2 @@\n", + " This is the file 'alpha'.\n", + "+new line added\n" ] + + expected_output3 = [ + "Index: A/B/E/alpha\n", + "===================================================================\n", + "--- A/B/E/alpha (revision 0)\n", + "+++ A/B/E/alpha (revision 3)\n", + "@@ -0,0 +1,2 @@\n", + "+This is the file 'alpha'.\n", + "+new line added\n" ] + + expected_output4 = [ + "Index: A/B/E/alpha\n", + "===================================================================\n", + "--- A/B/E/alpha (revision 3)\n", + "+++ A/B/E/alpha (revision 1)\n", + "@@ -1,2 +1 @@\n", + " This is the file 'alpha'.\n", + "-new line added\n" ] + + svntest.actions.run_and_verify_svn(None, expected_output1, [], + 'diff', '-r', '1:3', full_path) + svntest.actions.run_and_verify_svn(None, expected_output2, [], + 'diff', '-c', '3', sbox.repo_url) + svntest.actions.run_and_verify_svn(None, expected_output2, [], + 'diff', '-r', '1:3', sbox.repo_url) + svntest.actions.run_and_verify_svn(None, expected_output3, [], + 'diff', '-c', '3', '--sca', sbox.repo_url) + os.chdir(sbox.wc_dir) + svntest.main.run_svn(None, 'up', '-r2') + svntest.actions.run_and_verify_svn(None, expected_output2, [], + 'diff', '-r', 'BASE:3') + svntest.actions.run_and_verify_svn(None, expected_output4, [], + 'diff', '-r', '3:BASE') + svntest.actions.run_and_verify_svn(None, expected_output3, [], + 'diff', '-r', 'BASE:3', '--sca') + ######################################################################## #Run the tests @@ -3814,6 +3900,7 @@ diff_git_empty_files, diff_git_with_props, diff_git_with_props_on_dir, + diff_against_copysource_by_default, ] if __name__ == '__main__': Index: subversion/libsvn_ra_svn/client.c =================================================================== --- subversion/libsvn_ra_svn/client.c (revision 1053833) +++ subversion/libsvn_ra_svn/client.c (working copy) @@ -1316,6 +1316,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *diff_editor, void *diff_baton, apr_pool_t *pool) @@ -1325,10 +1326,11 @@ svn_boolean_t recurse = DEPTH_TO_RECURSE(depth); /* Tell the server we want to start a diff. */ - SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "diff", "(?r)cbbcbw", rev, + SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "diff", "(?r)cbbcbwb", rev, target, recurse, ignore_ancestry, versus_url, text_deltas, - svn_depth_to_word(depth))); + svn_depth_to_word(depth), + send_copyfrom_args)); SVN_ERR(handle_auth_request(sess_baton, pool)); /* Fetch a reporter for the caller to drive. The reporter will drive Index: subversion/libsvn_ra_svn/protocol =================================================================== --- subversion/libsvn_ra_svn/protocol (revision 1053833) +++ subversion/libsvn_ra_svn/protocol (working copy) @@ -358,7 +358,7 @@ diff params: ( [ rev:number ] target:string recurse:bool ignore-ancestry:bool - url:string ? text-deltas:bool ? depth:word ) + url:string ? text-deltas:bool ? depth:word ? send_copyfrom_args:bool) Client switches to report command set. Upon finish-report, server sends auth-request. After auth exchange completes, server switches to editor command set. Index: subversion/include/svn_wc.h =================================================================== --- subversion/include/svn_wc.h (revision 1053833) +++ subversion/include/svn_wc.h (working copy) @@ -5783,6 +5783,8 @@ * repository. The editor is allocated in @a result_pool; temporary * calculations are performed in @a scratch_pool. * + * @a ra_session/@a is the session to the repository root url. + * * @a anchor_path/@a target represent the base of the hierarchy to be compared. * * @a callbacks/@a callback_baton is the callback table to use when two @@ -5829,6 +5831,7 @@ svn_error_t * svn_wc_get_diff_editor6(const svn_delta_editor_t **editor, void **edit_baton, + svn_ra_session_t *ra_session, svn_wc_context_t *wc_ctx, const char *anchor_path, const char *target, Index: subversion/include/svn_ra.h =================================================================== --- subversion/include/svn_ra.h (revision 1053833) +++ subversion/include/svn_ra.h (working copy) @@ -1336,6 +1336,8 @@ * handler returned by apply_textdelta will be called once with a NULL * @c svn_txdelta_window_t pointer. * + * Pass TRUE to @a send_copyfrom_args to send the copyfrom info. + * * Use @a pool for memory allocation. * * @note The reporter provided by this function does NOT supply copy- @@ -1345,9 +1347,33 @@ * needed, and sending too much data back, a pre-1.5 'recurse' * directive may be sent to the server, based on @a depth. * - * @since New in 1.5. + * @since New in 1.7. */ svn_error_t * +svn_ra_do_diff4(svn_ra_session_t *session, + const svn_ra_reporter3_t **reporter, + void **report_baton, + svn_revnum_t revision, + const char *diff_target, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, + const char *versus_url, + const svn_delta_editor_t *diff_editor, + void *diff_baton, + apr_pool_t *pool); + +/** + * Similar to svn_ra_do_diff4(), but not taking the @a send_copyfrom_args + * option, hence passing FALSE. + * + * New code should use svn_ra_do_diff4(). + * + * @deprecated Provided for compatibility with the 1.5 API. + */ +SVN_DEPRECATED +svn_error_t * svn_ra_do_diff3(svn_ra_session_t *session, const svn_ra_reporter3_t **reporter, void **report_baton, @@ -1361,6 +1387,7 @@ void *diff_baton, apr_pool_t *pool); + /** * Similar to svn_ra_do_diff3(), but taking @c svn_ra_reporter2_t * instead of @c svn_ra_reporter3_t, and therefore only able to report Index: subversion/libsvn_wc/deprecated.c =================================================================== --- subversion/libsvn_wc/deprecated.c (revision 1053833) +++ subversion/libsvn_wc/deprecated.c (working copy) @@ -1780,23 +1780,24 @@ b->db = db; SVN_ERR(svn_wc_get_diff_editor6(editor, - edit_baton, - wc_ctx, - svn_wc_adm_access_path(anchor), - target, - &diff_callbacks3_wrapper, - b, - depth, - ignore_ancestry, - FALSE, - FALSE, - use_text_base, - reverse_order, - changelists, - cancel_func, - cancel_baton, - pool, - pool)); + edit_baton, + NULL, + wc_ctx, + svn_wc_adm_access_path(anchor), + target, + &diff_callbacks3_wrapper, + b, + depth, + ignore_ancestry, + FALSE, + FALSE, + use_text_base, + reverse_order, + changelists, + cancel_func, + cancel_baton, + pool, + pool)); /* Can't destroy wc_ctx. It is used by the diff editor */ Index: subversion/libsvn_wc/diff.c =================================================================== --- subversion/libsvn_wc/diff.c (revision 1053833) +++ subversion/libsvn_wc/diff.c (working copy) @@ -52,6 +52,7 @@ #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_hash.h" +#include "svn_ra.h" #include "private/svn_wc_private.h" @@ -197,6 +198,9 @@ /* Overall crawler editor baton. */ struct edit_baton { + + svn_ra_session_t *ra_session; + /* A wc db. */ svn_wc__db_t *db; @@ -314,6 +318,16 @@ drop the result into a file at TEMP_FILE_PATH. */ const char *temp_file_path; + /* The path and APR file handle to the temporary file that contains the + BASE version. */ + apr_file_t *copyfrom_temp_file; + const char *copyfrom_temp_path; + + apr_hash_t *pristine_props; + + /* The revision from where the source file is to be copied */ + svn_revnum_t copyfrom_revision; + /* The list of incoming BASE->repos propchanges. */ apr_array_header_t *propchanges; @@ -345,6 +359,7 @@ */ static svn_error_t * make_edit_baton(struct edit_baton **edit_baton, + svn_ra_session_t *ra_session, svn_wc__db_t *db, const char *anchor_path, const char *target, @@ -368,6 +383,7 @@ SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); eb = apr_pcalloc(pool, sizeof(*eb)); + eb->ra_session = ra_session; eb->db = db; eb->anchor_path = anchor_path; SVN_ERR(svn_dirent_get_absolute(&eb->anchor_abspath, eb->anchor_path, pool)); @@ -478,6 +494,31 @@ return fb; } +/* Get revision REVISION of the file described by B from the repository. + * Set B->copyfrom_temp_path to the path of a new temporary file containing + * the file's text. Set B->pristine_props to a new hash containing the + * file's properties. Install a pool cleanup handler on B->pool to delete + * the file. + */ +static svn_error_t * +get_file_from_ra(struct file_baton *b, const char *path, + svn_revnum_t revision) +{ + svn_stream_t *fstream; + + SVN_ERR(svn_stream_open_unique(&fstream, &(b->copyfrom_temp_path), NULL, + svn_io_file_del_on_pool_cleanup, b->pool, + b->pool)); + + SVN_ERR(svn_ra_get_file(b->eb->ra_session, + path, + revision, + fstream, NULL, + &(b->pristine_props), + b->pool)); + return svn_stream_close(fstream); +} + /* Get the empty file associated with the edit baton. This is cached so * that it can be reused, all empty files are the same. */ @@ -1436,17 +1477,28 @@ struct file_baton *fb; const char *full_path; - /* ### TODO: support copyfrom? */ - full_path = svn_dirent_join(pb->eb->anchor_path, path, file_pool); - fb = make_file_baton(full_path, TRUE, pb, file_pool); - *file_baton = fb; - /* Add this filename to the parent directory's list of elements that - have been compared. */ - apr_hash_set(pb->compared, apr_pstrdup(pb->pool, full_path), - APR_HASH_KEY_STRING, ""); + if (SVN_IS_VALID_REVNUM(copyfrom_revision)) + { + fb = make_file_baton(full_path, FALSE, pb, file_pool); + *file_baton = fb; + fb->copyfrom_revision = copyfrom_revision; + SVN_ERR(get_file_from_ra(fb, copyfrom_path+1, copyfrom_revision)); + apr_hash_set(pb->compared, apr_pstrdup(pb->pool, copyfrom_path), + APR_HASH_KEY_STRING, ""); + } + else + { + fb = make_file_baton(full_path, TRUE, pb, file_pool); + *file_baton = fb; + + /* Add this filename to the parent directory's list of elements that + have been compared. */ + apr_hash_set(pb->compared, apr_pstrdup(pb->pool, full_path), + APR_HASH_KEY_STRING, ""); + } return SVN_NO_ERROR; } @@ -1537,12 +1589,22 @@ else { /* The current text-base is the starting point if replacing */ - SVN_ERR(svn_wc__get_pristine_contents(&source, eb->db, fb->local_abspath, - fb->pool, fb->pool)); - if (source == NULL) - source = svn_stream_empty(fb->pool); + if (fb->copyfrom_temp_path) + { + SVN_ERR(svn_io_file_open(&(fb->copyfrom_temp_file), + fb->copyfrom_temp_path, + APR_READ, APR_OS_DEFAULT, fb->pool)); + source = svn_stream_from_aprfile2(fb->copyfrom_temp_file, TRUE, + fb->pool); + } + else + { + SVN_ERR(svn_wc__get_pristine_contents(&source, eb->db, fb->local_abspath, + fb->pool, fb->pool)); + if (source == NULL) + source = svn_stream_empty(fb->pool); + } } - /* This is the file that will contain the pristine repository version. It is created in the admin temporary area. This file continues to exists until after the diff callback is run, at which point it is deleted. */ @@ -1552,7 +1614,8 @@ temp_dir, svn_io_file_del_on_pool_cleanup, fb->pool, fb->pool)); - svn_txdelta_apply(source, temp_stream, + svn_txdelta_apply(source, + temp_stream, NULL, fb->temp_file_path /* error_info */, fb->pool, @@ -1613,8 +1676,11 @@ if (fb->added) base_props = apr_hash_make(pool); else - SVN_ERR(svn_wc__get_pristine_props(&base_props, eb->db, - fb->local_abspath, pool, pool)); + if(fb->pristine_props) + base_props = fb->pristine_props; + else + SVN_ERR(svn_wc__get_pristine_props(&base_props, eb->db, + fb->local_abspath, pool, pool)); repos_props = apply_propchanges(base_props, fb->propchanges); @@ -1690,9 +1756,12 @@ if (modified) { if (eb->use_text_base) - SVN_ERR(svn_wc__text_base_path_to_read(&localfile, - eb->db, fb->local_abspath, - fb->pool, pool)); + if(fb->copyfrom_temp_path) + localfile = fb->copyfrom_temp_path; + else + SVN_ERR(svn_wc__text_base_path_to_read(&localfile, + eb->db, fb->local_abspath, + fb->pool, pool)); else /* a detranslated version of the working file */ SVN_ERR(svn_wc__internal_translated_file( @@ -1737,10 +1806,14 @@ ? temp_file_path : localfile, eb->reverse_order - ? SVN_INVALID_REVNUM + ? fb->copyfrom_revision + ? fb->copyfrom_revision + : SVN_INVALID_REVNUM : eb->revnum, eb->reverse_order ? eb->revnum + : fb->copyfrom_revision + ? fb->copyfrom_revision : SVN_INVALID_REVNUM, eb->reverse_order ? original_mimetype @@ -1819,6 +1892,7 @@ svn_error_t * svn_wc_get_diff_editor6(const svn_delta_editor_t **editor, void **edit_baton, + svn_ra_session_t *ra_session, svn_wc_context_t *wc_ctx, const char *anchor_path, const char *target, @@ -1842,7 +1916,7 @@ const svn_delta_editor_t *inner_editor; const char *anchor_abspath; - SVN_ERR(make_edit_baton(&eb, + SVN_ERR(make_edit_baton(&eb, ra_session, wc_ctx->db, anchor_path, target, callbacks, callback_baton, @@ -1928,7 +2002,7 @@ else svn_dirent_split(&anchor_path, &target, target_path, pool); - SVN_ERR(make_edit_baton(&eb, + SVN_ERR(make_edit_baton(&eb, NULL, wc_ctx->db, anchor_path, target, Index: subversion/libsvn_client/repos_diff.c =================================================================== --- subversion/libsvn_client/repos_diff.c (revision 1053833) +++ subversion/libsvn_client/repos_diff.c (working copy) @@ -93,6 +93,9 @@ FALSE otherwise. */ svn_boolean_t walk_deleted_repos_dirs; + /* TRUE if the copied contents need to be shown as added. */ + svn_boolean_t show_copies_as_adds; + /* A callback used to see if the client wishes to cancel the running operation. */ svn_cancel_func_t cancel_func; @@ -175,10 +178,10 @@ const char *wcpath; /* The path and APR file handle to the temporary file that contains the - first repository version. Also, the pristine-property list of - this file. */ + first repository version. */ const char *path_start_revision; apr_file_t *file_start_revision; + apr_hash_t *pristine_props; /* The path and APR file handle to the temporary file that contains the @@ -198,6 +201,9 @@ /* A cache of any property changes (svn_prop_t) received for this file. */ apr_array_header_t *propchanges; + /* The copyfrom revision */ + svn_revnum_t copyfrom_revision; + /* The pool passed in by add_file or open_file. Also, the pool this file_baton is allocated in. */ apr_pool_t *pool; @@ -308,7 +314,8 @@ * the file. */ static svn_error_t * -get_file_from_ra(struct file_baton *b, svn_revnum_t revision) +get_file_from_ra(struct file_baton *b, const char *path, + svn_revnum_t revision) { svn_stream_t *fstream; @@ -317,7 +324,7 @@ b->pool)); SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session, - b->path, + path, revision, fstream, NULL, &(b->pristine_props), @@ -508,7 +515,7 @@ /* Compare a file being deleted against an empty file */ b = make_file_baton(path, FALSE, eb, iterpool); - SVN_ERR(get_file_from_ra(b, revision)); + SVN_ERR(get_file_from_ra(b, path, revision)); SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision))); @@ -573,7 +580,7 @@ /* Compare a file being deleted against an empty file */ b = make_file_baton(path, FALSE, eb, pool); - SVN_ERR(get_file_from_ra(b, eb->revision)); + SVN_ERR(get_file_from_ra(b, path, eb->revision)); SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision))); get_file_mime_types(&mimetype1, &mimetype2, b); @@ -791,8 +798,6 @@ struct dir_baton *pb = parent_baton; struct file_baton *b; - /* ### TODO: support copyfrom? */ - b = make_file_baton(path, TRUE, pb->edit_baton, pool); *file_baton = b; @@ -804,9 +809,29 @@ return SVN_NO_ERROR; } - SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision))); - b->pristine_props = pb->edit_baton->empty_hash; - + if(!pb->edit_baton->show_copies_as_adds + && SVN_IS_VALID_REVNUM(copyfrom_revision)) + { + const char *preserved_url; + const char *repos_root; + b->copyfrom_revision = copyfrom_revision; + SVN_ERR(svn_ra_get_session_url(pb->edit_baton->ra_session, + &preserved_url, + pool)); + SVN_ERR(svn_ra_get_repos_root2(pb->edit_baton->ra_session, + &repos_root, + pool)); + SVN_ERR(svn_ra_reparent(pb->edit_baton->ra_session, repos_root, + pool)); + SVN_ERR(get_file_from_ra(b, copyfrom_path+1, copyfrom_revision)); + SVN_ERR(svn_ra_reparent(pb->edit_baton->ra_session, + preserved_url, pool)); + } + else + { + SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision))); + b->pristine_props = pb->edit_baton->empty_hash; + } return SVN_NO_ERROR; } @@ -831,7 +856,7 @@ return SVN_NO_ERROR; } - SVN_ERR(get_file_from_ra(b, base_revision)); + SVN_ERR(get_file_from_ra(b, path, base_revision)); return SVN_NO_ERROR; } @@ -962,7 +987,7 @@ const char *mimetype1, *mimetype2; get_file_mime_types(&mimetype1, &mimetype2, b); - if (b->added) + if (b->added && eb->show_copies_as_adds) SVN_ERR(eb->diff_callbacks->file_added (local_dir_abspath, &content_state, &prop_state, &b->tree_conflicted, b->wcpath, @@ -981,7 +1006,8 @@ &b->tree_conflicted, b->wcpath, b->path_end_revision ? b->path_start_revision : NULL, b->path_end_revision, - b->edit_baton->revision, + b->copyfrom_revision ? b->copyfrom_revision + : b->edit_baton->revision, b->edit_baton->target_revision, mimetype1, mimetype2, b->propchanges, b->pristine_props, @@ -1298,6 +1324,7 @@ svn_revnum_t revision, svn_wc_notify_func2_t notify_func, void *notify_baton, + svn_boolean_t show_copies_as_adds, svn_cancel_func_t cancel_func, void *cancel_baton, const svn_delta_editor_t **editor, @@ -1324,6 +1351,7 @@ eb->notify_func = notify_func; eb->notify_baton = notify_baton; eb->walk_deleted_repos_dirs = TRUE; + eb->show_copies_as_adds = show_copies_as_adds; eb->cancel_func = cancel_func; eb->cancel_baton = cancel_baton; Index: subversion/libsvn_client/client.h =================================================================== --- subversion/libsvn_client/client.h (revision 1053833) +++ subversion/libsvn_client/client.h (working copy) @@ -644,6 +644,7 @@ svn_revnum_t revision, svn_wc_notify_func2_t notify_func, void *notify_baton, + svn_boolean_t show_copies_as_adds, svn_cancel_func_t cancel_func, void *cancel_baton, const svn_delta_editor_t **editor, Index: subversion/libsvn_client/merge.c =================================================================== --- subversion/libsvn_client/merge.c (revision 1053833) +++ subversion/libsvn_client/merge.c (working copy) @@ -5052,14 +5052,15 @@ merge_b->dry_run, merge_b->ra_session2, revision1, notification_receiver, notify_b, - merge_b->ctx->cancel_func, + TRUE, merge_b->ctx->cancel_func, merge_b->ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool)); - SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1, + SVN_ERR(svn_ra_do_diff4(merge_b->ra_session1, &reporter, &report_baton, revision2, "", depth, merge_b->ignore_ancestry, TRUE, /* text_deltas */ + FALSE, /* send_copyfrom_args */ url2, diff_editor, diff_edit_baton, pool)); /* Drive the reporter. */ Index: subversion/libsvn_client/diff.c =================================================================== --- subversion/libsvn_client/diff.c (revision 1053833) +++ subversion/libsvn_client/diff.c (working copy) @@ -1676,6 +1676,7 @@ const svn_opt_revision_t *peg_revision, svn_depth_t depth, svn_boolean_t ignore_ancestry, + svn_boolean_t show_copies_as_adds, apr_pool_t *pool) { svn_ra_session_t *extra_ra_session; @@ -1728,13 +1729,13 @@ NULL, callbacks, callback_baton, depth, FALSE /* doesn't matter for diff */, extra_ra_session, rev1, NULL /* no notify_func */, NULL /* no notify_baton */, - ctx->cancel_func, ctx->cancel_baton, + show_copies_as_adds, ctx->cancel_func, ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool)); /* We want to switch our txn into URL2 */ - SVN_ERR(svn_ra_do_diff3 + SVN_ERR(svn_ra_do_diff4 (ra_session, &reporter, &reporter_baton, rev2, target1, - depth, ignore_ancestry, TRUE, + depth, ignore_ancestry, TRUE, show_copies_as_adds ? FALSE : TRUE, url2, diff_editor, diff_edit_baton, pool)); /* Drive the reporter; do the diff. */ @@ -1776,6 +1777,7 @@ const char *url1, *anchor, *anchor_url, *target; svn_revnum_t rev; svn_ra_session_t *ra_session; + svn_ra_session_t *extra_ra_session; const svn_ra_reporter3_t *reporter; void *reporter_baton; const svn_delta_editor_t *diff_editor; @@ -1785,6 +1787,7 @@ const char *abspath1; const char *abspath2; const char *anchor_abspath; + const char *repos_root; SVN_ERR_ASSERT(! svn_path_is_url(path2)); @@ -1843,12 +1846,24 @@ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, anchor_url, NULL, NULL, FALSE, TRUE, ctx, pool)); + /* Establish RA session to path2's anchor */ + SVN_ERR(svn_client__open_ra_session_internal(&extra_ra_session, NULL, + anchor_url, + NULL, NULL, FALSE, TRUE, + ctx, pool)); + SVN_ERR(svn_ra_get_repos_root2(ra_session, + &repos_root, + pool)); + SVN_ERR(svn_ra_reparent(extra_ra_session, repos_root, + pool)); + callback_baton->ra_session = ra_session; if (use_git_diff_format) callback_baton->wc_root_abspath = find_wc_root(anchor_abspath, ctx->wc_ctx, pool, pool); SVN_ERR(svn_wc_get_diff_editor6(&diff_editor, &diff_edit_baton, + extra_ra_session, ctx->wc_ctx, anchor, target, @@ -1873,13 +1888,14 @@ else callback_baton->revnum2 = rev; - SVN_ERR(svn_ra_do_diff3(ra_session, + SVN_ERR(svn_ra_do_diff4(ra_session, &reporter, &reporter_baton, rev, target ? svn_path_uri_decode(target, pool) : NULL, depth, ignore_ancestry, TRUE, /* text_deltas */ + show_copies_as_adds ? FALSE : TRUE, url1, diff_editor, diff_edit_baton, pool)); @@ -1929,7 +1945,7 @@ SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx, path1, path2, revision1, revision2, peg_revision, depth, ignore_ancestry, - pool)); + show_copies_as_adds, pool)); } else /* path2 is a working copy path */ { @@ -2015,10 +2031,12 @@ ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool)); /* We want to switch our txn into URL2 */ - SVN_ERR(svn_ra_do_diff3 + SVN_ERR(svn_ra_do_diff4 (ra_session, &reporter, &reporter_baton, rev2, target1, depth, ignore_ancestry, - FALSE /* do not create text delta */, url2, diff_editor, + FALSE /* do not create text delta */, + FALSE /* send_copyfrom_args */, + url2, diff_editor, diff_edit_baton, pool)); /* Drive the reporter; do the diff. */ Index: subversion/libsvn_ra_neon/ra_neon.h =================================================================== --- subversion/libsvn_ra_neon/ra_neon.h (revision 1053833) +++ subversion/libsvn_ra_neon/ra_neon.h (working copy) @@ -349,6 +349,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *wc_diff, void *wc_diff_baton, Index: subversion/libsvn_ra_neon/fetch.c =================================================================== --- subversion/libsvn_ra_neon/fetch.c (revision 1053833) +++ subversion/libsvn_ra_neon/fetch.c (working copy) @@ -2809,6 +2809,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *wc_diff, void *wc_diff_baton, @@ -2821,7 +2822,7 @@ diff_target, versus_url, depth, - FALSE, + send_copyfrom_args, ignore_ancestry, FALSE, wc_diff, Index: subversion/libsvn_ra_serf/update.c =================================================================== --- subversion/libsvn_ra_serf/update.c (revision 1053833) +++ subversion/libsvn_ra_serf/update.c (working copy) @@ -2631,6 +2631,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *diff_editor, void *diff_baton, @@ -2641,7 +2642,8 @@ return make_update_reporter(ra_session, reporter, report_baton, revision, session->repos_url.path, versus_url, diff_target, - depth, ignore_ancestry, text_deltas, FALSE, + depth, ignore_ancestry, text_deltas, + send_copyfrom_args, diff_editor, diff_baton, pool); } Index: subversion/libsvn_ra_serf/ra_serf.h =================================================================== --- subversion/libsvn_ra_serf/ra_serf.h (revision 1053833) +++ subversion/libsvn_ra_serf/ra_serf.h (working copy) @@ -1247,6 +1247,7 @@ svn_depth_t depth, svn_boolean_t ignore_ancestry, svn_boolean_t text_deltas, + svn_boolean_t send_copyfrom_args, const char *versus_url, const svn_delta_editor_t *diff_editor, void *diff_baton, Index: subversion/svnserve/serve.c =================================================================== --- subversion/svnserve/serve.c (revision 1053833) +++ subversion/svnserve/serve.c (working copy) @@ -1774,6 +1774,8 @@ /* Default to unknown. Old clients won't send depth, but we'll handle that by converting recurse if necessary. */ svn_depth_t depth = svn_depth_unknown; +/* Default to false. Pre-1.7 clients won't send send_copyfrom_args. */ + svn_boolean_t send_copyfrom_args = FALSE; /* Parse the arguments. */ if (params->nelts == 5) @@ -1786,10 +1788,11 @@ } else { - SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbcb?w", + SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbcb?w?b", &rev, &target, &recurse, &ignore_ancestry, &versus_url, - &text_deltas, &depth_word)); + &text_deltas, &depth_word, + &send_copyfrom_args)); } target = svn_uri_canonicalize(target, pool); versus_url = svn_uri_canonicalize(versus_url, pool); @@ -1812,7 +1815,8 @@ svn_revnum_t from_rev; SVN_ERR(accept_report(NULL, &from_rev, conn, pool, b, rev, target, versus_path, - text_deltas, depth, FALSE, ignore_ancestry)); + text_deltas, depth, send_copyfrom_args, + ignore_ancestry)); SVN_ERR(log_command(b, conn, pool, "%s", svn_log__diff(full_path, from_rev, versus_path, rev, depth, ignore_ancestry,