summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/diff.c')
-rw-r--r--subversion/libsvn_client/diff.c2322
1 files changed, 1270 insertions, 1052 deletions
diff --git a/subversion/libsvn_client/diff.c b/subversion/libsvn_client/diff.c
index 0161b63..26890ae 100644
--- a/subversion/libsvn_client/diff.c
+++ b/subversion/libsvn_client/diff.c
@@ -33,7 +33,6 @@
#include "svn_types.h"
#include "svn_hash.h"
#include "svn_wc.h"
-#include "svn_delta.h"
#include "svn_diff.h"
#include "svn_mergeinfo.h"
#include "svn_client.h"
@@ -46,307 +45,198 @@
#include "svn_pools.h"
#include "svn_config.h"
#include "svn_props.h"
-#include "svn_time.h"
-#include "svn_sorts.h"
#include "svn_subst.h"
#include "client.h"
#include "private/svn_wc_private.h"
+#include "private/svn_diff_private.h"
+#include "private/svn_subr_private.h"
+#include "private/svn_io_private.h"
#include "svn_private_config.h"
-/*
- * Constant separator strings
- */
-static const char equal_string[] =
- "===================================================================";
-static const char under_string[] =
- "___________________________________________________________________";
-
-
-/*-----------------------------------------------------------------*/
-
/* Utilities */
-/* Wrapper for apr_file_printf(), which see. FORMAT is a utf8-encoded
- string after it is formatted, so this function can convert it to
- ENCODING before printing. */
-static svn_error_t *
-file_printf_from_utf8(apr_file_t *fptr, const char *encoding,
- const char *format, ...)
- __attribute__ ((format(printf, 3, 4)));
-static svn_error_t *
-file_printf_from_utf8(apr_file_t *fptr, const char *encoding,
- const char *format, ...)
-{
- va_list ap;
- const char *buf, *buf_apr;
-
- va_start(ap, format);
- buf = apr_pvsprintf(apr_file_pool_get(fptr), format, ap);
- va_end(ap);
-
- SVN_ERR(svn_utf_cstring_from_utf8_ex2(&buf_apr, buf, encoding,
- apr_file_pool_get(fptr)));
-
- return svn_io_file_write_full(fptr, buf_apr, strlen(buf_apr),
- NULL, apr_file_pool_get(fptr));
-}
-
-
-/* A helper function for display_prop_diffs. Output the differences between
- the mergeinfo stored in ORIG_MERGEINFO_VAL and NEW_MERGEINFO_VAL in a
- human-readable form to FILE, using ENCODING. Use POOL for temporary
- allocations. */
-static svn_error_t *
-display_mergeinfo_diff(const char *old_mergeinfo_val,
- const char *new_mergeinfo_val,
- const char *encoding,
- apr_file_t *file,
- apr_pool_t *pool)
-{
- apr_hash_t *old_mergeinfo_hash, *new_mergeinfo_hash, *added, *deleted;
- apr_hash_index_t *hi;
-
- if (old_mergeinfo_val)
- SVN_ERR(svn_mergeinfo_parse(&old_mergeinfo_hash, old_mergeinfo_val, pool));
- else
- old_mergeinfo_hash = NULL;
-
- if (new_mergeinfo_val)
- SVN_ERR(svn_mergeinfo_parse(&new_mergeinfo_hash, new_mergeinfo_val, pool));
- else
- new_mergeinfo_hash = NULL;
-
- SVN_ERR(svn_mergeinfo_diff(&deleted, &added, old_mergeinfo_hash,
- new_mergeinfo_hash,
- TRUE, pool));
-
- for (hi = apr_hash_first(pool, deleted);
- hi; hi = apr_hash_next(hi))
- {
- const char *from_path = svn__apr_hash_index_key(hi);
- apr_array_header_t *merge_revarray = svn__apr_hash_index_val(hi);
- svn_string_t *merge_revstr;
-
- SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray, pool));
-
- SVN_ERR(file_printf_from_utf8(file, encoding,
- _(" Reverse-merged %s:r%s%s"),
- from_path, merge_revstr->data,
- APR_EOL_STR));
- }
-
- for (hi = apr_hash_first(pool, added);
- hi; hi = apr_hash_next(hi))
- {
- const char *from_path = svn__apr_hash_index_key(hi);
- apr_array_header_t *merge_revarray = svn__apr_hash_index_val(hi);
- svn_string_t *merge_revstr;
-
- SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray, pool));
-
- SVN_ERR(file_printf_from_utf8(file, encoding,
- _(" Merged %s:r%s%s"),
- from_path, merge_revstr->data,
- APR_EOL_STR));
- }
-
- return SVN_NO_ERROR;
-}
#define MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir) \
svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL, \
_("Path '%s' must be an immediate child of " \
"the directory '%s'"), path, relative_to_dir)
-/* A helper function used by display_prop_diffs.
- TOKEN is a string holding a property value.
- If TOKEN is empty, or is already terminated by an EOL marker,
- return TOKEN unmodified. Else, return a new string consisting
- of the concatenation of TOKEN and the system's default EOL marker.
- The new string is allocated from POOL.
- If HAD_EOL is not NULL, indicate in *HAD_EOL if the token had a EOL. */
-static const svn_string_t *
-maybe_append_eol(const svn_string_t *token, svn_boolean_t *had_eol,
- apr_pool_t *pool)
-{
- const char *curp;
-
- if (had_eol)
- *had_eol = FALSE;
-
- if (token->len == 0)
- return token;
-
- curp = token->data + token->len - 1;
- if (*curp == '\r')
- {
- if (had_eol)
- *had_eol = TRUE;
- return token;
- }
- else if (*curp != '\n')
- {
- return svn_string_createf(pool, "%s%s", token->data, APR_EOL_STR);
- }
- else
- {
- if (had_eol)
- *had_eol = TRUE;
- return token;
- }
-}
-
-/* Adjust PATH to be relative to the repository root beneath ORIG_TARGET,
- * using RA_SESSION and WC_CTX, and return the result in *ADJUSTED_PATH.
- * ORIG_TARGET is one of the original targets passed to the diff command,
+/* Calculate the repository relative path of DIFF_RELPATH, using RA_SESSION
+ * and WC_CTX, and return the result in *REPOS_RELPATH.
+ * ORIG_TARGET is the related original target passed to the diff command,
* and may be used to derive leading path components missing from PATH.
- * WC_ROOT_ABSPATH is the absolute path to the root directory of a working
- * copy involved in a repos-wc diff, and may be NULL.
+ * ANCHOR is the local path where the diff editor is anchored.
* Do all allocations in POOL. */
static svn_error_t *
-adjust_relative_to_repos_root(const char **adjusted_path,
- const char *path,
- const char *orig_target,
- svn_ra_session_t *ra_session,
- svn_wc_context_t *wc_ctx,
- const char *wc_root_abspath,
- apr_pool_t *pool)
+make_repos_relpath(const char **repos_relpath,
+ const char *diff_relpath,
+ const char *orig_target,
+ svn_ra_session_t *ra_session,
+ svn_wc_context_t *wc_ctx,
+ const char *anchor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
const char *local_abspath;
- const char *orig_relpath;
- const char *child_relpath;
+ const char *orig_repos_relpath = NULL;
- if (! ra_session)
+ if (! ra_session
+ || (anchor && !svn_path_is_url(orig_target)))
{
+ svn_error_t *err;
/* We're doing a WC-WC diff, so we can retrieve all information we
* need from the working copy. */
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
- SVN_ERR(svn_wc__node_get_repos_relpath(adjusted_path, wc_ctx,
- local_abspath, pool, pool));
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath,
+ svn_dirent_join(anchor, diff_relpath,
+ scratch_pool),
+ scratch_pool));
+
+ err = svn_wc__node_get_repos_info(NULL, repos_relpath, NULL, NULL,
+ wc_ctx, local_abspath,
+ result_pool, scratch_pool);
+
+ if (!ra_session
+ || ! err
+ || (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND))
+ {
+ return svn_error_trace(err);
+ }
- /* Now deal with the repos-repos and repos-wc diff cases.
- * We need to make PATH appear as a child of ORIG_TARGET.
- * ORIG_TARGET is either a URL or a path to a working copy. First,
- * find out what ORIG_TARGET looks like relative to the repository root.*/
- if (svn_path_is_url(orig_target))
- SVN_ERR(svn_ra_get_path_relative_to_root(ra_session,
- &orig_relpath,
- orig_target, pool));
- else
- {
- const char *orig_abspath;
+ /* The path represents a local working copy path, but does not
+ exist. Fall through to calculate an in-repository location
+ based on the ra session */
- SVN_ERR(svn_dirent_get_absolute(&orig_abspath, orig_target, pool));
- SVN_ERR(svn_wc__node_get_repos_relpath(&orig_relpath, wc_ctx,
- orig_abspath, pool, pool));
+ /* ### Maybe we should use the nearest existing ancestor instead? */
+ svn_error_clear(err);
}
- /* PATH is either a child of the working copy involved in the diff (in
- * the repos-wc diff case), or it's a relative path we can readily use
- * (in either of the repos-repos and repos-wc diff cases). */
- child_relpath = NULL;
- if (wc_root_abspath)
- {
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
- child_relpath = svn_dirent_is_child(wc_root_abspath, local_abspath, pool);
- }
- if (child_relpath == NULL)
- child_relpath = path;
+ {
+ const char *url;
+ const char *repos_root_url;
- *adjusted_path = svn_relpath_join(orig_relpath, child_relpath, pool);
+ /* Would be nice if the RA layer could just provide the parent
+ repos_relpath of the ra session */
+ SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+ scratch_pool));
+
+ orig_repos_relpath = svn_uri_skip_ancestor(repos_root_url, url,
+ scratch_pool);
+
+ *repos_relpath = svn_relpath_join(orig_repos_relpath, diff_relpath,
+ result_pool);
+ }
return SVN_NO_ERROR;
}
-/* Adjust PATH, ORIG_PATH_1 and ORIG_PATH_2, representing the changed file
- * and the two original targets passed to the diff command, to handle the
+/* Adjust *INDEX_PATH, *ORIG_PATH_1 and *ORIG_PATH_2, representing the changed
+ * node and the two original targets passed to the diff command, to handle the
* case when we're dealing with different anchors. RELATIVE_TO_DIR is the
- * directory the diff target should be considered relative to. All
- * allocations are done in POOL. */
+ * directory the diff target should be considered relative to.
+ * ANCHOR is the local path where the diff editor is anchored. The resulting
+ * values are allocated in RESULT_POOL and temporary allocations are performed
+ * in SCRATCH_POOL. */
static svn_error_t *
-adjust_paths_for_diff_labels(const char **path,
+adjust_paths_for_diff_labels(const char **index_path,
const char **orig_path_1,
const char **orig_path_2,
const char *relative_to_dir,
- apr_pool_t *pool)
+ const char *anchor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_size_t len;
- const char *new_path = *path;
+ const char *new_path = *index_path;
const char *new_path1 = *orig_path_1;
const char *new_path2 = *orig_path_2;
- /* ### Holy cow. Due to anchor/target weirdness, we can't
- simply join diff_cmd_baton->orig_path_1 with path, ditto for
- orig_path_2. That will work when they're directory URLs, but
- not for file URLs. Nor can we just use anchor1 and anchor2
- from do_diff(), at least not without some more logic here.
- What a nightmare.
-
- For now, to distinguish the two paths, we'll just put the
- unique portions of the original targets in parentheses after
- the received path, with ellipses for handwaving. This makes
- the labels a bit clumsy, but at least distinctive. Better
- solutions are possible, they'll just take more thought. */
-
- len = strlen(svn_dirent_get_longest_ancestor(new_path1, new_path2, pool));
- new_path1 = new_path1 + len;
- new_path2 = new_path2 + len;
-
- /* ### Should diff labels print paths in local style? Is there
- already a standard for this? In any case, this code depends on
- a particular style, so not calling svn_dirent_local_style() on the
- paths below.*/
- if (new_path1[0] == '\0')
- new_path1 = apr_psprintf(pool, "%s", new_path);
- else if (new_path1[0] == '/')
- new_path1 = apr_psprintf(pool, "%s\t(...%s)", new_path, new_path1);
- else
- new_path1 = apr_psprintf(pool, "%s\t(.../%s)", new_path, new_path1);
-
- if (new_path2[0] == '\0')
- new_path2 = apr_psprintf(pool, "%s", new_path);
- else if (new_path2[0] == '/')
- new_path2 = apr_psprintf(pool, "%s\t(...%s)", new_path, new_path2);
- else
- new_path2 = apr_psprintf(pool, "%s\t(.../%s)", new_path, new_path2);
+ if (anchor)
+ new_path = svn_dirent_join(anchor, new_path, result_pool);
if (relative_to_dir)
{
/* Possibly adjust the paths shown in the output (see issue #2723). */
const char *child_path = svn_dirent_is_child(relative_to_dir, new_path,
- pool);
+ result_pool);
if (child_path)
new_path = child_path;
- else if (!svn_path_compare_paths(relative_to_dir, new_path))
+ else if (! strcmp(relative_to_dir, new_path))
new_path = ".";
else
return MAKE_ERR_BAD_RELATIVE_PATH(new_path, relative_to_dir);
- child_path = svn_dirent_is_child(relative_to_dir, new_path1, pool);
+ child_path = svn_dirent_is_child(relative_to_dir, new_path1,
+ result_pool);
+ }
- if (child_path)
- new_path1 = child_path;
- else if (!svn_path_compare_paths(relative_to_dir, new_path1))
- new_path1 = ".";
- else
- return MAKE_ERR_BAD_RELATIVE_PATH(new_path1, relative_to_dir);
+ {
+ apr_size_t len;
+ svn_boolean_t is_url1;
+ svn_boolean_t is_url2;
+ /* ### Holy cow. Due to anchor/target weirdness, we can't
+ simply join diff_cmd_baton->orig_path_1 with path, ditto for
+ orig_path_2. That will work when they're directory URLs, but
+ not for file URLs. Nor can we just use anchor1 and anchor2
+ from do_diff(), at least not without some more logic here.
+ What a nightmare.
+
+ For now, to distinguish the two paths, we'll just put the
+ unique portions of the original targets in parentheses after
+ the received path, with ellipses for handwaving. This makes
+ the labels a bit clumsy, but at least distinctive. Better
+ solutions are possible, they'll just take more thought. */
+
+ /* ### BH: We can now just construct the repos_relpath, etc. as the
+ anchor is available. See also make_repos_relpath() */
+
+ is_url1 = svn_path_is_url(new_path1);
+ is_url2 = svn_path_is_url(new_path2);
+
+ if (is_url1 && is_url2)
+ len = strlen(svn_uri_get_longest_ancestor(new_path1, new_path2,
+ scratch_pool));
+ else if (!is_url1 && !is_url2)
+ len = strlen(svn_dirent_get_longest_ancestor(new_path1, new_path2,
+ scratch_pool));
+ else
+ len = 0; /* Path and URL */
+
+ new_path1 += len;
+ new_path2 += len;
+ }
- child_path = svn_dirent_is_child(relative_to_dir, new_path2, pool);
+ /* ### Should diff labels print paths in local style? Is there
+ already a standard for this? In any case, this code depends on
+ a particular style, so not calling svn_dirent_local_style() on the
+ paths below.*/
- if (child_path)
- new_path2 = child_path;
- else if (!svn_path_compare_paths(relative_to_dir, new_path2))
- new_path2 = ".";
- else
- return MAKE_ERR_BAD_RELATIVE_PATH(new_path2, relative_to_dir);
- }
- *path = new_path;
+ if (new_path[0] == '\0')
+ new_path = ".";
+
+ if (new_path1[0] == '\0')
+ new_path1 = new_path;
+ else if (svn_path_is_url(new_path1))
+ new_path1 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path1);
+ else if (new_path1[0] == '/')
+ new_path1 = apr_psprintf(result_pool, "%s\t(...%s)", new_path, new_path1);
+ else
+ new_path1 = apr_psprintf(result_pool, "%s\t(.../%s)", new_path, new_path1);
+
+ if (new_path2[0] == '\0')
+ new_path2 = new_path;
+ else if (svn_path_is_url(new_path2))
+ new_path1 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2);
+ else if (new_path2[0] == '/')
+ new_path2 = apr_psprintf(result_pool, "%s\t(...%s)", new_path, new_path2);
+ else
+ new_path2 = apr_psprintf(result_pool, "%s\t(.../%s)", new_path, new_path2);
+
+ *index_path = new_path;
*orig_path_1 = new_path1;
*orig_path_2 = new_path2;
@@ -409,15 +299,22 @@ print_git_diff_header_deleted(svn_stream_t *os, const char *header_encoding,
* OS using HEADER_ENCODING. All allocations are done in RESULT_POOL. */
static svn_error_t *
print_git_diff_header_copied(svn_stream_t *os, const char *header_encoding,
- const char *copyfrom_path, const char *path,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_rev,
+ const char *path,
apr_pool_t *result_pool)
{
SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, result_pool,
"diff --git a/%s b/%s%s",
copyfrom_path, path, APR_EOL_STR));
- SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, result_pool,
- "copy from %s%s", copyfrom_path,
- APR_EOL_STR));
+ if (copyfrom_rev != SVN_INVALID_REVNUM)
+ SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, result_pool,
+ "copy from %s@%ld%s", copyfrom_path,
+ copyfrom_rev, APR_EOL_STR));
+ else
+ SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, result_pool,
+ "copy from %s%s", copyfrom_path,
+ APR_EOL_STR));
SVN_ERR(svn_stream_printf_from_utf8(os, header_encoding, result_pool,
"copy to %s%s", path, APR_EOL_STR));
return SVN_NO_ERROR;
@@ -459,11 +356,8 @@ print_git_diff_header_modified(svn_stream_t *os, const char *header_encoding,
* HEADER_ENCODING. Return suitable diff labels for the git diff in *LABEL1
* and *LABEL2. REPOS_RELPATH1 and REPOS_RELPATH2 are relative to reposroot.
* are the paths passed to the original diff command. REV1 and REV2 are
- * revisions being diffed. COPYFROM_PATH indicates where the diffed item
- * was copied from. RA_SESSION and WC_CTX are used to adjust paths in the
- * headers to be relative to the repository root.
- * WC_ROOT_ABSPATH is the absolute path to the root directory of a working
- * copy involved in a repos-wc diff, and may be NULL.
+ * revisions being diffed. COPYFROM_PATH and COPYFROM_REV indicate where the
+ * diffed item was copied from.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
print_git_diff_header(svn_stream_t *os,
@@ -474,10 +368,8 @@ print_git_diff_header(svn_stream_t *os,
svn_revnum_t rev1,
svn_revnum_t rev2,
const char *copyfrom_path,
+ svn_revnum_t copyfrom_rev,
const char *header_encoding,
- svn_ra_session_t *ra_session,
- svn_wc_context_t *wc_ctx,
- const char *wc_root_abspath,
apr_pool_t *scratch_pool)
{
if (operation == svn_diff_op_deleted)
@@ -493,7 +385,8 @@ print_git_diff_header(svn_stream_t *os,
else if (operation == svn_diff_op_copied)
{
SVN_ERR(print_git_diff_header_copied(os, header_encoding,
- copyfrom_path, repos_relpath2,
+ copyfrom_path, copyfrom_rev,
+ repos_relpath2,
scratch_pool));
*label1 = diff_label(apr_psprintf(scratch_pool, "a/%s", copyfrom_path),
rev1, scratch_pool);
@@ -534,8 +427,8 @@ print_git_diff_header(svn_stream_t *os,
}
/* A helper func that writes out verbal descriptions of property diffs
- to FILE. Of course, the apr_file_t will probably be the 'outfile'
- passed to svn_client_diff5, which is probably stdout.
+ to OUTSTREAM. Of course, OUTSTREAM will probably be whatever was
+ passed to svn_client_diff6(), which is probably stdout.
### FIXME needs proper docstring
@@ -544,189 +437,91 @@ print_git_diff_header(svn_stream_t *os,
needed to normalize paths relative the repository root, and are ignored
if USE_GIT_DIFF_FORMAT is FALSE.
- WC_ROOT_ABSPATH is the absolute path to the root directory of a working
- copy involved in a repos-wc diff, and may be NULL. */
+ ANCHOR is the local path where the diff editor is anchored. */
static svn_error_t *
display_prop_diffs(const apr_array_header_t *propchanges,
apr_hash_t *original_props,
- const char *path,
+ const char *diff_relpath,
+ const char *anchor,
const char *orig_path1,
const char *orig_path2,
svn_revnum_t rev1,
svn_revnum_t rev2,
const char *encoding,
- apr_file_t *file,
+ svn_stream_t *outstream,
const char *relative_to_dir,
svn_boolean_t show_diff_header,
svn_boolean_t use_git_diff_format,
svn_ra_session_t *ra_session,
svn_wc_context_t *wc_ctx,
- const char *wc_root_abspath,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- int i;
- const char *path1 = apr_pstrdup(pool, orig_path1);
- const char *path2 = apr_pstrdup(pool, orig_path2);
+ const char *repos_relpath1 = NULL;
+ const char *repos_relpath2 = NULL;
+ const char *index_path = diff_relpath;
+ const char *adjusted_path1 = orig_path1;
+ const char *adjusted_path2 = orig_path2;
if (use_git_diff_format)
{
- SVN_ERR(adjust_relative_to_repos_root(&path1, path, orig_path1,
- ra_session, wc_ctx,
- wc_root_abspath,
- pool));
- SVN_ERR(adjust_relative_to_repos_root(&path2, path, orig_path2,
- ra_session, wc_ctx,
- wc_root_abspath,
- pool));
+ SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, orig_path1,
+ ra_session, wc_ctx, anchor,
+ scratch_pool, scratch_pool));
+ SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, orig_path2,
+ ra_session, wc_ctx, anchor,
+ scratch_pool, scratch_pool));
}
/* If we're creating a diff on the wc root, path would be empty. */
- if (path[0] == '\0')
- path = apr_psprintf(pool, ".");
+ SVN_ERR(adjust_paths_for_diff_labels(&index_path, &adjusted_path1,
+ &adjusted_path2,
+ relative_to_dir, anchor,
+ scratch_pool, scratch_pool));
if (show_diff_header)
{
const char *label1;
const char *label2;
- const char *adjusted_path1 = apr_pstrdup(pool, path1);
- const char *adjusted_path2 = apr_pstrdup(pool, path2);
-
- SVN_ERR(adjust_paths_for_diff_labels(&path, &adjusted_path1,
- &adjusted_path2,
- relative_to_dir, pool));
- label1 = diff_label(adjusted_path1, rev1, pool);
- label2 = diff_label(adjusted_path2, rev2, pool);
+ label1 = diff_label(adjusted_path1, rev1, scratch_pool);
+ label2 = diff_label(adjusted_path2, rev2, scratch_pool);
/* ### Should we show the paths in platform specific format,
* ### diff_content_changed() does not! */
- SVN_ERR(file_printf_from_utf8(file, encoding,
- "Index: %s" APR_EOL_STR
- "%s" APR_EOL_STR,
- path, equal_string));
+ SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, scratch_pool,
+ "Index: %s" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
if (use_git_diff_format)
- {
- svn_stream_t *os;
-
- os = svn_stream_from_aprfile2(file, TRUE, pool);
- SVN_ERR(print_git_diff_header(os, &label1, &label2,
- svn_diff_op_modified,
- path1, path2, rev1, rev2, NULL,
- encoding, ra_session, wc_ctx,
- wc_root_abspath, pool));
- SVN_ERR(svn_stream_close(os));
- }
-
- SVN_ERR(file_printf_from_utf8(file, encoding,
- "--- %s" APR_EOL_STR
- "+++ %s" APR_EOL_STR,
- label1,
- label2));
+ SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+ svn_diff_op_modified,
+ repos_relpath1, repos_relpath2,
+ rev1, rev2, NULL,
+ SVN_INVALID_REVNUM,
+ encoding, scratch_pool));
+
+ /* --- label1
+ * +++ label2 */
+ SVN_ERR(svn_diff__unidiff_write_header(
+ outstream, encoding, label1, label2, scratch_pool));
}
- SVN_ERR(file_printf_from_utf8(file, encoding,
- _("%sProperty changes on: %s%s"),
- APR_EOL_STR,
- use_git_diff_format ? path1 : path,
- APR_EOL_STR));
-
- SVN_ERR(file_printf_from_utf8(file, encoding, "%s" APR_EOL_STR,
- under_string));
-
- for (i = 0; i < propchanges->nelts; i++)
- {
- const char *action;
- const svn_string_t *original_value;
- const svn_prop_t *propchange =
- &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
-
- if (original_props)
- original_value = apr_hash_get(original_props,
- propchange->name, APR_HASH_KEY_STRING);
- else
- original_value = NULL;
-
- /* If the property doesn't exist on either side, or if it exists
- with the same value, skip it. */
- if ((! (original_value || propchange->value))
- || (original_value && propchange->value
- && svn_string_compare(original_value, propchange->value)))
- continue;
-
- if (! original_value)
- action = "Added";
- else if (! propchange->value)
- action = "Deleted";
- else
- action = "Modified";
- SVN_ERR(file_printf_from_utf8(file, encoding, "%s: %s%s", action,
- propchange->name, APR_EOL_STR));
+ SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, scratch_pool,
+ _("%sProperty changes on: %s%s"),
+ APR_EOL_STR,
+ use_git_diff_format
+ ? repos_relpath1
+ : index_path,
+ APR_EOL_STR));
- if (strcmp(propchange->name, SVN_PROP_MERGEINFO) == 0)
- {
- const char *orig = original_value ? original_value->data : NULL;
- const char *val = propchange->value ? propchange->value->data : NULL;
- svn_error_t *err = display_mergeinfo_diff(orig, val, encoding,
- file, pool);
-
- /* Issue #3896: If we can't pretty-print mergeinfo differences
- because invalid mergeinfo is present, then don't let the diff
- fail, just print the diff as any other property. */
- if (err && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
- {
- svn_error_clear(err);
- }
- else
- {
- SVN_ERR(err);
- continue;
- }
- }
+ SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, scratch_pool,
+ SVN_DIFF__UNDER_STRING APR_EOL_STR));
- {
- svn_stream_t *os = svn_stream_from_aprfile2(file, TRUE, pool);
- svn_diff_t *diff;
- svn_diff_file_options_t options = { 0 };
- const svn_string_t *tmp;
- const svn_string_t *orig;
- const svn_string_t *val;
- svn_boolean_t val_has_eol;
-
- /* The last character in a property is often not a newline.
- An eol character is appended to prevent the diff API to add a
- ' \ No newline at end of file' line. We add
- ' \ No newline at end of property' manually if needed. */
- tmp = original_value ? original_value : svn_string_create("", pool);
- orig = maybe_append_eol(tmp, NULL, pool);
-
- tmp = propchange->value ? propchange->value :
- svn_string_create("", pool);
- val = maybe_append_eol(tmp, &val_has_eol, pool);
-
- SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options, pool));
-
- /* UNIX patch will try to apply a diff even if the diff header
- * is missing. It tries to be helpful by asking the user for a
- * target filename when it can't determine the target filename
- * from the diff header. But there usually are no files which
- * UNIX patch could apply the property diff to, so we use "##"
- * instead of "@@" as the default hunk delimiter for property diffs.
- * We also supress the diff header. */
- SVN_ERR(svn_diff_mem_string_output_unified2(os, diff, FALSE, "##",
- svn_dirent_local_style(path, pool),
- svn_dirent_local_style(path, pool),
- encoding, orig, val, pool));
- SVN_ERR(svn_stream_close(os));
- if (!val_has_eol)
- {
- const char *s = "\\ No newline at end of property" APR_EOL_STR;
- apr_size_t len = strlen(s);
- SVN_ERR(svn_stream_write(os, s, &len));
- }
- }
- }
+ SVN_ERR(svn_diff__display_prop_diffs(
+ outstream, encoding, propchanges, original_props,
+ TRUE /* pretty_print_mergeinfo */, scratch_pool));
return SVN_NO_ERROR;
}
@@ -756,8 +551,8 @@ struct diff_cmd_baton {
} options;
apr_pool_t *pool;
- apr_file_t *outfile;
- apr_file_t *errfile;
+ svn_stream_t *outstream;
+ svn_stream_t *errstream;
const char *header_encoding;
@@ -769,7 +564,7 @@ struct diff_cmd_baton {
const char *orig_path_2;
/* These are the numeric representations of the revisions passed to
- svn_client_diff5, either may be SVN_INVALID_REVNUM. We need these
+ svn_client_diff6(), either may be SVN_INVALID_REVNUM. We need these
because some of the svn_wc_diff_callbacks4_t don't get revision
arguments.
@@ -782,102 +577,87 @@ struct diff_cmd_baton {
/* Set this if you want diff output even for binary files. */
svn_boolean_t force_binary;
- /* Set this flag if you want diff_file_changed to output diffs
- unconditionally, even if the diffs are empty. */
- svn_boolean_t force_empty;
-
/* The directory that diff target paths should be considered as
relative to for output generation (see issue #2723). */
const char *relative_to_dir;
+ /* Whether property differences are ignored. */
+ svn_boolean_t ignore_properties;
+
+ /* Whether to show only property changes. */
+ svn_boolean_t properties_only;
+
/* Whether we're producing a git-style diff. */
svn_boolean_t use_git_diff_format;
+ /* Whether addition of a file is summarized versus showing a full diff. */
+ svn_boolean_t no_diff_added;
+
+ /* Whether deletion of a file is summarized versus showing a full diff. */
+ svn_boolean_t no_diff_deleted;
+
+ /* Whether to ignore copyfrom information when showing adds */
+ svn_boolean_t no_copyfrom_on_add;
+
+ /* Empty files for creating diffs or NULL if not used yet */
+ const char *empty_file;
+
svn_wc_context_t *wc_ctx;
/* The RA session used during diffs involving the repository. */
svn_ra_session_t *ra_session;
- /* During a repos-wc diff, this is the absolute path to the root
- * directory of the working copy involved in the diff. */
- const char *wc_root_abspath;
-
/* The anchor to prefix before wc paths */
const char *anchor;
- /* A hashtable using the visited paths as keys.
- * ### This is needed for us to know if we need to print a diff header for
- * ### a path that has property changes. */
- apr_hash_t *visited_paths;
+ /* Whether the local diff target of a repos->wc diff is a copy. */
+ svn_boolean_t repos_wc_diff_target_is_copy;
};
-
-/* A helper function that marks a path as visited. It copies PATH
- * into the correct pool before referencing it from the hash table. */
-static void
-mark_path_as_visited(struct diff_cmd_baton *diff_cmd_baton, const char *path)
-{
- const char *p;
-
- p = apr_pstrdup(apr_hash_pool_get(diff_cmd_baton->visited_paths), path);
- apr_hash_set(diff_cmd_baton->visited_paths, p, APR_HASH_KEY_STRING, p);
-}
-
/* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
*/
static svn_error_t *
-diff_props_changed(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *path,
+diff_props_changed(const char *diff_relpath,
+ svn_revnum_t rev1,
+ svn_revnum_t rev2,
svn_boolean_t dir_was_added,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
- void *diff_baton,
+ svn_boolean_t show_diff_header,
+ struct diff_cmd_baton *diff_cmd_baton,
apr_pool_t *scratch_pool)
{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
apr_array_header_t *props;
- svn_boolean_t show_diff_header;
+
+ /* If property differences are ignored, there's nothing to do. */
+ if (diff_cmd_baton->ignore_properties)
+ return SVN_NO_ERROR;
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
scratch_pool));
- if (apr_hash_get(diff_cmd_baton->visited_paths, path, APR_HASH_KEY_STRING))
- show_diff_header = FALSE;
- else
- show_diff_header = TRUE;
-
if (props->nelts > 0)
{
/* We're using the revnums from the diff_cmd_baton since there's
* no revision argument to the svn_wc_diff_callback_t
* dir_props_changed(). */
- SVN_ERR(display_prop_diffs(props, original_props, path,
+ SVN_ERR(display_prop_diffs(props, original_props,
+ diff_relpath,
+ diff_cmd_baton->anchor,
diff_cmd_baton->orig_path_1,
diff_cmd_baton->orig_path_2,
- diff_cmd_baton->revnum1,
- diff_cmd_baton->revnum2,
+ rev1,
+ rev2,
diff_cmd_baton->header_encoding,
- diff_cmd_baton->outfile,
+ diff_cmd_baton->outstream,
diff_cmd_baton->relative_to_dir,
show_diff_header,
diff_cmd_baton->use_git_diff_format,
diff_cmd_baton->ra_session,
diff_cmd_baton->wc_ctx,
- diff_cmd_baton->wc_root_abspath,
scratch_pool));
-
- /* We've printed the diff header so now we can mark the path as
- * visited. */
- if (show_diff_header)
- mark_path_as_visited(diff_cmd_baton, path);
}
- if (state)
- *state = svn_wc_notify_state_unknown;
- if (tree_conflicted)
- *tree_conflicted = FALSE;
-
return SVN_NO_ERROR;
}
@@ -885,7 +665,7 @@ diff_props_changed(svn_wc_notify_state_t *state,
static svn_error_t *
diff_dir_props_changed(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
- const char *path,
+ const char *diff_relpath,
svn_boolean_t dir_was_added,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
@@ -894,25 +674,33 @@ diff_dir_props_changed(svn_wc_notify_state_t *state,
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
-
- return svn_error_trace(diff_props_changed(state,
- tree_conflicted, path,
+ return svn_error_trace(diff_props_changed(diff_relpath,
+ /* ### These revs be filled
+ * ### with per node info */
+ dir_was_added
+ ? 0 /* Magic legacy value */
+ : diff_cmd_baton->revnum1,
+ diff_cmd_baton->revnum2,
dir_was_added,
propchanges,
original_props,
- diff_baton,
+ TRUE /* show_diff_header */,
+ diff_cmd_baton,
scratch_pool));
}
-/* Show differences between TMPFILE1 and TMPFILE2. PATH, REV1, and REV2 are
- used in the headers to indicate the file and revisions. If either
+/* Show differences between TMPFILE1 and TMPFILE2. DIFF_RELPATH, REV1, and
+ REV2 are used in the headers to indicate the file and revisions. If either
MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff,
- but instead print a warning message. */
+ but instead print a warning message.
+
+ If FORCE_DIFF is TRUE, always write a diff, even for empty diffs.
+
+ Set *WROTE_HEADER to TRUE if a diff header was written */
static svn_error_t *
-diff_content_changed(const char *path,
+diff_content_changed(svn_boolean_t *wrote_header,
+ const char *diff_relpath,
const char *tmpfile1,
const char *tmpfile2,
svn_revnum_t rev1,
@@ -920,32 +708,33 @@ diff_content_changed(const char *path,
const char *mimetype1,
const char *mimetype2,
svn_diff_operation_kind_t operation,
+ svn_boolean_t force_diff,
const char *copyfrom_path,
- void *diff_baton)
+ svn_revnum_t copyfrom_rev,
+ struct diff_cmd_baton *diff_cmd_baton,
+ apr_pool_t *scratch_pool)
{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
int exitcode;
- apr_pool_t *subpool = svn_pool_create(diff_cmd_baton->pool);
- svn_stream_t *os;
const char *rel_to_dir = diff_cmd_baton->relative_to_dir;
- apr_file_t *errfile = diff_cmd_baton->errfile;
+ svn_stream_t *errstream = diff_cmd_baton->errstream;
+ svn_stream_t *outstream = diff_cmd_baton->outstream;
const char *label1, *label2;
svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE;
- const char *path1, *path2;
+ const char *index_path = diff_relpath;
+ const char *path1 = diff_cmd_baton->orig_path_1;
+ const char *path2 = diff_cmd_baton->orig_path_2;
- /* Get a stream from our output file. */
- os = svn_stream_from_aprfile2(diff_cmd_baton->outfile, TRUE, subpool);
+ /* If only property differences are shown, there's nothing to do. */
+ if (diff_cmd_baton->properties_only)
+ return SVN_NO_ERROR;
/* Generate the diff headers. */
+ SVN_ERR(adjust_paths_for_diff_labels(&index_path, &path1, &path2,
+ rel_to_dir, diff_cmd_baton->anchor,
+ scratch_pool, scratch_pool));
- path1 = apr_pstrdup(subpool, diff_cmd_baton->orig_path_1);
- path2 = apr_pstrdup(subpool, diff_cmd_baton->orig_path_2);
-
- SVN_ERR(adjust_paths_for_diff_labels(&path, &path1, &path2,
- rel_to_dir, subpool));
-
- label1 = diff_label(path1, rev1, subpool);
- label2 = diff_label(path2, rev2, subpool);
+ label1 = diff_label(path1, rev1, scratch_pool);
+ label2 = diff_label(path2, rev2, scratch_pool);
/* Possible easy-out: if either mime-type is binary and force was not
specified, don't attempt to generate a viewable diff at all.
@@ -958,66 +747,115 @@ diff_content_changed(const char *path,
if (! diff_cmd_baton->force_binary && (mt1_binary || mt2_binary))
{
/* Print out the diff header. */
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
- "Index: %s" APR_EOL_STR "%s" APR_EOL_STR, path, equal_string));
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
+ "Index: %s" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
/* ### Print git diff headers. */
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
_("Cannot display: file marked as a binary type.%s"),
APR_EOL_STR));
if (mt1_binary && !mt2_binary)
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
"svn:mime-type = %s" APR_EOL_STR, mimetype1));
else if (mt2_binary && !mt1_binary)
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
"svn:mime-type = %s" APR_EOL_STR, mimetype2));
else if (mt1_binary && mt2_binary)
{
if (strcmp(mimetype1, mimetype2) == 0)
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
"svn:mime-type = %s" APR_EOL_STR,
mimetype1));
else
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
"svn:mime-type = (%s, %s)" APR_EOL_STR,
mimetype1, mimetype2));
}
/* Exit early. */
- svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
if (diff_cmd_baton->diff_cmd)
{
+ apr_file_t *outfile;
+ apr_file_t *errfile;
+ const char *outfilename;
+ const char *errfilename;
+ svn_stream_t *stream;
+
/* Print out the diff header. */
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
- "Index: %s" APR_EOL_STR "%s" APR_EOL_STR, path, equal_string));
- /* Close the stream (flush) */
- SVN_ERR(svn_stream_close(os));
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
+ "Index: %s" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
/* ### Do we want to add git diff headers here too? I'd say no. The
* ### 'Index' and '===' line is something subversion has added. The rest
* ### is up to the external diff application. We may be dealing with
* ### a non-git compatible diff application.*/
+ /* We deal in streams, but svn_io_run_diff2() deals in file handles,
+ so we may need to make temporary files and then copy the contents
+ to our stream. */
+ outfile = svn_stream__aprfile(outstream);
+ if (outfile)
+ outfilename = NULL;
+ else
+ SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+
+ errfile = svn_stream__aprfile(errstream);
+ if (errfile)
+ errfilename = NULL;
+ else
+ SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+
SVN_ERR(svn_io_run_diff2(".",
diff_cmd_baton->options.for_external.argv,
diff_cmd_baton->options.for_external.argc,
label1, label2,
tmpfile1, tmpfile2,
- &exitcode, diff_cmd_baton->outfile, errfile,
- diff_cmd_baton->diff_cmd, subpool));
+ &exitcode, outfile, errfile,
+ diff_cmd_baton->diff_cmd, scratch_pool));
+
+ /* Now, open and copy our files to our output streams. */
+ if (outfilename)
+ {
+ SVN_ERR(svn_io_file_close(outfile, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&stream, outfilename,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(stream, svn_stream_disown(outstream,
+ scratch_pool),
+ NULL, NULL, scratch_pool));
+ }
+ if (errfilename)
+ {
+ SVN_ERR(svn_io_file_close(errfile, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&stream, errfilename,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(stream, svn_stream_disown(errstream,
+ scratch_pool),
+ NULL, NULL, scratch_pool));
+ }
+
+ /* We have a printed a diff for this path, mark it as visited. */
+ *wrote_header = TRUE;
}
else /* use libsvn_diff to generate the diff */
{
@@ -1025,68 +863,69 @@ diff_content_changed(const char *path,
SVN_ERR(svn_diff_file_diff_2(&diff, tmpfile1, tmpfile2,
diff_cmd_baton->options.for_internal,
- subpool));
+ scratch_pool));
- if (svn_diff_contains_diffs(diff) || diff_cmd_baton->force_empty ||
- diff_cmd_baton->use_git_diff_format)
+ if (force_diff
+ || diff_cmd_baton->use_git_diff_format
+ || svn_diff_contains_diffs(diff))
{
/* Print out the diff header. */
- SVN_ERR(svn_stream_printf_from_utf8
- (os, diff_cmd_baton->header_encoding, subpool,
- "Index: %s" APR_EOL_STR "%s" APR_EOL_STR,
- path, equal_string));
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
+ "Index: %s" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
if (diff_cmd_baton->use_git_diff_format)
{
- const char *tmp_path1, *tmp_path2;
- SVN_ERR(adjust_relative_to_repos_root(
- &tmp_path1, path, diff_cmd_baton->orig_path_1,
- diff_cmd_baton->ra_session, diff_cmd_baton->wc_ctx,
- diff_cmd_baton->wc_root_abspath, subpool));
- SVN_ERR(adjust_relative_to_repos_root(
- &tmp_path2, path, diff_cmd_baton->orig_path_2,
- diff_cmd_baton->ra_session, diff_cmd_baton->wc_ctx,
- diff_cmd_baton->wc_root_abspath, subpool));
- SVN_ERR(print_git_diff_header(os, &label1, &label2, operation,
- tmp_path1, tmp_path2, rev1, rev2,
+ const char *repos_relpath1;
+ const char *repos_relpath2;
+ SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
+ diff_cmd_baton->orig_path_1,
+ diff_cmd_baton->ra_session,
+ diff_cmd_baton->wc_ctx,
+ diff_cmd_baton->anchor,
+ scratch_pool, scratch_pool));
+ SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
+ diff_cmd_baton->orig_path_2,
+ diff_cmd_baton->ra_session,
+ diff_cmd_baton->wc_ctx,
+ diff_cmd_baton->anchor,
+ scratch_pool, scratch_pool));
+ SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+ operation,
+ repos_relpath1, repos_relpath2,
+ rev1, rev2,
copyfrom_path,
+ copyfrom_rev,
diff_cmd_baton->header_encoding,
- diff_cmd_baton->ra_session,
- diff_cmd_baton->wc_ctx,
- diff_cmd_baton->wc_root_abspath,
- subpool));
+ scratch_pool));
}
/* Output the actual diff */
- if (svn_diff_contains_diffs(diff) || diff_cmd_baton->force_empty)
- SVN_ERR(svn_diff_file_output_unified3
- (os, diff, tmpfile1, tmpfile2, label1, label2,
+ if (force_diff || svn_diff_contains_diffs(diff))
+ SVN_ERR(svn_diff_file_output_unified3(outstream, diff,
+ tmpfile1, tmpfile2, label1, label2,
diff_cmd_baton->header_encoding, rel_to_dir,
diff_cmd_baton->options.for_internal->show_c_function,
- subpool));
+ scratch_pool));
/* We have a printed a diff for this path, mark it as visited. */
- mark_path_as_visited(diff_cmd_baton, path);
+ *wrote_header = TRUE;
}
-
- /* Close the stream (flush) */
- SVN_ERR(svn_stream_close(os));
}
/* ### todo: someday we'll need to worry about whether we're going
to need to write a diff plug-in mechanism that makes use of the
two paths, instead of just blindly running SVN_CLIENT_DIFF. */
- /* Destroy the subpool. */
- svn_pool_destroy(subpool);
-
return SVN_NO_ERROR;
}
static svn_error_t *
diff_file_opened(svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
- const char *path,
+ const char *diff_relpath,
svn_revnum_t rev,
void *diff_baton,
apr_pool_t *scratch_pool)
@@ -1099,7 +938,7 @@ static svn_error_t *
diff_file_changed(svn_wc_notify_state_t *content_state,
svn_wc_notify_state_t *prop_state,
svn_boolean_t *tree_conflicted,
- const char *path,
+ const char *diff_relpath,
const char *tmpfile1,
const char *tmpfile2,
svn_revnum_t rev1,
@@ -1112,23 +951,33 @@ diff_file_changed(svn_wc_notify_state_t *content_state,
apr_pool_t *scratch_pool)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
+ svn_boolean_t wrote_header = FALSE;
+
+ /* During repos->wc diff of a copy revision numbers obtained
+ * from the working copy are always SVN_INVALID_REVNUM. */
+ if (diff_cmd_baton->repos_wc_diff_target_is_copy)
+ {
+ if (rev1 == SVN_INVALID_REVNUM &&
+ diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM)
+ rev1 = diff_cmd_baton->revnum1;
+
+ if (rev2 == SVN_INVALID_REVNUM &&
+ diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM)
+ rev2 = diff_cmd_baton->revnum2;
+ }
+
if (tmpfile1)
- SVN_ERR(diff_content_changed(path,
+ SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
tmpfile1, tmpfile2, rev1, rev2,
mimetype1, mimetype2,
- svn_diff_op_modified, NULL, diff_baton));
+ svn_diff_op_modified, FALSE,
+ NULL,
+ SVN_INVALID_REVNUM, diff_cmd_baton,
+ scratch_pool));
if (prop_changes->nelts > 0)
- SVN_ERR(diff_props_changed(prop_state, tree_conflicted,
- path, FALSE, prop_changes,
- original_props, diff_baton, scratch_pool));
- if (content_state)
- *content_state = svn_wc_notify_state_unknown;
- if (prop_state)
- *prop_state = svn_wc_notify_state_unknown;
- if (tree_conflicted)
- *tree_conflicted = FALSE;
+ SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, FALSE, prop_changes,
+ original_props, !wrote_header,
+ diff_cmd_baton, scratch_pool));
return SVN_NO_ERROR;
}
@@ -1141,7 +990,7 @@ static svn_error_t *
diff_file_added(svn_wc_notify_state_t *content_state,
svn_wc_notify_state_t *prop_state,
svn_boolean_t *tree_conflicted,
- const char *path,
+ const char *diff_relpath,
const char *tmpfile1,
const char *tmpfile2,
svn_revnum_t rev1,
@@ -1156,126 +1005,151 @@ diff_file_added(svn_wc_notify_state_t *content_state,
apr_pool_t *scratch_pool)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
+ svn_boolean_t wrote_header = FALSE;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
+ /* During repos->wc diff of a copy revision numbers obtained
+ * from the working copy are always SVN_INVALID_REVNUM. */
+ if (diff_cmd_baton->repos_wc_diff_target_is_copy)
+ {
+ if (rev1 == SVN_INVALID_REVNUM &&
+ diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM)
+ rev1 = diff_cmd_baton->revnum1;
+
+ if (rev2 == SVN_INVALID_REVNUM &&
+ diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM)
+ rev2 = diff_cmd_baton->revnum2;
+ }
- /* We want diff_file_changed to unconditionally show diffs, even if
- the diff is empty (as would be the case if an empty file were
- added.) It's important, because 'patch' would still see an empty
- diff and create an empty file. It's also important to let the
- user see that *something* happened. */
- diff_cmd_baton->force_empty = TRUE;
+ if (diff_cmd_baton->no_copyfrom_on_add
+ && (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision)))
+ {
+ apr_hash_t *empty_hash = apr_hash_make(scratch_pool);
+ apr_array_header_t *new_changes;
+
+ /* Rebase changes on having no left source. */
+ if (!diff_cmd_baton->empty_file)
+ SVN_ERR(svn_io_open_unique_file3(NULL, &diff_cmd_baton->empty_file,
+ NULL, svn_io_file_del_on_pool_cleanup,
+ diff_cmd_baton->pool, scratch_pool));
+
+ SVN_ERR(svn_prop_diffs(&new_changes,
+ svn_prop__patch(original_props, prop_changes,
+ scratch_pool),
+ empty_hash,
+ scratch_pool));
+
+ tmpfile1 = diff_cmd_baton->empty_file;
+ prop_changes = new_changes;
+ original_props = empty_hash;
+ copyfrom_revision = SVN_INVALID_REVNUM;
+ }
- if (tmpfile1 && copyfrom_path)
- SVN_ERR(diff_content_changed(path,
+ if (diff_cmd_baton->no_diff_added)
+ {
+ const char *index_path = diff_relpath;
+
+ if (diff_cmd_baton->anchor)
+ index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath,
+ scratch_pool);
+
+ SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
+ "Index: %s (added)" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
+ wrote_header = TRUE;
+ }
+ else if (tmpfile1 && copyfrom_path)
+ SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
tmpfile1, tmpfile2, rev1, rev2,
mimetype1, mimetype2,
- svn_diff_op_copied, copyfrom_path,
- diff_baton));
+ svn_diff_op_copied,
+ TRUE /* force diff output */,
+ copyfrom_path,
+ copyfrom_revision, diff_cmd_baton,
+ scratch_pool));
else if (tmpfile1)
- SVN_ERR(diff_content_changed(path,
+ SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
tmpfile1, tmpfile2, rev1, rev2,
mimetype1, mimetype2,
- svn_diff_op_added, NULL, diff_baton));
+ svn_diff_op_added,
+ TRUE /* force diff output */,
+ NULL, SVN_INVALID_REVNUM,
+ diff_cmd_baton, scratch_pool));
+
if (prop_changes->nelts > 0)
- SVN_ERR(diff_props_changed(prop_state, tree_conflicted,
- path, FALSE, prop_changes,
- original_props, diff_baton, scratch_pool));
- if (content_state)
- *content_state = svn_wc_notify_state_unknown;
- if (prop_state)
- *prop_state = svn_wc_notify_state_unknown;
- if (tree_conflicted)
- *tree_conflicted = FALSE;
-
- diff_cmd_baton->force_empty = FALSE;
+ SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2,
+ FALSE, prop_changes,
+ original_props, ! wrote_header,
+ diff_cmd_baton, scratch_pool));
return SVN_NO_ERROR;
}
/* An svn_wc_diff_callbacks4_t function. */
static svn_error_t *
-diff_file_deleted_with_diff(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *path,
- const char *tmpfile1,
- const char *tmpfile2,
- const char *mimetype1,
- const char *mimetype2,
- apr_hash_t *original_props,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_file_deleted(svn_wc_notify_state_t *state,
+ svn_boolean_t *tree_conflicted,
+ const char *diff_relpath,
+ const char *tmpfile1,
+ const char *tmpfile2,
+ const char *mimetype1,
+ const char *mimetype2,
+ apr_hash_t *original_props,
+ void *diff_baton,
+ apr_pool_t *scratch_pool)
{
struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
+ if (diff_cmd_baton->no_diff_deleted)
+ {
+ const char *index_path = diff_relpath;
- if (tmpfile1)
- SVN_ERR(diff_content_changed(path,
- tmpfile1, tmpfile2, diff_cmd_baton->revnum1,
- diff_cmd_baton->revnum2,
- mimetype1, mimetype2,
- svn_diff_op_deleted, NULL, diff_baton));
+ if (diff_cmd_baton->anchor)
+ index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath,
+ scratch_pool);
- /* We don't list all the deleted properties. */
+ SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream,
+ diff_cmd_baton->header_encoding, scratch_pool,
+ "Index: %s (deleted)" APR_EOL_STR
+ SVN_DIFF__EQUAL_STRING APR_EOL_STR,
+ index_path));
+ }
+ else
+ {
+ svn_boolean_t wrote_header = FALSE;
+ if (tmpfile1)
+ SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
+ tmpfile1, tmpfile2,
+ diff_cmd_baton->revnum1,
+ diff_cmd_baton->revnum2,
+ mimetype1, mimetype2,
+ svn_diff_op_deleted, FALSE,
+ NULL, SVN_INVALID_REVNUM,
+ diff_cmd_baton,
+ scratch_pool));
+
+ /* Should we also report the properties as deleted? */
+ }
- if (state)
- *state = svn_wc_notify_state_unknown;
- if (tree_conflicted)
- *tree_conflicted = FALSE;
+ /* We don't list all the deleted properties. */
return SVN_NO_ERROR;
}
/* An svn_wc_diff_callbacks4_t function. */
static svn_error_t *
-diff_file_deleted_no_diff(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *path,
- const char *tmpfile1,
- const char *tmpfile2,
- const char *mimetype1,
- const char *mimetype2,
- apr_hash_t *original_props,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
-
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
-
- if (state)
- *state = svn_wc_notify_state_unknown;
- if (tree_conflicted)
- *tree_conflicted = FALSE;
-
- return file_printf_from_utf8
- (diff_cmd_baton->outfile,
- diff_cmd_baton->header_encoding,
- "Index: %s (deleted)" APR_EOL_STR "%s" APR_EOL_STR,
- path, equal_string);
-}
-
-/* An svn_wc_diff_callbacks4_t function. */
-static svn_error_t *
diff_dir_added(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
- const char *path,
+ const char *diff_relpath,
svn_revnum_t rev,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
void *diff_baton,
apr_pool_t *scratch_pool)
{
- /*struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);*/
-
/* Do nothing. */
return SVN_NO_ERROR;
@@ -1285,14 +1159,10 @@ diff_dir_added(svn_wc_notify_state_t *state,
static svn_error_t *
diff_dir_deleted(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
- const char *path,
+ const char *diff_relpath,
void *diff_baton,
apr_pool_t *scratch_pool)
{
- /*struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);*/
-
/* Do nothing. */
return SVN_NO_ERROR;
@@ -1303,15 +1173,11 @@ static svn_error_t *
diff_dir_opened(svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
- const char *path,
+ const char *diff_relpath,
svn_revnum_t rev,
void *diff_baton,
apr_pool_t *scratch_pool)
{
- /*struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);*/
-
/* Do nothing. */
return SVN_NO_ERROR;
@@ -1322,20 +1188,28 @@ static svn_error_t *
diff_dir_closed(svn_wc_notify_state_t *contentstate,
svn_wc_notify_state_t *propstate,
svn_boolean_t *tree_conflicted,
- const char *path,
+ const char *diff_relpath,
svn_boolean_t dir_was_added,
void *diff_baton,
apr_pool_t *scratch_pool)
{
- /*struct diff_cmd_baton *diff_cmd_baton = diff_baton;
- if (diff_cmd_baton->anchor)
- path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);*/
-
/* Do nothing. */
return SVN_NO_ERROR;
}
+static const svn_wc_diff_callbacks4_t diff_callbacks =
+{
+ diff_file_opened,
+ diff_file_changed,
+ diff_file_added,
+ diff_file_deleted,
+ diff_dir_deleted,
+ diff_dir_opened,
+ diff_dir_added,
+ diff_dir_props_changed,
+ diff_dir_closed
+};
/*-----------------------------------------------------------------*/
@@ -1347,14 +1221,14 @@ diff_dir_closed(svn_wc_notify_state_t *contentstate,
this knowledge has been grokked yet.
There are five cases:
- 1. path is not an URL and start_revision != end_revision
- 2. path is not an URL and start_revision == end_revision
- 3. path is an URL and start_revision != end_revision
- 4. path is an URL and start_revision == end_revision
- 5. path is not an URL and no revisions given
+ 1. path is not a URL and start_revision != end_revision
+ 2. path is not a URL and start_revision == end_revision
+ 3. path is a URL and start_revision != end_revision
+ 4. path is a URL and start_revision == end_revision
+ 5. path is not a URL and no revisions given
With only one distinct revision the working copy provides the
- other. When path is an URL there is no working copy. Thus
+ other. When path is a URL there is no working copy. Thus
1: compare repository versions for URL coresponding to working copy
2: compare working copy against repository version
@@ -1366,47 +1240,18 @@ diff_dir_closed(svn_wc_notify_state_t *contentstate,
the user specifies two dates that resolve to the same revision. */
-
-
-/* Helper function: given a working-copy ABSPATH_OR_URL, return its
- associated url in *URL, allocated in RESULT_POOL. If ABSPATH_OR_URL is
- *already* a URL, that's fine, return ABSPATH_OR_URL allocated in
- RESULT_POOL.
-
- Use SCRATCH_POOL for temporary allocations. */
-static svn_error_t *
-convert_to_url(const char **url,
- svn_wc_context_t *wc_ctx,
- const char *abspath_or_url,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- if (svn_path_is_url(abspath_or_url))
- {
- *url = apr_pstrdup(result_pool, abspath_or_url);
- return SVN_NO_ERROR;
- }
-
- SVN_ERR(svn_wc__node_get_url(url, wc_ctx, abspath_or_url,
- result_pool, scratch_pool));
- if (! *url)
- return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
- _("Path '%s' has no URL"),
- svn_dirent_local_style(abspath_or_url,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
-/** Check if paths PATH1 and PATH2 are urls and if the revisions REVISION1
- * and REVISION2 are local. If PEG_REVISION is not unspecified, ensure that
- * at least one of the two revisions is non-local.
- * If PATH1 can only be found in the repository, set *IS_REPOS1 to TRUE.
- * If PATH2 can only be found in the repository, set *IS_REPOS2 to TRUE. */
+/** Check if paths PATH_OR_URL1 and PATH_OR_URL2 are urls and if the
+ * revisions REVISION1 and REVISION2 are local. If PEG_REVISION is not
+ * unspecified, ensure that at least one of the two revisions is not
+ * BASE or WORKING.
+ * If PATH_OR_URL1 can only be found in the repository, set *IS_REPOS1
+ * to TRUE. If PATH_OR_URL2 can only be found in the repository, set
+ * *IS_REPOS2 to TRUE. */
static svn_error_t *
check_paths(svn_boolean_t *is_repos1,
svn_boolean_t *is_repos2,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision)
@@ -1419,8 +1264,8 @@ check_paths(svn_boolean_t *is_repos1,
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
- /* Revisions can be said to be local or remote. BASE and WORKING,
- for example, are local. */
+ /* Revisions can be said to be local or remote.
+ * BASE and WORKING are local revisions. */
is_local_rev1 =
((revision1->kind == svn_opt_revision_base)
|| (revision1->kind == svn_opt_revision_working));
@@ -1428,25 +1273,18 @@ check_paths(svn_boolean_t *is_repos1,
((revision2->kind == svn_opt_revision_base)
|| (revision2->kind == svn_opt_revision_working));
- if (peg_revision->kind != svn_opt_revision_unspecified)
- {
- if (is_local_rev1 && is_local_rev2)
- return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
- _("At least one revision must be non-local "
- "for a pegged diff"));
+ if (peg_revision->kind != svn_opt_revision_unspecified &&
+ is_local_rev1 && is_local_rev2)
+ return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+ _("At least one revision must be something other "
+ "than BASE or WORKING when diffing a URL"));
- *is_repos1 = ! is_local_rev1 || svn_path_is_url(path1);
- *is_repos2 = ! is_local_rev2 || svn_path_is_url(path2);
- }
- else
- {
- /* Working copy paths with non-local revisions get turned into
- URLs. We don't do that here, though. We simply record that it
- needs to be done, which is information that helps us choose our
- diff helper function. */
- *is_repos1 = ! is_local_rev1 || svn_path_is_url(path1);
- *is_repos2 = ! is_local_rev2 || svn_path_is_url(path2);
- }
+ /* Working copy paths with non-local revisions get turned into
+ URLs. We don't do that here, though. We simply record that it
+ needs to be done, which is information that helps us choose our
+ diff helper function. */
+ *is_repos1 = ! is_local_rev1 || svn_path_is_url(path_or_url1);
+ *is_repos2 = ! is_local_rev2 || svn_path_is_url(path_or_url2);
return SVN_NO_ERROR;
}
@@ -1491,11 +1329,52 @@ check_diff_target_exists(const char *url,
return SVN_NO_ERROR;
}
-/** Prepare a repos repos diff between PATH1 and PATH2@PEG_REVISION,
- * in the revision range REVISION1:REVISION2.
+
+/* Return in *RESOLVED_URL the URL which PATH_OR_URL@PEG_REVISION has in
+ * REVISION. If the object has no location in REVISION, set *RESOLVED_URL
+ * to NULL. */
+static svn_error_t *
+resolve_pegged_diff_target_url(const char **resolved_url,
+ svn_ra_session_t *ra_session,
+ const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ /* Check if the PATH_OR_URL exists at REVISION. */
+ err = svn_client__repos_locations(resolved_url, NULL,
+ NULL, NULL,
+ ra_session,
+ path_or_url,
+ peg_revision,
+ revision,
+ NULL,
+ ctx, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES ||
+ err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ *resolved_url = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/** Prepare a repos repos diff between PATH_OR_URL1 and
+ * PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2.
* Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2.
* Return suitable anchors in *ANCHOR1 and *ANCHOR2, and targets in
* *TARGET1 and *TARGET2, based on *URL1 and *URL2.
+ * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify
+ * that at least one of the diff targets exists.
* Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION
* which is pointing at *ANCHOR1.
* Use client context CTX. Do all allocations in POOL. */
@@ -1509,101 +1388,108 @@ diff_prepare_repos_repos(const char **url1,
const char **anchor2,
const char **target1,
const char **target2,
+ svn_node_kind_t *kind1,
+ svn_node_kind_t *kind2,
svn_ra_session_t **ra_session,
svn_client_ctx_t *ctx,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision,
apr_pool_t *pool)
{
- svn_node_kind_t kind1, kind2;
- const char *path2_abspath;
- const char *path1_abspath;
+ const char *abspath_or_url2;
+ const char *abspath_or_url1;
+ const char *repos_root_url;
+ const char *wri_abspath = NULL;
- if (!svn_path_is_url(path2))
- SVN_ERR(svn_dirent_get_absolute(&path2_abspath, path2,
- pool));
+ if (!svn_path_is_url(path_or_url2))
+ {
+ SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2, pool));
+ SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, abspath_or_url2,
+ pool, pool));
+ wri_abspath = abspath_or_url2;
+ }
else
- path2_abspath = path2;
+ *url2 = abspath_or_url2 = apr_pstrdup(pool, path_or_url2);
- if (!svn_path_is_url(path1))
- SVN_ERR(svn_dirent_get_absolute(&path1_abspath, path1,
- pool));
+ if (!svn_path_is_url(path_or_url1))
+ {
+ SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
+ SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, abspath_or_url1,
+ pool, pool));
+ wri_abspath = abspath_or_url1;
+ }
else
- path1_abspath = path1;
-
- /* Figure out URL1 and URL2. */
- SVN_ERR(convert_to_url(url1, ctx->wc_ctx, path1_abspath,
- pool, pool));
- SVN_ERR(convert_to_url(url2, ctx->wc_ctx, path2_abspath,
- pool, pool));
+ *url1 = abspath_or_url1 = apr_pstrdup(pool, path_or_url1);
/* We need exactly one BASE_PATH, so we'll let the BASE_PATH
- calculated for PATH2 override the one for PATH1 (since the diff
- will be "applied" to URL2 anyway). */
+ calculated for PATH_OR_URL2 override the one for PATH_OR_URL1
+ (since the diff will be "applied" to URL2 anyway). */
*base_path = NULL;
- if (strcmp(*url1, path1) != 0)
- *base_path = path1;
- if (strcmp(*url2, path2) != 0)
- *base_path = path2;
+ if (strcmp(*url1, path_or_url1) != 0)
+ *base_path = path_or_url1;
+ if (strcmp(*url2, path_or_url2) != 0)
+ *base_path = path_or_url2;
- SVN_ERR(svn_client__open_ra_session_internal(ra_session, NULL, *url2,
- NULL, NULL, FALSE,
- TRUE, ctx, pool));
+ SVN_ERR(svn_client_open_ra_session2(ra_session, *url2, wri_abspath,
+ ctx, pool, pool));
/* If we are performing a pegged diff, we need to find out what our
actual URLs will be. */
if (peg_revision->kind != svn_opt_revision_unspecified)
{
- svn_opt_revision_t *start_ignore, *end_ignore;
- svn_error_t *err;
-
- err = svn_client__repos_locations(url1, &start_ignore,
- url2, &end_ignore,
- *ra_session,
- path2,
- peg_revision,
- revision1,
- revision2,
- ctx, pool);
- if (err)
+ const char *resolved_url1;
+ const char *resolved_url2;
+
+ SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session,
+ path_or_url2, peg_revision,
+ revision2, ctx, pool));
+
+ SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
+ SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session,
+ path_or_url1, peg_revision,
+ revision1, ctx, pool));
+
+ /* Either or both URLs might have changed as a result of resolving
+ * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs
+ * could be resolved, use the same URL for URL1 and URL2, so we can
+ * show a diff that adds or removes the object (see issue #4153). */
+ if (resolved_url2)
{
- if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
- {
- /* Don't give up just yet. A missing path might translate
- * into an addition in the diff. Below, we verify that each
- * URL exists on at least one side of the diff. */
- svn_error_clear(err);
- }
- else
- return svn_error_trace(err);
+ *url2 = resolved_url2;
+ if (!resolved_url1)
+ *url1 = resolved_url2;
}
- else
+ if (resolved_url1)
{
- /* Reparent the session, since *URL2 might have changed as a result
- the above call. */
- SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
+ *url1 = resolved_url1;
+ if (!resolved_url2)
+ *url2 = resolved_url1;
}
+
+ /* Reparent the session, since *URL2 might have changed as a result
+ the above call. */
+ SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
}
/* Resolve revision and get path kind for the second target. */
SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx,
- (path2 == *url2) ? NULL : path2_abspath,
+ (path_or_url2 == *url2) ? NULL : abspath_or_url2,
*ra_session, revision2, pool));
- SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, &kind2, pool));
+ SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool));
/* Do the same for the first target. */
SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx,
- (strcmp(path1, *url1) == 0) ? NULL : path1_abspath,
+ (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1,
*ra_session, revision1, pool));
- SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, &kind1, pool));
+ SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool));
/* Either both URLs must exist at their respective revisions,
* or one of them may be missing from one side of the diff. */
- if (kind1 == svn_node_none && kind2 == svn_node_none)
+ if (*kind1 == svn_node_none && *kind2 == svn_node_none)
{
if (strcmp(*url1, *url2) == 0)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
@@ -1612,76 +1498,33 @@ diff_prepare_repos_repos(const char **url1,
*url1, *rev1, *rev2);
else
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
- _("Diff targets '%s and '%s' were not found "
+ _("Diff targets '%s' and '%s' were not found "
"in the repository at revisions '%ld' and "
"'%ld'"),
*url1, *url2, *rev1, *rev2);
}
- else if (kind1 == svn_node_none)
+ else if (*kind1 == svn_node_none)
SVN_ERR(check_diff_target_exists(*url1, *rev2, *rev1, *ra_session, pool));
- else if (kind2 == svn_node_none)
+ else if (*kind2 == svn_node_none)
SVN_ERR(check_diff_target_exists(*url2, *rev1, *rev2, *ra_session, pool));
+ SVN_ERR(svn_ra_get_repos_root2(*ra_session, &repos_root_url, pool));
+
/* Choose useful anchors and targets for our two URLs. */
*anchor1 = *url1;
*anchor2 = *url2;
*target1 = "";
*target2 = "";
- if ((kind1 == svn_node_none) || (kind2 == svn_node_none))
- {
- svn_node_kind_t kind;
- const char *repos_root;
- const char *new_anchor;
- svn_revnum_t rev;
-
- /* The diff target does not exist on one side of the diff.
- * This can happen if the target was added or deleted within the
- * revision range being diffed.
- * However, we don't know how deep within a added/deleted subtree the
- * diff target is. Find a common parent that exists on both sides of
- * the diff and use it as anchor for the diff operation.
- *
- * ### This can fail due to authz restrictions (like in issue #3242).
- * ### But it is the only option we have right now to try to get
- * ### a usable diff in this situation. */
-
- SVN_ERR(svn_ra_get_repos_root2(*ra_session, &repos_root, pool));
-
- /* Since we already know that one of the URLs does exist,
- * look for an existing parent of the URL which doesn't exist. */
- new_anchor = (kind1 == svn_node_none ? *anchor1 : *anchor2);
- rev = (kind1 == svn_node_none ? *rev1 : *rev2);
- do
- {
- if (strcmp(new_anchor, repos_root) != 0)
- {
- new_anchor = svn_path_uri_decode(svn_uri_dirname(new_anchor,
- pool),
- pool);
- if (*base_path)
- *base_path = svn_dirent_dirname(*base_path, pool);
- }
- SVN_ERR(svn_ra_reparent(*ra_session, new_anchor, pool));
- SVN_ERR(svn_ra_check_path(*ra_session, "", rev, &kind, pool));
-
- }
- while (kind != svn_node_dir);
- *anchor1 = *anchor2 = new_anchor;
- /* Diff targets must be relative to the new anchor. */
- *target1 = svn_uri_skip_ancestor(new_anchor, *url1, pool);
- *target2 = svn_uri_skip_ancestor(new_anchor, *url2, pool);
- SVN_ERR_ASSERT(*target1 && *target2);
- if (kind1 == svn_node_none)
- kind1 = svn_node_dir;
- else
- kind2 = svn_node_dir;
- }
- else if ((kind1 == svn_node_file) || (kind2 == svn_node_file))
+ /* If none of the targets is the repository root open the parent directory
+ to allow describing replacement of the target itself */
+ if (strcmp(*url1, repos_root_url) != 0
+ && strcmp(*url2, repos_root_url) != 0)
{
svn_uri_split(anchor1, target1, *url1, pool);
svn_uri_split(anchor2, target2, *url2, pool);
- if (*base_path)
+ if (*base_path
+ && (*kind1 == svn_node_file || *kind2 == svn_node_file))
*base_path = svn_dirent_dirname(*base_path, pool);
SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool));
}
@@ -1691,8 +1534,8 @@ diff_prepare_repos_repos(const char **url1,
/* A Theoretical Note From Ben, regarding do_diff().
- This function is really svn_client_diff5(). If you read the public
- API description for svn_client_diff5(), it sounds quite Grand. It
+ This function is really svn_client_diff6(). If you read the public
+ API description for svn_client_diff6(), it sounds quite Grand. It
sounds really generalized and abstract and beautiful: that it will
diff any two paths, be they working-copy paths or URLs, at any two
revisions.
@@ -1708,11 +1551,15 @@ diff_prepare_repos_repos(const char **url1,
- svn_client__get_diff_editor: compares some URL1@REV1 vs. URL2@REV2
+ Since Subversion 1.8 we also have a variant of svn_wc_diff called
+ svn_client__arbitrary_nodes_diff, that allows handling WORKING-WORKING
+ comparisions between nodes in the working copy.
+
So the truth of the matter is, if the caller's arguments can't be
- pigeonholed into one of these three use-cases, we currently bail
- with a friendly apology.
+ pigeonholed into one of these use-cases, we currently bail with a
+ friendly apology.
- Perhaps someday a brave soul will truly make svn_client_diff5
+ Perhaps someday a brave soul will truly make svn_client_diff6()
perfectly general. For now, we live with the 90% case. Certainly,
the commandline client only calls this function in legal ways.
When there are other users of svn_client.h, maybe this will become
@@ -1725,17 +1572,16 @@ static svn_error_t *
unsupported_diff_error(svn_error_t *child_err)
{
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, child_err,
- _("Sorry, svn_client_diff5 was called in a way "
+ _("Sorry, svn_client_diff6 was called in a way "
"that is not yet supported"));
}
-
/* Perform a diff between two working-copy paths.
PATH1 and PATH2 are both working copy paths. REVISION1 and
REVISION2 are their respective revisions.
- All other options are the same as those passed to svn_client_diff5(). */
+ All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
diff_wc_wc(const char *path1,
const svn_opt_revision_t *revision1,
@@ -1765,11 +1611,12 @@ diff_wc_wc(const char *path1,
if ((strcmp(path1, path2) != 0)
|| (! ((revision1->kind == svn_opt_revision_base)
&& (revision2->kind == svn_opt_revision_working))))
- return unsupported_diff_error
- (svn_error_create
- (SVN_ERR_INCORRECT_PARAMS, NULL,
- _("Only diffs between a path's text-base "
- "and its working files are supported at this time")));
+ return unsupported_diff_error(
+ svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Only diffs between a path's text-base "
+ "and its working files are supported at this time"
+ )));
+
/* Resolve named revisions to real numbers. */
err = svn_client__get_revision_number(&callback_baton->revnum1, NULL,
@@ -1790,7 +1637,8 @@ diff_wc_wc(const char *path1,
callback_baton->revnum2 = SVN_INVALID_REVNUM; /* WC */
- SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, abspath1, FALSE, pool));
+ SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1,
+ TRUE, FALSE, pool));
if (kind != svn_node_dir)
callback_baton->anchor = svn_dirent_dirname(path1, pool);
@@ -1808,22 +1656,21 @@ diff_wc_wc(const char *path1,
return SVN_NO_ERROR;
}
-
/* Perform a diff between two repository paths.
- PATH1 and PATH2 may be either URLs or the working copy paths.
+ PATH_OR_URL1 and PATH_OR_URL2 may be either URLs or the working copy paths.
REVISION1 and REVISION2 are their respective revisions.
- If PEG_REVISION is specified, PATH2 is the path at the peg revision,
+ If PEG_REVISION is specified, PATH_OR_URL2 is the path at the peg revision,
and the actual two paths compared are determined by following copy
- history from PATH2.
+ history from PATH_OR_URL2.
- All other options are the same as those passed to svn_client_diff5(). */
+ All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
struct diff_cmd_baton *callback_baton,
svn_client_ctx_t *ctx,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision,
@@ -1839,24 +1686,44 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
+ const svn_diff_tree_processor_t *diff_processor;
+
const char *url1;
const char *url2;
const char *base_path;
svn_revnum_t rev1;
svn_revnum_t rev2;
+ svn_node_kind_t kind1;
+ svn_node_kind_t kind2;
const char *anchor1;
const char *anchor2;
const char *target1;
const char *target2;
svn_ra_session_t *ra_session;
+ const char *wri_abspath = NULL;
/* Prepare info for the repos repos diff. */
SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
&anchor1, &anchor2, &target1, &target2,
- &ra_session, ctx, path1, path2,
+ &kind1, &kind2, &ra_session,
+ ctx, path_or_url1, path_or_url2,
revision1, revision2, peg_revision,
pool));
+ /* Find a WC path for the ra session */
+ if (!svn_path_is_url(path_or_url1))
+ SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url1, pool));
+ else if (!svn_path_is_url(path_or_url2))
+ SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url2, pool));
+
+ /* Set up the repos_diff editor on BASE_PATH, if available.
+ Otherwise, we just use "". */
+
+ SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+ callbacks, callback_baton,
+ TRUE /* walk_deleted_dirs */,
+ pool, pool));
+
/* Get actual URLs. */
callback_baton->orig_path_1 = url1;
callback_baton->orig_path_2 = url2;
@@ -1868,51 +1735,83 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
callback_baton->ra_session = ra_session;
callback_baton->anchor = base_path;
+ /* The repository can bring in a new working copy, but not delete
+ everything. Luckily our new diff handler can just be reversed. */
+ if (kind2 == svn_node_none)
+ {
+ const char *str_tmp;
+ svn_revnum_t rev_tmp;
+
+ str_tmp = url2;
+ url2 = url1;
+ url1 = str_tmp;
+
+ rev_tmp = rev2;
+ rev2 = rev1;
+ rev1 = rev_tmp;
+
+ str_tmp = anchor2;
+ anchor2 = anchor1;
+ anchor1 = str_tmp;
+
+ str_tmp = target2;
+ target2 = target1;
+ target1 = str_tmp;
+
+ diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
+ NULL, pool);
+ }
+
+ /* Filter the first path component using a filter processor, until we fixed
+ the diff processing to handle this directly */
+ if ((kind1 != svn_node_file && kind2 != svn_node_file) && target1[0] != '\0')
+ {
+ diff_processor = svn_diff__tree_processor_filter_create(diff_processor,
+ target1, pool);
+ }
+
/* Now, we open an extra RA session to the correct anchor
location for URL1. This is used during the editor calls to fetch file
contents. */
- SVN_ERR(svn_client__open_ra_session_internal(&extra_ra_session, NULL,
- anchor1, NULL, NULL, FALSE,
- TRUE, ctx, pool));
+ SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, wri_abspath,
+ ctx, pool, pool));
- /* Set up the repos_diff editor on BASE_PATH, if available.
- Otherwise, we just use "". */
- SVN_ERR(svn_client__get_diff_editor(
+ SVN_ERR(svn_client__get_diff_editor2(
&diff_editor, &diff_edit_baton,
- NULL, "", depth,
- extra_ra_session, rev1, TRUE, FALSE,
- callbacks, callback_baton,
+ extra_ra_session, depth,
+ rev1,
+ TRUE /* text_deltas */,
+ diff_processor,
ctx->cancel_func, ctx->cancel_baton,
- NULL /* no notify_func */, NULL /* no notify_baton */,
- pool, pool));
+ pool));
/* We want to switch our txn into URL2 */
- SVN_ERR(svn_ra_do_diff3
- (ra_session, &reporter, &reporter_baton, rev2, target1,
- depth, ignore_ancestry, TRUE,
- url2, diff_editor, diff_edit_baton, pool));
+ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton,
+ rev2, target1,
+ depth, ignore_ancestry, TRUE /* text_deltas */,
+ url2, diff_editor, diff_edit_baton, pool));
/* Drive the reporter; do the diff. */
SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
svn_depth_infinity,
FALSE, NULL,
pool));
- return reporter->finish_report(reporter_baton, pool);
-}
+ return svn_error_trace(reporter->finish_report(reporter_baton, pool));
+}
/* Perform a diff between a repository path and a working-copy path.
- PATH1 may be either a URL or a working copy path. PATH2 is a
+ PATH_OR_URL1 may be either a URL or a working copy path. PATH2 is a
working copy path. REVISION1 and REVISION2 are their respective
revisions. If REVERSE is TRUE, the diff will be done in reverse.
- If PEG_REVISION is specified, then PATH1 is the path in the peg
+ If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg
revision, and the actual repository path to be compared is
determined by following copy history.
- All other options are the same as those passed to svn_client_diff5(). */
+ All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
-diff_repos_wc(const char *path1,
+diff_repos_wc(const char *path_or_url1,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *peg_revision,
const char *path2,
@@ -1924,10 +1823,12 @@ diff_repos_wc(const char *path1,
svn_boolean_t use_git_diff_format,
const apr_array_header_t *changelists,
const svn_wc_diff_callbacks4_t *callbacks,
- struct diff_cmd_baton *callback_baton,
+ void *callback_baton,
+ struct diff_cmd_baton *cmd_baton,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
+ apr_pool_t *pool = scratch_pool;
const char *url1, *anchor, *anchor_url, *target;
svn_revnum_t rev;
svn_ra_session_t *ra_session;
@@ -1938,22 +1839,33 @@ diff_repos_wc(const char *path1,
void *diff_edit_baton;
svn_boolean_t rev2_is_base = (revision2->kind == svn_opt_revision_base);
svn_boolean_t server_supports_depth;
- const char *abspath1;
+ const char *abspath_or_url1;
const char *abspath2;
const char *anchor_abspath;
+ svn_node_kind_t kind1;
+ svn_node_kind_t kind2;
+ svn_boolean_t is_copy;
+ svn_revnum_t cf_revision;
+ const char *cf_repos_relpath;
+ const char *cf_repos_root_url;
SVN_ERR_ASSERT(! svn_path_is_url(path2));
- if (!svn_path_is_url(path1))
- SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
+ if (!svn_path_is_url(path_or_url1))
+ {
+ SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
+ SVN_ERR(svn_wc__node_get_url(&url1, ctx->wc_ctx, abspath_or_url1,
+ pool, pool));
+ }
else
- abspath1 = path1;
+ {
+ url1 = path_or_url1;
+ abspath_or_url1 = path_or_url1;
+ }
SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool));
- /* Convert path1 to a URL to feed to do_diff. */
- SVN_ERR(convert_to_url(&url1, ctx->wc_ctx, abspath1, pool, pool));
-
+ /* Convert path_or_url1 to a URL to feed to do_diff. */
SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
ctx->wc_ctx, path2,
pool, pool));
@@ -1962,63 +1874,72 @@ diff_repos_wc(const char *path1,
SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool));
SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath,
pool, pool));
- if (! anchor_url)
- return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
- _("Directory '%s' has no URL"),
- svn_dirent_local_style(anchor, pool));
+ SVN_ERR_ASSERT(anchor_url != NULL);
/* If we are performing a pegged diff, we need to find out what our
actual URLs will be. */
if (peg_revision->kind != svn_opt_revision_unspecified)
{
- svn_opt_revision_t *start_ignore, *end_ignore, end;
- const char *url_ignore;
-
- end.kind = svn_opt_revision_unspecified;
-
- SVN_ERR(svn_client__repos_locations(&url1, &start_ignore,
- &url_ignore, &end_ignore,
+ SVN_ERR(svn_client__repos_locations(&url1, NULL, NULL, NULL,
NULL,
- path1,
+ path_or_url1,
peg_revision,
- revision1, &end,
+ revision1, NULL,
ctx, pool));
if (!reverse)
{
- callback_baton->orig_path_1 = url1;
- callback_baton->orig_path_2 =
+ cmd_baton->orig_path_1 = url1;
+ cmd_baton->orig_path_2 =
svn_path_url_add_component2(anchor_url, target, pool);
}
else
{
- callback_baton->orig_path_1 =
+ cmd_baton->orig_path_1 =
svn_path_url_add_component2(anchor_url, target, pool);
- callback_baton->orig_path_2 = url1;
+ cmd_baton->orig_path_2 = url1;
}
}
- /* Establish RA session to path2's anchor */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, anchor_url,
- NULL, NULL, FALSE, TRUE,
- ctx, pool));
- callback_baton->ra_session = ra_session;
- if (use_git_diff_format)
- {
- SVN_ERR(svn_wc__get_wc_root(&callback_baton->wc_root_abspath,
- ctx->wc_ctx, anchor_abspath,
- pool, pool));
- }
- callback_baton->anchor = anchor;
+ /* Open an RA session to URL1 to figure out its node kind. */
+ SVN_ERR(svn_client_open_ra_session2(&ra_session, url1, abspath2,
+ ctx, pool, pool));
+ /* Resolve the revision to use for URL1. */
+ SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx,
+ (strcmp(path_or_url1, url1) == 0)
+ ? NULL : abspath_or_url1,
+ ra_session, revision1, pool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind1, pool));
+ /* Figure out the node kind of the local target. */
+ SVN_ERR(svn_wc_read_kind2(&kind2, ctx->wc_ctx, abspath2,
+ TRUE, FALSE, pool));
+
+ cmd_baton->ra_session = ra_session;
+ cmd_baton->anchor = anchor;
+
+ if (!reverse)
+ cmd_baton->revnum1 = rev;
+ else
+ cmd_baton->revnum2 = rev;
+
+ /* Check if our diff target is a copied node. */
+ SVN_ERR(svn_wc__node_get_origin(&is_copy,
+ &cf_revision,
+ &cf_repos_relpath,
+ &cf_repos_root_url,
+ NULL, NULL,
+ ctx->wc_ctx, abspath2,
+ FALSE, pool, pool));
+
+ /* Use the diff editor to generate the diff. */
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
SVN_RA_CAPABILITY_DEPTH, pool));
-
- SVN_ERR(svn_wc_get_diff_editor6(&diff_editor, &diff_edit_baton,
+ SVN_ERR(svn_wc__get_diff_editor(&diff_editor, &diff_edit_baton,
ctx->wc_ctx,
anchor_abspath,
target,
depth,
- ignore_ancestry,
+ ignore_ancestry || is_copy,
show_copies_as_adds,
use_git_diff_format,
rev2_is_base,
@@ -2028,54 +1949,114 @@ diff_repos_wc(const char *path1,
callbacks, callback_baton,
ctx->cancel_func, ctx->cancel_baton,
pool, pool));
-
- /* Tell the RA layer we want a delta to change our txn to URL1 */
- SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx,
- (strcmp(path1, url1) == 0)
- ? NULL : abspath1,
- ra_session, revision1, pool));
-
- if (!reverse)
- callback_baton->revnum1 = rev;
- else
- callback_baton->revnum2 = rev;
+ SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool));
if (depth != svn_depth_infinity)
diff_depth = depth;
else
diff_depth = svn_depth_unknown;
- SVN_ERR(svn_ra_do_diff3(ra_session,
- &reporter, &reporter_baton,
- rev,
- target,
- diff_depth,
- ignore_ancestry,
- TRUE, /* text_deltas */
- url1,
- diff_editor, diff_edit_baton, pool));
-
- /* Create a txn mirror of path2; the diff editor will print
- diffs in reverse. :-) */
- SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, abspath2,
- reporter, reporter_baton,
- FALSE, depth, TRUE, (! server_supports_depth),
- FALSE,
- ctx->cancel_func, ctx->cancel_baton,
- NULL, NULL, /* notification is N/A */
- pool));
+ if (is_copy)
+ {
+ const char *copyfrom_parent_url;
+ const char *copyfrom_basename;
+ svn_depth_t copy_depth;
+
+ cmd_baton->repos_wc_diff_target_is_copy = TRUE;
+
+ /* We're diffing a locally copied/moved node.
+ * Describe the copy source to the reporter instead of the copy itself.
+ * Doing the latter would generate a single add_directory() call to the
+ * diff editor which results in an unexpected diff (the copy would
+ * be shown as deleted). */
+
+ if (cf_repos_relpath[0] == '\0')
+ {
+ copyfrom_parent_url = cf_repos_root_url;
+ copyfrom_basename = "";
+ }
+ else
+ {
+ const char *parent_relpath;
+ svn_relpath_split(&parent_relpath, &copyfrom_basename,
+ cf_repos_relpath, scratch_pool);
+
+ copyfrom_parent_url = svn_path_url_add_component2(cf_repos_root_url,
+ parent_relpath,
+ scratch_pool);
+ }
+ SVN_ERR(svn_ra_reparent(ra_session, copyfrom_parent_url, pool));
+
+ /* Tell the RA layer we want a delta to change our txn to URL1 */
+ SVN_ERR(svn_ra_do_diff3(ra_session,
+ &reporter, &reporter_baton,
+ rev,
+ target,
+ diff_depth,
+ ignore_ancestry,
+ TRUE, /* text_deltas */
+ url1,
+ diff_editor, diff_edit_baton, pool));
+
+ /* Report the copy source. */
+ SVN_ERR(svn_wc__node_get_depth(&copy_depth, ctx->wc_ctx, abspath2,
+ pool));
+
+ if (copy_depth == svn_depth_unknown)
+ copy_depth = svn_depth_infinity;
+
+ SVN_ERR(reporter->set_path(reporter_baton, "",
+ cf_revision,
+ copy_depth, FALSE, NULL, scratch_pool));
+
+ if (strcmp(target, copyfrom_basename) != 0)
+ SVN_ERR(reporter->link_path(reporter_baton, target,
+ svn_path_url_add_component2(
+ cf_repos_root_url,
+ cf_repos_relpath,
+ scratch_pool),
+ cf_revision,
+ copy_depth, FALSE, NULL, scratch_pool));
+
+ /* Finish the report to generate the diff. */
+ SVN_ERR(reporter->finish_report(reporter_baton, pool));
+ }
+ else
+ {
+ /* Tell the RA layer we want a delta to change our txn to URL1 */
+ SVN_ERR(svn_ra_do_diff3(ra_session,
+ &reporter, &reporter_baton,
+ rev,
+ target,
+ diff_depth,
+ ignore_ancestry,
+ TRUE, /* text_deltas */
+ url1,
+ diff_editor, diff_edit_baton, pool));
+
+ /* Create a txn mirror of path2; the diff editor will print
+ diffs in reverse. :-) */
+ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, abspath2,
+ reporter, reporter_baton,
+ FALSE, depth, TRUE,
+ (! server_supports_depth),
+ FALSE,
+ ctx->cancel_func, ctx->cancel_baton,
+ NULL, NULL, /* notification is N/A */
+ pool));
+ }
return SVN_NO_ERROR;
}
-/* This is basically just the guts of svn_client_diff[_peg]5(). */
+/* This is basically just the guts of svn_client_diff[_peg]6(). */
static svn_error_t *
do_diff(const svn_wc_diff_callbacks4_t *callbacks,
struct diff_cmd_baton *callback_baton,
svn_client_ctx_t *ctx,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision,
@@ -2090,56 +2071,184 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
svn_boolean_t is_repos2;
/* Check if paths/revisions are urls/local. */
- SVN_ERR(check_paths(&is_repos1, &is_repos2, path1, path2,
+ SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2,
revision1, revision2, peg_revision));
if (is_repos1)
{
if (is_repos2)
{
+ /* ### Ignores 'show_copies_as_adds'. */
SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx,
- path1, path2, revision1, revision2,
+ path_or_url1, path_or_url2,
+ revision1, revision2,
peg_revision, depth, ignore_ancestry,
pool));
}
- else /* path2 is a working copy path */
+ else /* path_or_url2 is a working copy path */
{
- SVN_ERR(diff_repos_wc(path1, revision1, peg_revision,
- path2, revision2, FALSE, depth,
+ SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision,
+ path_or_url2, revision2, FALSE, depth,
ignore_ancestry, show_copies_as_adds,
use_git_diff_format, changelists,
- callbacks, callback_baton, ctx, pool));
+ callbacks, callback_baton, callback_baton,
+ ctx, pool));
}
}
- else /* path1 is a working copy path */
+ else /* path_or_url1 is a working copy path */
{
if (is_repos2)
{
- SVN_ERR(diff_repos_wc(path2, revision2, peg_revision,
- path1, revision1, TRUE, depth,
+ SVN_ERR(diff_repos_wc(path_or_url2, revision2, peg_revision,
+ path_or_url1, revision1, TRUE, depth,
ignore_ancestry, show_copies_as_adds,
use_git_diff_format, changelists,
- callbacks, callback_baton, ctx, pool));
+ callbacks, callback_baton, callback_baton,
+ ctx, pool));
}
- else /* path2 is a working copy path */
+ else /* path_or_url2 is a working copy path */
{
- SVN_ERR(diff_wc_wc(path1, revision1, path2, revision2,
- depth, ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists,
- callbacks, callback_baton, ctx, pool));
+ if (revision1->kind == svn_opt_revision_working
+ && revision2->kind == svn_opt_revision_working)
+ {
+ const char *abspath1;
+ const char *abspath2;
+
+ SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool));
+
+ SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
+ depth,
+ callbacks,
+ callback_baton,
+ ctx, pool));
+ }
+ else
+ SVN_ERR(diff_wc_wc(path_or_url1, revision1,
+ path_or_url2, revision2,
+ depth, ignore_ancestry, show_copies_as_adds,
+ use_git_diff_format, changelists,
+ callbacks, callback_baton, ctx, pool));
}
}
return SVN_NO_ERROR;
}
+/* Perform a diff between a repository path and a working-copy path.
+
+ PATH_OR_URL1 may be either a URL or a working copy path. PATH2 is a
+ working copy path. REVISION1 and REVISION2 are their respective
+ revisions. If REVERSE is TRUE, the diff will be done in reverse.
+ If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg
+ revision, and the actual repository path to be compared is
+ determined by following copy history.
+
+ All other options are the same as those passed to svn_client_diff6(). */
+static svn_error_t *
+diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func,
+ void *summarize_baton,
+ const char *path_or_url1,
+ const svn_opt_revision_t *revision1,
+ const svn_opt_revision_t *peg_revision,
+ const char *path2,
+ const svn_opt_revision_t *revision2,
+ svn_boolean_t reverse,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ const apr_array_header_t *changelists,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ const char *anchor, *target;
+ svn_wc_diff_callbacks4_t *callbacks;
+ void *callback_baton;
+ struct diff_cmd_baton cmd_baton;
+
+ SVN_ERR_ASSERT(! svn_path_is_url(path2));
+
+ SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
+ ctx->wc_ctx, path2,
+ pool, pool));
+
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &callbacks, &callback_baton, target, reverse,
+ summarize_func, summarize_baton, pool));
+
+ SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision,
+ path2, revision2, reverse,
+ depth, FALSE, TRUE, FALSE, changelists,
+ callbacks, callback_baton, &cmd_baton,
+ ctx, pool));
+ return SVN_NO_ERROR;
+}
+
+/* Perform a summary diff between two working-copy paths.
+
+ PATH1 and PATH2 are both working copy paths. REVISION1 and
+ REVISION2 are their respective revisions.
+
+ All other options are the same as those passed to svn_client_diff6(). */
+static svn_error_t *
+diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func,
+ void *summarize_baton,
+ const char *path1,
+ const svn_opt_revision_t *revision1,
+ const char *path2,
+ const svn_opt_revision_t *revision2,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ const apr_array_header_t *changelists,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_wc_diff_callbacks4_t *callbacks;
+ void *callback_baton;
+ const char *abspath1, *target1;
+ svn_node_kind_t kind;
+
+ SVN_ERR_ASSERT(! svn_path_is_url(path1));
+ SVN_ERR_ASSERT(! svn_path_is_url(path2));
+
+ /* Currently we support only the case where path1 and path2 are the
+ same path. */
+ if ((strcmp(path1, path2) != 0)
+ || (! ((revision1->kind == svn_opt_revision_base)
+ && (revision2->kind == svn_opt_revision_working))))
+ return unsupported_diff_error
+ (svn_error_create
+ (SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Summarized diffs are only supported between a path's text-base "
+ "and its working files at this time")));
+
+ /* Find the node kind of PATH1 so that we know whether the diff drive will
+ be anchored at PATH1 or its parent dir. */
+ SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
+ SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1,
+ TRUE, FALSE, pool));
+ target1 = (kind == svn_node_dir) ? "" : svn_dirent_basename(path1, pool);
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &callbacks, &callback_baton, target1, FALSE,
+ summarize_func, summarize_baton, pool));
+
+ SVN_ERR(svn_wc_diff6(ctx->wc_ctx,
+ abspath1,
+ callbacks, callback_baton,
+ depth,
+ ignore_ancestry, FALSE /* show_copies_as_adds */,
+ FALSE /* use_git_diff_format */, changelists,
+ ctx->cancel_func, ctx->cancel_baton,
+ pool));
+ return SVN_NO_ERROR;
+}
+
/* Perform a diff summary between two repository paths. */
static svn_error_t *
diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func,
void *summarize_baton,
svn_client_ctx_t *ctx,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision,
@@ -2155,35 +2264,82 @@ diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func,
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
+ const svn_diff_tree_processor_t *diff_processor;
+
const char *url1;
const char *url2;
const char *base_path;
svn_revnum_t rev1;
svn_revnum_t rev2;
+ svn_node_kind_t kind1;
+ svn_node_kind_t kind2;
const char *anchor1;
const char *anchor2;
const char *target1;
const char *target2;
svn_ra_session_t *ra_session;
+ svn_wc_diff_callbacks4_t *callbacks;
+ void *callback_baton;
/* Prepare info for the repos repos diff. */
SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
&anchor1, &anchor2, &target1, &target2,
- &ra_session, ctx,
- path1, path2, revision1, revision2,
+ &kind1, &kind2, &ra_session,
+ ctx, path_or_url1, path_or_url2,
+ revision1, revision2,
peg_revision, pool));
- /* Now, we open an extra RA session to the correct anchor
- location for URL1. This is used to get the kind of deleted paths. */
- SVN_ERR(svn_client__open_ra_session_internal(&extra_ra_session, NULL,
- anchor1, NULL, NULL, FALSE,
- TRUE, ctx, pool));
-
/* Set up the repos_diff editor. */
- SVN_ERR(svn_client__get_diff_summarize_editor
- (target2, summarize_func,
- summarize_baton, extra_ra_session, rev1, ctx->cancel_func,
- ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool));
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &callbacks, &callback_baton,
+ target1, FALSE, summarize_func, summarize_baton, pool));
+
+ SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+ callbacks, callback_baton,
+ TRUE /* walk_deleted_dirs */,
+ pool, pool));
+
+
+ /* The repository can bring in a new working copy, but not delete
+ everything. Luckily our new diff handler can just be reversed. */
+ if (kind2 == svn_node_none)
+ {
+ const char *str_tmp;
+ svn_revnum_t rev_tmp;
+
+ str_tmp = url2;
+ url2 = url1;
+ url1 = str_tmp;
+
+ rev_tmp = rev2;
+ rev2 = rev1;
+ rev1 = rev_tmp;
+
+ str_tmp = anchor2;
+ anchor2 = anchor1;
+ anchor1 = str_tmp;
+
+ str_tmp = target2;
+ target2 = target1;
+ target1 = str_tmp;
+
+ diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
+ NULL, pool);
+ }
+
+ /* Now, we open an extra RA session to the correct anchor
+ location for URL1. This is used to get deleted path information. */
+ SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, NULL,
+ ctx, pool, pool));
+
+ SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
+ extra_ra_session,
+ depth,
+ rev1,
+ FALSE /* text_deltas */,
+ diff_processor,
+ ctx->cancel_func, ctx->cancel_baton,
+ pool));
/* We want to switch our txn into URL2 */
SVN_ERR(svn_ra_do_diff3
@@ -2196,7 +2352,7 @@ diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func,
SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
svn_depth_infinity,
FALSE, NULL, pool));
- return reporter->finish_report(reporter_baton, pool);
+ return svn_error_trace(reporter->finish_report(reporter_baton, pool));
}
/* This is basically just the guts of svn_client_diff_summarize[_peg]2(). */
@@ -2204,31 +2360,94 @@ static svn_error_t *
do_diff_summarize(svn_client_diff_summarize_func_t summarize_func,
void *summarize_baton,
svn_client_ctx_t *ctx,
- const char *path1,
- const char *path2,
+ const char *path_or_url1,
+ const char *path_or_url2,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *revision2,
const svn_opt_revision_t *peg_revision,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
+ const apr_array_header_t *changelists,
apr_pool_t *pool)
{
svn_boolean_t is_repos1;
svn_boolean_t is_repos2;
/* Check if paths/revisions are urls/local. */
- SVN_ERR(check_paths(&is_repos1, &is_repos2, path1, path2,
+ SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2,
revision1, revision2, peg_revision));
- if (is_repos1 && is_repos2)
- return diff_summarize_repos_repos(summarize_func, summarize_baton, ctx,
- path1, path2, revision1, revision2,
- peg_revision, depth, ignore_ancestry,
- pool);
- else
- return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Summarizing diff can only compare repository "
- "to repository"));
+ if (is_repos1)
+ {
+ if (is_repos2)
+ SVN_ERR(diff_summarize_repos_repos(summarize_func, summarize_baton, ctx,
+ path_or_url1, path_or_url2,
+ revision1, revision2,
+ peg_revision, depth, ignore_ancestry,
+ pool));
+ else
+ SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton,
+ path_or_url1, revision1,
+ peg_revision,
+ path_or_url2, revision2,
+ FALSE, depth,
+ ignore_ancestry,
+ changelists,
+ ctx, pool));
+ }
+ else /* ! is_repos1 */
+ {
+ if (is_repos2)
+ SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton,
+ path_or_url2, revision2,
+ peg_revision,
+ path_or_url1, revision1,
+ TRUE, depth,
+ ignore_ancestry,
+ changelists,
+ ctx, pool));
+ else
+ {
+ if (revision1->kind == svn_opt_revision_working
+ && revision2->kind == svn_opt_revision_working)
+ {
+ const char *abspath1;
+ const char *abspath2;
+ svn_wc_diff_callbacks4_t *callbacks;
+ void *callback_baton;
+ const char *target;
+ svn_node_kind_t kind;
+
+ SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool));
+
+ SVN_ERR(svn_io_check_resolved_path(abspath1, &kind, pool));
+
+ if (kind == svn_node_dir)
+ target = "";
+ else
+ target = svn_dirent_basename(path_or_url1, NULL);
+
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &callbacks, &callback_baton, target, FALSE,
+ summarize_func, summarize_baton, pool));
+
+ SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
+ depth,
+ callbacks,
+ callback_baton,
+ ctx, pool));
+ }
+ else
+ SVN_ERR(diff_summarize_wc_wc(summarize_func, summarize_baton,
+ path_or_url1, revision1,
+ path_or_url2, revision2,
+ depth, ignore_ancestry,
+ changelists, ctx, pool));
+ }
+ }
+
+ return SVN_NO_ERROR;
}
@@ -2247,8 +2466,7 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton,
/* See if there is a diff command and/or diff arguments. */
if (config)
{
- svn_config_t *cfg = apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING);
+ svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_CMD, NULL);
if (options == NULL)
@@ -2334,139 +2552,139 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton,
* These cases require server communication.
*/
svn_error_t *
-svn_client_diff5(const apr_array_header_t *options,
- const char *path1,
+svn_client_diff6(const apr_array_header_t *options,
+ const char *path_or_url1,
const svn_opt_revision_t *revision1,
- const char *path2,
+ const char *path_or_url2,
const svn_opt_revision_t *revision2,
const char *relative_to_dir,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
+ svn_boolean_t no_diff_added,
svn_boolean_t no_diff_deleted,
svn_boolean_t show_copies_as_adds,
svn_boolean_t ignore_content_type,
+ svn_boolean_t ignore_properties,
+ svn_boolean_t properties_only,
svn_boolean_t use_git_diff_format,
const char *header_encoding,
- apr_file_t *outfile,
- apr_file_t *errfile,
+ svn_stream_t *outstream,
+ svn_stream_t *errstream,
const apr_array_header_t *changelists,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
struct diff_cmd_baton diff_cmd_baton = { 0 };
- svn_wc_diff_callbacks4_t diff_callbacks;
+ svn_opt_revision_t peg_revision;
+
+ if (ignore_properties && properties_only)
+ return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Cannot ignore properties and show only "
+ "properties at the same time"));
/* We will never do a pegged diff from here. */
- svn_opt_revision_t peg_revision;
peg_revision.kind = svn_opt_revision_unspecified;
/* setup callback and baton */
- diff_callbacks.file_opened = diff_file_opened;
- diff_callbacks.file_changed = diff_file_changed;
- diff_callbacks.file_added = diff_file_added;
- diff_callbacks.file_deleted = no_diff_deleted ? diff_file_deleted_no_diff :
- diff_file_deleted_with_diff;
- diff_callbacks.dir_added = diff_dir_added;
- diff_callbacks.dir_deleted = diff_dir_deleted;
- diff_callbacks.dir_props_changed = diff_dir_props_changed;
- diff_callbacks.dir_opened = diff_dir_opened;
- diff_callbacks.dir_closed = diff_dir_closed;
-
- diff_cmd_baton.orig_path_1 = path1;
- diff_cmd_baton.orig_path_2 = path2;
+ diff_cmd_baton.orig_path_1 = path_or_url1;
+ diff_cmd_baton.orig_path_2 = path_or_url2;
SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
ctx->config, pool));
diff_cmd_baton.pool = pool;
- diff_cmd_baton.outfile = outfile;
- diff_cmd_baton.errfile = errfile;
+ diff_cmd_baton.outstream = outstream;
+ diff_cmd_baton.errstream = errstream;
diff_cmd_baton.header_encoding = header_encoding;
diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
- diff_cmd_baton.force_empty = FALSE;
diff_cmd_baton.force_binary = ignore_content_type;
+ diff_cmd_baton.ignore_properties = ignore_properties;
+ diff_cmd_baton.properties_only = properties_only;
diff_cmd_baton.relative_to_dir = relative_to_dir;
diff_cmd_baton.use_git_diff_format = use_git_diff_format;
+ diff_cmd_baton.no_diff_added = no_diff_added;
+ diff_cmd_baton.no_diff_deleted = no_diff_deleted;
+ diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
+
diff_cmd_baton.wc_ctx = ctx->wc_ctx;
- diff_cmd_baton.visited_paths = apr_hash_make(pool);
diff_cmd_baton.ra_session = NULL;
- diff_cmd_baton.wc_root_abspath = NULL;
diff_cmd_baton.anchor = NULL;
return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
- path1, path2, revision1, revision2, &peg_revision,
+ path_or_url1, path_or_url2, revision1, revision2,
+ &peg_revision,
depth, ignore_ancestry, show_copies_as_adds,
use_git_diff_format, changelists, pool);
}
svn_error_t *
-svn_client_diff_peg5(const apr_array_header_t *options,
- const char *path,
+svn_client_diff_peg6(const apr_array_header_t *options,
+ const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *start_revision,
const svn_opt_revision_t *end_revision,
const char *relative_to_dir,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
+ svn_boolean_t no_diff_added,
svn_boolean_t no_diff_deleted,
svn_boolean_t show_copies_as_adds,
svn_boolean_t ignore_content_type,
+ svn_boolean_t ignore_properties,
+ svn_boolean_t properties_only,
svn_boolean_t use_git_diff_format,
const char *header_encoding,
- apr_file_t *outfile,
- apr_file_t *errfile,
+ svn_stream_t *outstream,
+ svn_stream_t *errstream,
const apr_array_header_t *changelists,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
struct diff_cmd_baton diff_cmd_baton = { 0 };
- svn_wc_diff_callbacks4_t diff_callbacks;
+
+ if (ignore_properties && properties_only)
+ return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Cannot ignore properties and show only "
+ "properties at the same time"));
/* setup callback and baton */
- diff_callbacks.file_opened = diff_file_opened;
- diff_callbacks.file_changed = diff_file_changed;
- diff_callbacks.file_added = diff_file_added;
- diff_callbacks.file_deleted = no_diff_deleted ? diff_file_deleted_no_diff :
- diff_file_deleted_with_diff;
- diff_callbacks.dir_added = diff_dir_added;
- diff_callbacks.dir_deleted = diff_dir_deleted;
- diff_callbacks.dir_props_changed = diff_dir_props_changed;
- diff_callbacks.dir_opened = diff_dir_opened;
- diff_callbacks.dir_closed = diff_dir_closed;
-
- diff_cmd_baton.orig_path_1 = path;
- diff_cmd_baton.orig_path_2 = path;
+ diff_cmd_baton.orig_path_1 = path_or_url;
+ diff_cmd_baton.orig_path_2 = path_or_url;
SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
ctx->config, pool));
diff_cmd_baton.pool = pool;
- diff_cmd_baton.outfile = outfile;
- diff_cmd_baton.errfile = errfile;
+ diff_cmd_baton.outstream = outstream;
+ diff_cmd_baton.errstream = errstream;
diff_cmd_baton.header_encoding = header_encoding;
diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
- diff_cmd_baton.force_empty = FALSE;
diff_cmd_baton.force_binary = ignore_content_type;
+ diff_cmd_baton.ignore_properties = ignore_properties;
+ diff_cmd_baton.properties_only = properties_only;
diff_cmd_baton.relative_to_dir = relative_to_dir;
diff_cmd_baton.use_git_diff_format = use_git_diff_format;
+ diff_cmd_baton.no_diff_added = no_diff_added;
+ diff_cmd_baton.no_diff_deleted = no_diff_deleted;
+ diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
+
diff_cmd_baton.wc_ctx = ctx->wc_ctx;
- diff_cmd_baton.visited_paths = apr_hash_make(pool);
diff_cmd_baton.ra_session = NULL;
- diff_cmd_baton.wc_root_abspath = NULL;
diff_cmd_baton.anchor = NULL;
return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
- path, path, start_revision, end_revision, peg_revision,
+ path_or_url, path_or_url, start_revision, end_revision,
+ peg_revision,
depth, ignore_ancestry, show_copies_as_adds,
use_git_diff_format, changelists, pool);
}
svn_error_t *
-svn_client_diff_summarize2(const char *path1,
+svn_client_diff_summarize2(const char *path_or_url1,
const svn_opt_revision_t *revision1,
- const char *path2,
+ const char *path_or_url2,
const svn_opt_revision_t *revision2,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
@@ -2480,14 +2698,14 @@ svn_client_diff_summarize2(const char *path1,
svn_opt_revision_t peg_revision;
peg_revision.kind = svn_opt_revision_unspecified;
- /* ### CHANGELISTS parameter isn't used */
return do_diff_summarize(summarize_func, summarize_baton, ctx,
- path1, path2, revision1, revision2, &peg_revision,
- depth, ignore_ancestry, pool);
+ path_or_url1, path_or_url2, revision1, revision2,
+ &peg_revision,
+ depth, ignore_ancestry, changelists, pool);
}
svn_error_t *
-svn_client_diff_summarize_peg2(const char *path,
+svn_client_diff_summarize_peg2(const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *start_revision,
const svn_opt_revision_t *end_revision,
@@ -2499,10 +2717,10 @@ svn_client_diff_summarize_peg2(const char *path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- /* ### CHANGELISTS parameter isn't used */
return do_diff_summarize(summarize_func, summarize_baton, ctx,
- path, path, start_revision, end_revision,
- peg_revision, depth, ignore_ancestry, pool);
+ path_or_url, path_or_url,
+ start_revision, end_revision, peg_revision,
+ depth, ignore_ancestry, changelists, pool);
}
svn_client_diff_summarize_t *