You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2011/10/10 18:01:01 UTC
svn commit: r1181047 -
/subversion/branches/tree-read-api/subversion/libsvn_client/diff.c
Author: julianfoad
Date: Mon Oct 10 16:01:01 2011
New Revision: 1181047
URL: http://svn.apache.org/viewvc?rev=1181047&view=rev
Log:
On the 'tree-read-api' branch: An initial implementation of a flexible diff
that compares two arbitrary trees.
* subversion/libsvn_client/diff.c
(tree_get_tmp_file, compare_two_trees, open_tree): New functions.
(do_diff): Use compare_two_trees() always (for demonstration purposes).
Modified:
subversion/branches/tree-read-api/subversion/libsvn_client/diff.c
Modified: subversion/branches/tree-read-api/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/diff.c?rev=1181047&r1=1181046&r2=1181047&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/diff.c Mon Oct 10 16:01:01 2011
@@ -50,6 +50,7 @@
#include "svn_sorts.h"
#include "svn_subst.h"
#include "client.h"
+#include "tree.h"
#include "private/svn_wc_private.h"
@@ -67,6 +68,239 @@ static const char under_string[] =
/*-----------------------------------------------------------------*/
+
+/* */
+static svn_error_t *
+tree_get_tmp_file(svn_client_tree_t *tree,
+ const char **tmpfile_abspath,
+ apr_hash_t **props,
+ const char *relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_stream_t *s_in;
+ svn_stream_t *s_out;
+
+ SVN_ERR(svn_tree_get_file(tree, &s_in, props, relpath,
+ scratch_pool, scratch_pool));
+ if (s_in == NULL)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("no file at '%s'"), relpath);
+
+ SVN_ERR(svn_stream_open_unique(&s_out, tmpfile_abspath, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(s_in, s_out, NULL, NULL, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Compare two (independent) trees */
+static svn_error_t *
+compare_two_trees(svn_client_tree_t *tree1,
+ const char *relpath1,
+ svn_client_tree_t *tree2,
+ const char *relpath2,
+ const svn_wc_diff_callbacks4_t *callbacks,
+ void *callback_baton,
+ apr_pool_t *scratch_pool)
+{
+ const char *empty_file = "/dev/null";
+ svn_kind_t kind1, kind2;
+
+ SVN_ERR(svn_tree_get_kind(tree1, &kind1, relpath1, scratch_pool));
+ SVN_ERR(svn_tree_get_kind(tree2, &kind2, relpath2, scratch_pool));
+
+ if (kind1 == kind2)
+ {
+ switch (kind1)
+ {
+ case svn_kind_none:
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("No node at relative paths '%s' and '%s'"),
+ relpath1, relpath2);
+ case svn_kind_file:
+ {
+ const char *tmpfile1, *tmpfile2;
+ apr_hash_t *props1, *props2;
+ apr_array_header_t *propchanges;
+ svn_revnum_t rev = SVN_INVALID_REVNUM;
+
+ SVN_ERR(tree_get_tmp_file(tree1, &tmpfile1, &props1, relpath1,
+ scratch_pool, scratch_pool));
+ SVN_ERR(tree_get_tmp_file(tree2, &tmpfile2, &props2, relpath2,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_prop_diffs(&propchanges, props1, props2, scratch_pool));
+
+ SVN_ERR(callbacks->file_opened(NULL, NULL,
+ relpath1, rev,
+ callback_baton, scratch_pool));
+ SVN_ERR(callbacks->file_changed(NULL, NULL, NULL,
+ relpath1, tmpfile1, tmpfile2,
+ rev, rev, NULL, NULL,
+ propchanges, props1,
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_dir:
+ {
+ apr_hash_t *dirents1, *dirents2;
+ apr_hash_t *props1, *props2;
+ apr_array_header_t *propchanges;
+ svn_revnum_t rev = SVN_INVALID_REVNUM;
+ apr_hash_t *all_children;
+ apr_hash_index_t *hi;
+
+ /* Open dir and compare properties */
+ SVN_ERR(svn_tree_get_dir(tree1, &dirents1, &props1, relpath1,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_tree_get_dir(tree2, &dirents2, &props2, relpath2,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_prop_diffs(&propchanges, props1, props2, scratch_pool));
+ SVN_ERR(callbacks->dir_opened(NULL, NULL, NULL,
+ relpath1, rev,
+ callback_baton, scratch_pool));
+ SVN_ERR(callbacks->dir_props_changed(NULL, NULL,
+ relpath1, FALSE /* added */,
+ propchanges, props1,
+ callback_baton, scratch_pool));
+
+ /* Recurse */
+ all_children = apr_hash_overlay(scratch_pool, dirents1, dirents2);
+ for (hi = apr_hash_first(scratch_pool, all_children);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *name = svn__apr_hash_index_key(hi);
+ const char *child_relpath1 = svn_relpath_join(relpath1, name,
+ scratch_pool);
+ const char *child_relpath2 = svn_relpath_join(relpath2, name,
+ scratch_pool);
+
+ SVN_ERR(compare_two_trees(tree1, child_relpath1,
+ tree2, child_relpath2,
+ callbacks, callback_baton,
+ scratch_pool));
+ }
+
+ /* Close dir */
+ SVN_ERR(callbacks->dir_closed(NULL, NULL, NULL,
+ relpath1, FALSE /* added */,
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_symlink:
+ SVN_DBG(("compare_two_trees: '%s': is a symlink\n",
+ relpath1));
+ break;
+ default:
+ /* Unknown node kind. */
+ SVN_DBG(("compare_two_trees: '%s': unknown node kind %d\n",
+ relpath1, kind1));
+ break;
+ }
+ }
+ else /* different kinds */
+ {
+ /* Show a delete ... */
+ switch (kind1)
+ {
+ case svn_kind_none:
+ break;
+ case svn_kind_file:
+ {
+ const char *tmpfile1;
+ apr_hash_t *props1;
+
+ SVN_ERR(tree_get_tmp_file(tree1, &tmpfile1, &props1, relpath1,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(callbacks->file_deleted(NULL, NULL,
+ relpath1, tmpfile1, empty_file,
+ NULL, NULL,
+ props1,
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_dir:
+ {
+ apr_hash_t *dirents1;
+ apr_hash_t *props1;
+
+ SVN_ERR(svn_tree_get_dir(tree1, &dirents1, &props1, relpath1,
+ scratch_pool, scratch_pool));
+ SVN_ERR(callbacks->dir_deleted(NULL, NULL,
+ relpath1,
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_symlink:
+ SVN_DBG(("compare_two_trees: deleted symlink '%s'\n", relpath1));
+ break;
+ default:
+ /* Unknown node kind. */
+ SVN_DBG(("compare_two_trees: deleted '%s': unknown node kind %d\n",
+ relpath1, kind1));
+ break;
+ }
+
+ /* ... followed by an add. */
+ switch (kind2)
+ {
+ case svn_kind_none:
+ break;
+ case svn_kind_file:
+ {
+ const char *tmpfile2;
+ apr_hash_t *props1, *props2;
+ apr_array_header_t *propchanges;
+ svn_revnum_t rev = SVN_INVALID_REVNUM;
+
+ SVN_ERR(tree_get_tmp_file(tree2, &tmpfile2, &props2, relpath2,
+ scratch_pool, scratch_pool));
+ props1 = apr_hash_make(scratch_pool);
+ SVN_ERR(svn_prop_diffs(&propchanges, props1, props2, scratch_pool));
+
+ SVN_ERR(callbacks->file_added(NULL, NULL, NULL,
+ relpath1, empty_file, tmpfile2,
+ rev, rev, NULL, NULL,
+ NULL, SVN_INVALID_REVNUM, /* cp-from */
+ propchanges, props1,
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_dir:
+ {
+ apr_hash_t *dirents2;
+ apr_hash_t *props2;
+ svn_revnum_t rev = SVN_INVALID_REVNUM;
+
+ SVN_ERR(svn_tree_get_dir(tree2, &dirents2, &props2, relpath2,
+ scratch_pool, scratch_pool));
+ SVN_ERR(callbacks->dir_added(NULL, NULL, NULL, NULL,
+ relpath2, rev,
+ NULL, SVN_INVALID_REVNUM, /* cp-from */
+ callback_baton, scratch_pool));
+ break;
+ }
+ case svn_kind_symlink:
+ SVN_DBG(("compare_two_trees: added symlink '%s'\n", relpath1));
+ break;
+ default:
+ /* Unknown node kind. */
+ SVN_DBG(("compare_two_trees: added '%s': unknown node kind %d\n",
+ relpath1, kind1));
+ break;
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Compare one tree with a delta against it */
+
+
+/*-----------------------------------------------------------------*/
+
/* Utilities */
@@ -1923,6 +2157,44 @@ diff_repos_wc(const char *path1,
}
+/* Open a tree, whether in the repository or a WC or unversioned on disk. */
+static svn_error_t *
+open_tree(svn_client_tree_t **tree,
+ const char *path,
+ const svn_opt_revision_t *revision,
+ const svn_opt_revision_t *peg_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (svn_path_is_url(path)
+ || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
+ {
+ SVN_ERR(svn_client__repository_tree(tree, path, peg_revision, revision,
+ ctx, result_pool));
+ }
+ else
+ {
+ const char *abspath;
+ int wc_format;
+
+ SVN_ERR(svn_path_get_absolute(&abspath, path, scratch_pool));
+ SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, abspath, scratch_pool));
+ if (wc_format > 0)
+ {
+ if (revision->kind == svn_opt_revision_working)
+ SVN_ERR(svn_client__wc_working_tree(tree, abspath, ctx,
+ result_pool));
+ else
+ SVN_ERR(svn_client__wc_base_tree(tree, abspath, ctx, result_pool));
+ }
+ else
+ SVN_ERR(svn_client__disk_tree(tree, abspath, result_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* This is basically just the guts of svn_client_diff[_peg]5(). */
static svn_error_t *
do_diff(const svn_wc_diff_callbacks4_t *callbacks,
@@ -1947,6 +2219,20 @@ do_diff(const svn_wc_diff_callbacks4_t *
SVN_ERR(check_paths(&is_repos1, &is_repos2, path1, path2,
revision1, revision2, peg_revision));
+ /* Compare two independent trees */
+ {
+ svn_client_tree_t *tree1, *tree2;
+
+ SVN_ERR(open_tree(&tree1, path1, revision1, peg_revision, ctx, pool, pool));
+ SVN_ERR(open_tree(&tree2, path2, revision2, peg_revision, ctx, pool, pool));
+
+ SVN_ERR(compare_two_trees(tree1, "" /* relpath1 */,
+ tree2, "" /* relpath2 */,
+ callbacks, callback_baton,
+ pool));
+ return SVN_NO_ERROR;
+ }
+
if (is_repos1)
{
if (is_repos2)