Ok, I couldn't use os.path.walk, but I've written some python code to
build a tree of the same type I mentioned yesterday out of the actual
files on the disk. You just make a call like this:
tree = svn_tree.build_tree_from_wc(wc-path)
and voila, it works. I've attached a patch to xml_tests.py, which
includes a new test (which passes, yay) using this method, and the new
version of svn_tree.py.
Index: xml_tests.py
===================================================================
RCS file: /cvs/subversion/subversion/tests/clients/cmdline/xml_tests.py,v
retrieving revision 1.2
diff -u -r1.2 xml_tests.py
--- xml_tests.py 2001/04/16 22:18:01 1.2
+++ xml_tests.py 2001/04/19 15:26:30
@@ -158,12 +158,36 @@
return 0 # final success
+#-----------------------------------------------------------------------------
+def xml_test_3():
+ """checkout a wc from co1-inline.xml - 2"""
+
+ import svn_tree
+
+ wc_dir = 'wc-t3'
+
+ # actually do the check out
+ svn_test_main.run_svn ('co', '-d', wc_dir, \
+ '--xml-file', XML_DIR + '/co1-inline.xml', \
+ '-r 1', ANCESTOR_PATH)
+
+
+ exp_tree = svn_tree.build_tree_from_paths(greek_paths)
+ result_tree = svn_tree.build_tree_from_wc(wc_dir)
+
+ if svn_tree.compare_trees(exp_tree, result_tree):
+ return 0
+ else:
+ return 1
+
+
########################################################################
## List all tests here, starting with None:
test_list = [ None,
xml_test_1,
- xml_test_2
+ xml_test_2,
+ xml_test_3
]
## And run the main test routine on them:
----- new file begins here
#!/usr/bin/env python
#
# svn_tree.py: tools for comparing directory trees
#
# Subversion is a tool for revision control.
# See http://subversion.tigris.org for more information.
#
# ====================================================================
# Copyright (c) 2001 Sam Tobin-Hochstadt. All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
######################################################################
# A node in the directory tree.
#
# If CHILDREN is None, then the node is a file.
# Otherwise, CHILDREN is a list of the nodes making up that
# directorie's children.
#
# NAME is simply the name of the file of directory
class SVNTreeNode:
def __init__(self, name, children=None):
self.name = name
self.children = children
def __cmp__(self, other):
"Allows comparison on trees. Only compares the names."
# could this be rewritten as 'return
# self.name.__cmp__(other.name)' ??
if self.name == other.name:
return 0
if self.name > other.name:
return 1
else:
return -1
def add_child(self, newchild):
"""Add a child to a node. If the child already exists. merget
the new node's children with the current child node's
children. The merge is done recursively."""
if self.children is None:
self.children = []
n = 0
for a in self.children:
if a.name == newchild.name:
n = 1
break
if n == 0:
self.children.append(newchild)
elif not (newchild.children is None):
for i in newchild.children:
a.add_child(i)
# reserved name of the root of the tree
root_node_name = "__SVN_ROOT_NODE"
# Exception raised if you screw up in this module.
class SVNTreeError(Exception): pass
# Exception raised if two trees are unequal
class SVNTreeUnequal(Exception): pass
########################################################################
# Code for handling creating trees from lists of pathnames. This will
# most likely be used for generating expected trees. Also useful for
# handling the output of SVN commands
#
def create_from_path(path):
"""Create and return a new tree, given a path representing a single
entry into that tree."""
# internal use only
# get a list of all the names in the path
# each of these will be a child of the former
elements = path.split("/")
if len(elements) == 0:
raise SVNTreeError
root_node = SVNTreeNode(elements[0], None)
add_elements_as_path(root_node, elements[1:])
return root_node
def add_elements_as_path(top_node, path_list):
"""Add the elements in PATH as if they were a single path below
TOP_NODE."""
# The idea of this function is to take a list like so:
# ['A', 'B', 'C'] and a top node, say 'Z', and generate a tree
# like this:
#
# Z -> A -> B -> C
#
# where 1 -> 2 means 2 is a child of 1.
#
# It's sort of hard to describe clearly, but it's quite useful.
prev_node = top_node
for i in path_list:
new_node = SVNTreeNode(i, None)
prev_node.add_child(new_node)
prev_node = new_node
def build_tree_from_paths(paths):
"Take PATHS and create a tree from them."
root = SVNTreeNode(root_node_name, None)
for i in paths:
root.add_child(create_from_path(i))
return root
#######################################################################
# Code for creating trees from the actual on-disk files. This is the
# most fundamental result, and (obviously) the most important for SVN
# to get right.
#
import os.path # lots of stuff
import os
import copy
def handle_dir(path, current_parent):
# get a list of all the files
all_files = os.listdir(path)
files = []
dirs = []
# put dirs and files in their own lists, and remove SVN dirs
for f in all_files:
f = os.path.join(path, f)
if os.path.isdir(f) and os.path.basename(f) != 'SVN':
dirs.append(f)
elif os.path.isfile(f):
files.append(f)
# add each file as a child of CURRENT_PARENT
for f in files:
current_parent.add_child(SVNTreeNode(os.path.basename(f)))
# for each subdir, create a node, walk its tree, add it as a child
for d in dirs:
new_dir_node = SVNTreeNode(os.path.basename(d))
handle_dir(d, new_dir_node)
current_parent.add_child(new_dir_node)
def build_tree_from_wc(wc_path):
"""Takes WC_PATH as the path to a working copy. Walks the tree below
that path, and creates the tree based on the actual found
files."""
root = SVNTreeNode(root_node_name, None)
# Walk the tree recursively
handle_dir(os.path.normpath(wc_path), root)
return root
#######################################################################
# Code for handling trees generally.
#
def compare_trees(a, b):
"""Check whether A and B are the equal as trees. This comparison
does not take into account the order of children."""
try:
if a.name != b.name:
print "Error: %s is not the same as %s." % (a.name, b.name)
raise SVNTreeUnequal
if a.children is None:
if b.children is None:
# They are both files with the same name, and so are equal.
return 1
else:
# A is a file and B is a dir.
raise SVNTreeUnequal
if b.children is None:
# B is a file and A is a dir.
raise SVNTreeUnequal
# order of children doesn't matter
a.children.sort()
b.children.sort()
for i in range(max(len(a.children), len(b.children))):
compare_trees(a.children[i], b.children[i])
except IndexError:
# We ran off the end of one of the lists
print "Error: unequal number of children"
# Propogate the error upward
raise SVNTreeUnequal
except SVNTreeUnequal:
if a.name == root_node_name:
return 0
else:
# TODO Some nice pretty-printing of structure here
print "Unequal at node %s" % a.name
raise SVNTreeUnequal
return 1
def dump_tree(n,indent=""):
"Print out a nice representation of the tree."
# Code partially stolen from Dave Beazley
# We have to take the len(n.children), so we use a substitute.
if n.children is None:
tmp_children = []
else:
tmp_children = n.children
if n.name == root_node_name:
print "%s%s" % (indent, "Top of the tree")
else:
print "%s%s" % (indent, n.name)
# Shift the lines over
indent = indent.replace("-"," ")
indent = indent.replace("+"," ")
for i in range(len(tmp_children)):
c = tmp_children[i]
if i == len(tmp_children)-1:
# We're at the end of the list
dump_tree(c,indent + " +-- ")
else:
dump_tree(c,indent + " |-- ")
#########################################
## Test code -- to be removed
greek_paths = ['iota', 'A', 'A/mu', 'A/B', 'A/B/lambda', 'A/B/E',
'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 'A/C', 'A/D',
'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
'A/D/H', 'A/D/H/chi', 'A/D/H/psi', 'A/D/H/omega']
greek_paths2 = ['iota', 'A', 'A/mu', 'A/B', 'A/B/lambda', 'A/B/E',
'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 'A/C', 'A/D',
'A/D/H', 'A/D/H/chi', 'A/D/H/psi', 'A/D/H/omega']
greek_paths3 = ['iota', 'A', 'A/mu', 'A/B', 'A/B/lambda', 'A/B/E',
'A/D/gamma', 'A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 'A/C', 'A/D',
'A/D/H', 'A/D/H/chi', 'A/D/H/psi', 'A/D/H/omegafoo']
if __name__ == '__main__':
# We were invoked on the command line
test_tree = build_tree_from_paths(greek_paths)
test_tree2 = build_tree_from_paths(greek_paths2)
test_tree3 = build_tree_from_paths(greek_paths3)
dump_tree(test_tree)
dump_tree(test_tree2)
if 1 == compare_trees(test_tree, test_tree3):
print "they are equal"
else:
print "they are not equal"
### End of file.
# local variables:
# eval: (load-file "../../../svn-dev.el")
# end:
sam th --- sam_at_uchicago.edu --- http://www.abisource.com/~sam/
OpenPGP Key: CABD33FC --- http://samth.dyndns.org/key
DeCSS: http://samth.dyndns.org/decss
- application/pgp-signature attachment: stored
Received on Sat Oct 21 14:36:29 2006