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/05/05 22:25:27 UTC
svn commit: r1099956 - in /subversion/trunk/subversion: libsvn_wc/info.c
tests/cmdline/info_tests.py
Author: julianfoad
Date: Thu May 5 20:25:27 2011
New Revision: 1099956
URL: http://svn.apache.org/viewvc?rev=1099956&view=rev
Log:
Fix "svn info" so it consistently reports a tree-conflicted node that is not
present. Such a conflict can occur, for example, on an incoming delete on
update. Previously, "info" would only report such a node if it was the
target of the command, and would not find it through recursion.
Also fix a test that was supposed to test this but was broken.
* subversion/libsvn_wc/info.c
(found_entry_baton): Add a field to record unvisited tree conflicts.
(info_found_node_callback): If the visited node is a directory, record any
tree-conflicted children. Remove the visited node from the list of
tree-conflicted nodes.
(depth_includes): New function.
(svn_wc__get_info): After walking the tree, report info on any unvisited
tree conflicts remaining in the list.
* subversion/tests/cmdline/info_tests.py
(info_with_tree_conflicts): Fix the test to correctly check the output of
recursive info.
Modified:
subversion/trunk/subversion/libsvn_wc/info.c
subversion/trunk/subversion/tests/cmdline/info_tests.py
Modified: subversion/trunk/subversion/libsvn_wc/info.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/info.c?rev=1099956&r1=1099955&r2=1099956&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/info.c (original)
+++ subversion/trunk/subversion/libsvn_wc/info.c Thu May 5 20:25:27 2011
@@ -23,6 +23,7 @@
#include "svn_dirent_uri.h"
#include "svn_path.h"
+#include "svn_pools.h"
#include "svn_wc.h"
#include "wc.h"
@@ -266,6 +267,9 @@ struct found_entry_baton
svn_info_receiver2_t receiver;
void *receiver_baton;
svn_wc__db_t *db;
+ /* The set of tree conflicts that have been found but not (yet) visited by
+ * the tree walker. Map of abspath -> svn_wc_conflict_description2_t. */
+ apr_hash_t *tree_conflicts;
};
/* Call WALK_BATON->receiver with WALK_BATON->receiver_baton, passing to it
@@ -278,43 +282,67 @@ info_found_node_callback(const char *loc
apr_pool_t *pool)
{
struct found_entry_baton *fe_baton = walk_baton;
- svn_info2_t *info = NULL;
- const svn_wc_conflict_description2_t *tree_conflict = NULL;
- svn_error_t *err;
+ svn_info2_t *info;
+
+ SVN_ERR(build_info_for_entry(&info, fe_baton->db, local_abspath,
+ kind, pool, pool));
- SVN_ERR(svn_wc__db_op_read_tree_conflict(&tree_conflict, fe_baton->db,
- local_abspath, pool, pool));
+ SVN_ERR_ASSERT(info != NULL && info->wc_info != NULL);
+ SVN_ERR(fe_baton->receiver(fe_baton->receiver_baton, local_abspath,
+ info, pool));
- err = build_info_for_entry(&info, fe_baton->db, local_abspath,
- kind, pool, pool);
- if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- && tree_conflict)
+ /* If this node is a versioned directory, make a note of any tree conflicts
+ * on all immediate children. Some of these may be visited later in this
+ * walk, at which point they will be removed from the list, while any that
+ * are not visited will remain in the list. */
+ if (kind == svn_node_dir)
{
- apr_array_header_t *conflicts = apr_array_make(pool,
- 1, sizeof(const svn_wc_conflict_description2_t *));
+ apr_hash_t *conflicts;
+ apr_hash_index_t *hi;
- svn_error_clear(err);
+ SVN_ERR(svn_wc__db_op_read_all_tree_conflicts(
+ &conflicts, fe_baton->db, local_abspath,
+ apr_hash_pool_get(fe_baton->tree_conflicts), pool));
+ for (hi = apr_hash_first(pool, conflicts); hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *this_basename = svn__apr_hash_index_key(hi);
- SVN_ERR(build_info_for_unversioned(&info, pool));
- SVN_ERR(svn_wc__internal_get_repos_info(&(info->repos_root_URL),
- NULL, fe_baton->db,
- local_abspath, FALSE, FALSE,
- pool, pool));
-
- APR_ARRAY_PUSH(conflicts, const svn_wc_conflict_description2_t *)
- = tree_conflict;
- info->wc_info->conflicts = conflicts;
+ apr_hash_set(fe_baton->tree_conflicts,
+ svn_dirent_join(local_abspath, this_basename, pool),
+ APR_HASH_KEY_STRING, svn__apr_hash_index_val(hi));
+ }
}
- else if (err)
- return svn_error_return(err);
- SVN_ERR_ASSERT(info != NULL && info->wc_info != NULL);
- SVN_ERR(fe_baton->receiver(fe_baton->receiver_baton, local_abspath,
- info, pool));
+ /* Delete this path which we are currently visiting from the list of tree
+ * conflicts. This relies on the walker visiting a directory before visiting
+ * its children. */
+ apr_hash_set(fe_baton->tree_conflicts, local_abspath, APR_HASH_KEY_STRING,
+ NULL);
+
return SVN_NO_ERROR;
}
+/* Return TRUE iff the subtree at ROOT_ABSPATH, restricted to depth DEPTH,
+ * would include the path CHILD_ABSPATH of kind CHILD_KIND. */
+static svn_boolean_t
+depth_includes(const char *root_abspath,
+ svn_depth_t depth,
+ const char *child_abspath,
+ svn_node_kind_t child_kind,
+ apr_pool_t *scratch_pool)
+{
+ const char *parent_abspath = svn_dirent_dirname(child_abspath, scratch_pool);
+
+ return (depth == svn_depth_infinity
+ || ((depth == svn_depth_immediates
+ || (depth == svn_depth_files && child_kind == svn_node_file))
+ && strcmp(root_abspath, parent_abspath) == 0)
+ || strcmp(root_abspath, child_abspath) == 0);
+}
+
+
svn_error_t *
svn_wc__get_info(svn_wc_context_t *wc_ctx,
const char *local_abspath,
@@ -327,11 +355,24 @@ svn_wc__get_info(svn_wc_context_t *wc_ct
apr_pool_t *scratch_pool)
{
struct found_entry_baton fe_baton;
+ const svn_wc_conflict_description2_t *root_tree_conflict;
svn_error_t *err;
+ apr_pool_t *iterpool;
+ apr_hash_index_t *hi;
fe_baton.receiver = receiver;
fe_baton.receiver_baton = receiver_baton;
fe_baton.db = wc_ctx->db;
+ fe_baton.tree_conflicts = apr_hash_make(scratch_pool);
+
+ SVN_ERR(svn_wc__db_op_read_tree_conflict(&root_tree_conflict,
+ wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (root_tree_conflict)
+ {
+ apr_hash_set(fe_baton.tree_conflicts, local_abspath, APR_HASH_KEY_STRING,
+ root_tree_conflict);
+ }
err = svn_wc__internal_walk_children(wc_ctx->db, local_abspath,
FALSE /* show_hidden */,
@@ -342,18 +383,46 @@ svn_wc__get_info(svn_wc_context_t *wc_ct
scratch_pool);
/* If the target root node is not present, svn_wc__internal_walk_children()
- returns a PATH_NOT_FOUND error and doesn't call the callback. In this
- case, check for a tree conflict on this node, and if there
- is one, send a minimal info struct. */
- if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ returns a PATH_NOT_FOUND error and doesn't call the callback. If there
+ is a tree conflict on this node, that is not an error. */
+ if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND && root_tree_conflict)
+ svn_error_clear(err);
+ else if (err)
+ return svn_error_return(err);
+
+ /* If there are any tree conflicts that we have found but have not reported,
+ * send a minimal info struct for each one now. */
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, fe_baton.tree_conflicts); hi;
+ hi = apr_hash_next(hi))
{
- svn_error_clear(err);
+ const char *this_abspath = svn__apr_hash_index_key(hi);
+ const svn_wc_conflict_description2_t *tree_conflict
+ = svn__apr_hash_index_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ if (depth_includes(local_abspath, depth, tree_conflict->local_abspath,
+ tree_conflict->kind, iterpool))
+ {
+ apr_array_header_t *conflicts = apr_array_make(iterpool,
+ 1, sizeof(const svn_wc_conflict_description2_t *));
+ svn_info2_t *info;
+
+ SVN_ERR(build_info_for_unversioned(&info, iterpool));
+ SVN_ERR(svn_wc__internal_get_repos_info(&(info->repos_root_URL),
+ NULL,
+ fe_baton.db,
+ local_abspath, FALSE, FALSE,
+ iterpool, iterpool));
+ APR_ARRAY_PUSH(conflicts, const svn_wc_conflict_description2_t *)
+ = tree_conflict;
+ info->wc_info->conflicts = conflicts;
- SVN_ERR(info_found_node_callback(local_abspath, svn_node_none, &fe_baton,
- scratch_pool));
+ SVN_ERR(receiver(receiver_baton, this_abspath, info, iterpool));
+ }
}
- else if (err)
- return svn_error_return(err);
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
Modified: subversion/trunk/subversion/tests/cmdline/info_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/info_tests.py?rev=1099956&r1=1099955&r2=1099956&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/info_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/info_tests.py Thu May 5 20:25:27 2011
@@ -191,22 +191,15 @@ def info_with_tree_conflicts(sbox):
},
)])
- # Check recursive info. Just ensure that all victims are listed.
- exit_code, output, error = svntest.actions.run_and_verify_svn(None, None,
- [], 'info',
- G, '-R')
+ # Check recursive info.
+ expected_infos = [{ 'Path' : re.escape(G) }]
for fname, action, reason in scenarios:
- found = False
- expected = ".*incoming %s.*" % (action)
- for item in output:
- if re.search(expected, item):
- found = True
- break
- if not found:
- raise svntest.verify.SVNUnexpectedStdout(
- "Tree conflict missing in svn info -R output:\n"
- " Expected: '%s'\n"
- " Found: '%s'" % (expected, output))
+ path = os.path.join(G, fname)
+ tree_conflict_re = ".*local %s, incoming %s.*" % (reason, action)
+ expected_infos.append({ 'Path' : re.escape(path),
+ 'Tree conflict' : tree_conflict_re })
+ expected_infos.sort(key=lambda info: info['Path'])
+ svntest.actions.run_and_verify_info(expected_infos, G, '-R')
def info_on_added_file(sbox):
"""info on added file"""