Merge lp:~bzr/bzr/faster-ls into lp:~bzr/bzr/trunk-old

Proposed by Ian Clatworthy
Status: Superseded
Proposed branch: lp:~bzr/bzr/faster-ls
Merge into: lp:~bzr/bzr/trunk-old
Diff against target: 497 lines (has conflicts)
Text conflict in bzrlib/builtins.py
To merge this branch: bzr merge lp:~bzr/bzr/faster-ls
Reviewer Review Type Date Requested Status
John A Meinel Needs Information
Review via email: mp+6670@code.launchpad.net

This proposal has been superseded by a proposal from 2009-06-17.

To post a comment you must log in.
Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote :

This patch makes ls faster. In particular, ls on a historical revision is faster: ls -r-1 on the OpenOffice trunk drops from 62 seconds to 1.3 seconds. Plain ls on a subdirectory gets faster as well, dropping from 3-4 seconds to around 1 second.

As well as improving performance, I've moved most of the ls logic out of builtins.py to ls.py. New unit tests are needed for this new public API. Before I add those, I'd like a reviewer to confirm:

1. the new API looks right
2. my changes to the blackbox tests are correct.

In the latter case, I believe that 'ls dir --from-root' ought to show just the stuff in dir, not everything (as the current test expects).

Revision history for this message
John A Meinel (jameinel) wrote :

I would *like* to see 'ls' work on top of "iter_entries_by_dir()" rather than list_files().

As near as I can tell, the only thing that list_files gives is "kind character" (though perhaps it also does more with unversioned files?)

I'd like to know if you think that is possible, or if it is too different of an api.

I'm not sure about having a file named "ls.py", maybe it's okay. I don't really have a much better name for it.

+# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd

I'm pretty sure 'ls.py' is only (C) 2009 :)

+def ls(tree, outf, from_dir=None, recursive=False, kind=None, unknown=False,
+ versioned=False, ignored=False, verbose=False, null=False, show_ids=False,
+ prefix=None, from_root=False):

^- When I see a function with that many parameters, I wonder if it wouldn't be better implemented as some sort of class... Not saying that is true here, but it seems like it might.

I think we should also think about whether it would be possible to add "bzr ls --added" more easily with this change or not. As near as I can tell, it would be ~ the same effort, so at least it isn't worse.

The main difficulty we've had is that "Versioned" is a state of a single tree, but "Added" is a diff between two trees. So you need an api that can give you the difference among 3 trees:
1) basis_tree
2) current working tree (versioned info)
3) filesystem

Since that gives you "Newly Versioned" "Renamed" "Ignored" etc.

As for the blackbox changes... You changed everything to be '--recurse' to '--no-recurse' (since the default changed), but you didn't add back any "--recurse" tests.

Also, is this the correct behavior:
        self.ls_equals('subdir/b\n'
                       , '--from-root')

I'm thinking it probably is, but I'll note that it is a change in behavior. 'bzr ls --from-root' used to be equivalent to 'bzr ls ../../' until you got to the root. We now don't have a way from a subdirectory to say "ls everything" without giving the full path to the root.

While doing "bzr ls --from-root ." would be an easy way to specify the opposite.

review: Needs Information
Revision history for this message
John A Meinel (jameinel) wrote :

Sorry, I hit 'Save' before I was done.

=== modified file 'bzrlib/transform.py'

I really wonder if this should be in "bzrlib/preview_tree.py". Not that you have to do that.

I noticed you didn't add "bzrlib/tests/test_ls.py". Since the code is now refactored, it would be nice if most of the 'blackbox/tests_ls.py' was actually done as whitebox tests. Again, not required, but if you at least added the test file and some very basic tests, it would be a place that could be obviously expanded later.

So overall, I think it is worth landing something like this.

Revision history for this message
Robert Collins (lifeless) wrote :

Just a couple of comments.

ls.py doesn't seem to know if its UI layer or logic layer.

I'd personally rather have a clear logic layer - something that takes
the needed parameters and provides an iterator over [semi-]structured
data.

then the command can be
for thing in [dols]:
    self.output.write("%s%s\n" % (thing[0], thing[1]))

This would make this useful for GUI's as well.

I think a good home for this would be tree.py, rather than a new python
module, as it is very tree specific code (in fact, in refactoring terms,
the first parameter 'tree' stands out like a ForeignMethod).

-Rob

Revision history for this message
Martin Pool (mbp) wrote :

> This patch makes ls faster. In particular, ls on a historical revision is
> faster: ls -r-1 on the OpenOffice trunk drops from 62 seconds to 1.3 seconds.
> Plain ls on a subdirectory gets faster as well, dropping from 3-4 seconds to
> around 1 second.

Just a meta-comment: also explaining in the cover letter what you changed to do that will help with reviews. People may have comments only on the approach without reading the patches and also it helps verify the patch does what was intended.

Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote :

> Just a couple of comments.
>
> ls.py doesn't seem to know if its UI layer or logic layer.
>
> I'd personally rather have a clear logic layer - something that takes
> the needed parameters and provides an iterator over [semi-]structured
> data.
>
> then the command can be
> for thing in [dols]:
> self.output.write("%s%s\n" % (thing[0], thing[1]))
>
> This would make this useful for GUI's as well.
>
> I think a good home for this would be tree.py, rather than a new python
> module, as it is very tree specific code (in fact, in refactoring terms,
> the first parameter 'tree' stands out like a ForeignMethod).

All good points, though the use case for a GUI ls client isn't really there. (The functionality tends to be provided by far more powerful GUIs like qbrowse and Olive). I'll resubmit this in a less ambitious form, just focusing on the logic bug and performance issue for now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2009-06-11 06:54:33 +0000
+++ bzrlib/builtins.py 2009-06-16 02:37:53 +0000
@@ -2357,42 +2357,32 @@
2357 recursive=False, from_root=False,2357 recursive=False, from_root=False,
2358 unknown=False, versioned=False, ignored=False,2358 unknown=False, versioned=False, ignored=False,
2359 null=False, kind=None, show_ids=False, path=None):2359 null=False, kind=None, show_ids=False, path=None):
23602360 # Validate the command line options
2361 if kind and kind not in ('file', 'directory', 'symlink'):2361 if kind and kind not in ('file', 'directory', 'symlink'):
2362 raise errors.BzrCommandError('invalid kind specified')2362 raise errors.BzrCommandError('invalid kind specified')
2363
2364 if verbose and null:2363 if verbose and null:
2365 raise errors.BzrCommandError('Cannot set both --verbose and --null')2364 raise errors.BzrCommandError('Cannot set both --verbose and --null')
2366 all = not (unknown or versioned or ignored)
2367
2368 selection = {'I':ignored, '?':unknown, 'V':versioned}
2369
2370 if path is None:2365 if path is None:
2371 fs_path = '.'2366 path = '.'
2372 prefix = ''2367 elif from_root:
2373 else:2368 raise errors.BzrCommandError('cannot specify both --from-root'
2374 if from_root:2369 ' and PATH')
2375 raise errors.BzrCommandError('cannot specify both --from-root'2370
2376 ' and PATH')2371 # Get the tree
2377 fs_path = path2372 tree, branch, dir = bzrdir.BzrDir.open_containing_tree_or_branch(path)
2378 prefix = path2373 mutter("ls dir is %s", dir)
2379 tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2380 fs_path)
2381 if from_root:
2382 relpath = u''
2383 elif relpath:
2384 relpath += '/'
2385 if revision is not None or tree is None:2374 if revision is not None or tree is None:
2386 tree = _get_one_revision_tree('ls', revision, branch=branch)2375 tree = _get_one_revision_tree('ls', revision, branch=branch)
23872376
2388 apply_view = False2377 # Calculate the prefix to use
2389 if isinstance(tree, WorkingTree) and tree.supports_views():2378 prefix = None
2390 view_files = tree.views.lookup_view()2379 if from_root:
2391 if view_files:2380 if dir:
2392 apply_view = True2381 prefix = dir + '/'
2393 view_str = views.view_display_str(view_files)2382 elif path != '.':
2394 note("Ignoring files outside view. View is %s" % view_str)2383 prefix = path + '/'
23952384
2385<<<<<<< TREE
2396 tree.lock_read()2386 tree.lock_read()
2397 try:2387 try:
2398 for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):2388 for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
@@ -2436,6 +2426,13 @@
2436 self.outf.write(outstring + '\n')2426 self.outf.write(outstring + '\n')
2437 finally:2427 finally:
2438 tree.unlock()2428 tree.unlock()
2429=======
2430 # Display the files
2431 from bzrlib import ls
2432 ls.ls(tree, self.outf, from_dir=dir, recursive=recursive,
2433 kind=kind, unknown=unknown, versioned=versioned, ignored=ignored,
2434 verbose=verbose, null=null, show_ids=show_ids, prefix=prefix)
2435>>>>>>> MERGE-SOURCE
24392436
24402437
2441class cmd_unknowns(Command):2438class cmd_unknowns(Command):
24422439
=== added file 'bzrlib/ls.py'
--- bzrlib/ls.py 1970-01-01 00:00:00 +0000
+++ bzrlib/ls.py 2009-06-16 02:37:53 +0000
@@ -0,0 +1,115 @@
1# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""List files in a tree."""
18
19
20from bzrlib.lazy_import import lazy_import
21lazy_import(globals(), """
22from bzrlib import (
23 errors,
24 osutils,
25 )
26from bzrlib.trace import mutter, note
27from bzrlib.workingtree import WorkingTree
28""")
29
30
31def ls(tree, outf, from_dir=None, recursive=False, kind=None, unknown=False,
32 versioned=False, ignored=False, verbose=False, null=False, show_ids=False,
33 prefix=None, from_root=False):
34 """List files for a tree.
35
36 If unknown, versioned and ignored are all False, then all are displayed.
37
38 :param tree: the tree to display files for
39 :param outf: the output stream
40 :param from_dir: just from this directory
41 :param recursive: whether to recurse into subdirectories or not
42 :param kind: one of 'file', 'symlink', 'directory' or None for all
43 :param unknown: include unknown files or not
44 :param versioned: include versioned files or not
45 :param ignored: include ignored files or not
46 :param verbose: show file kinds, not just paths
47 :param null: separate entries with null characters instead of newlines
48 :param show_ids: show file_ids or not
49 :param prefix: prefix paths with this string or None for no prefix
50 :param from_root: show paths from the root instead of relative
51 """
52 mutter("ls from: %s" % (from_dir,))
53 # Tell the user if a view if being applied
54 apply_view = False
55 if isinstance(tree, WorkingTree) and tree.supports_views():
56 view_files = tree.views.lookup_view()
57 if view_files:
58 apply_view = True
59 view_str = views.view_display_str(view_files)
60 note("Ignoring files outside view. View is %s" % view_str)
61
62 # Find and display the files
63 all = not (unknown or versioned or ignored)
64 selection = {'I':ignored, '?':unknown, 'V':versioned}
65 #if from_dir and from_root:
66 # prefix = from_dir
67 #else:
68 # prefix = None
69 tree.lock_read()
70 try:
71 for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
72 from_dir=from_dir, recursive=recursive):
73 # Apply additional masking
74 if not all and not selection[fc]:
75 continue
76 if kind is not None and fkind != kind:
77 continue
78 if apply_view:
79 if from_dir:
80 fullpath = osutils.pathjoin(from_dir, fp)
81 else:
82 fullpath = fp
83 try:
84 views.check_path_in_view(tree, fullpath)
85 except errors.FileOutsideView:
86 continue
87
88 # Output the entry
89 kindch = entry.kind_character()
90 if prefix is not None:
91 fp = osutils.pathjoin(prefix, fp)
92 outstring = fp + kindch
93 if verbose:
94 outstring = '%-8s %s' % (fc, outstring)
95 if show_ids and fid is not None:
96 outstring = "%-50s %s" % (outstring, fid)
97 outf.write(outstring + '\n')
98 elif null:
99 outf.write(fp + '\0')
100 if show_ids:
101 if fid is not None:
102 outf.write(fid)
103 outf.write('\0')
104 outf.flush()
105 else:
106 if show_ids:
107 if fid is not None:
108 my_id = fid
109 else:
110 my_id = ''
111 outf.write('%-50s %s\n' % (outstring, my_id))
112 else:
113 outf.write(outstring + '\n')
114 finally:
115 tree.unlock()
0116
=== modified file 'bzrlib/revisiontree.py'
--- bzrlib/revisiontree.py 2009-06-10 03:56:49 +0000
+++ bzrlib/revisiontree.py 2009-06-16 02:37:53 +0000
@@ -114,11 +114,18 @@
114 def has_filename(self, filename):114 def has_filename(self, filename):
115 return bool(self.inventory.path2id(filename))115 return bool(self.inventory.path2id(filename))
116116
117 def list_files(self, include_root=False):117 def list_files(self, include_root=False, from_dir=None, recursive=True):
118 # The only files returned by this are those from the version118 # The only files returned by this are those from the version
119 entries = self.inventory.iter_entries()119 inv = self.inventory
120 # skip the root for compatability with the current apis.120 if from_dir is None:
121 if self.inventory.root is not None and not include_root:121 from_dir_id = None
122 else:
123 from_dir_id = inv.path2id(from_dir)
124 if from_dir_id is None:
125 # Directory not versioned
126 return
127 entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
128 if inv.root is not None and not include_root and from_dir is None:
122 # skip the root for compatability with the current apis.129 # skip the root for compatability with the current apis.
123 entries.next()130 entries.next()
124 for path, entry in entries:131 for path, entry in entries:
125132
=== modified file 'bzrlib/tests/blackbox/test_ls.py'
--- bzrlib/tests/blackbox/test_ls.py 2009-05-08 13:39:32 +0000
+++ bzrlib/tests/blackbox/test_ls.py 2009-06-16 02:37:53 +0000
@@ -129,19 +129,11 @@
129 self.ls_equals('b\n')129 self.ls_equals('b\n')
130 self.ls_equals('b\0'130 self.ls_equals('b\0'
131 , '--null')131 , '--null')
132 self.ls_equals('.bzrignore\n'132 self.ls_equals('subdir/b\n'
133 'a\n'
134 'subdir/\n'
135 'subdir/b\n'
136 , '--from-root')133 , '--from-root')
137 self.ls_equals('.bzrignore\0'134 self.ls_equals('subdir/b\0'
138 'a\0'
139 'subdir\0'
140 'subdir/b\0'
141 , '--from-root --null')135 , '--from-root --null')
142 self.ls_equals('.bzrignore\n'136 self.ls_equals('subdir/b\n'
143 'a\n'
144 'subdir/\n'
145 , '--from-root', recursive=False)137 , '--from-root', recursive=False)
146138
147 def test_ls_path(self):139 def test_ls_path(self):
148140
=== modified file 'bzrlib/tests/tree_implementations/test_list_files.py'
--- bzrlib/tests/tree_implementations/test_list_files.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/tree_implementations/test_list_files.py 2009-06-16 02:37:53 +0000
@@ -49,7 +49,68 @@
49 try:49 try:
50 actual = [(path, status, kind, file_id)50 actual = [(path, status, kind, file_id)
51 for path, status, kind, file_id, ie in51 for path, status, kind, file_id, ie in
52 tree.list_files(include_root=False)]52 tree.list_files()]
53 finally:
54 tree.unlock()
55 self.assertEqual(expected, actual)
56
57 def test_list_files_with_root_no_recurse(self):
58 work_tree = self.make_branch_and_tree('wt')
59 tree = self.get_tree_no_parents_abc_content(work_tree)
60 expected = [('', 'V', 'directory', 'root-id'),
61 ('a', 'V', 'file', 'a-id'),
62 ('b', 'V', 'directory', 'b-id'),
63 ]
64 tree.lock_read()
65 try:
66 actual = [(path, status, kind, file_id)
67 for path, status, kind, file_id, ie in
68 tree.list_files(include_root=True, recursive=False)]
69 finally:
70 tree.unlock()
71 self.assertEqual(expected, actual)
72
73 def test_list_files_no_root_no_recurse(self):
74 work_tree = self.make_branch_and_tree('wt')
75 tree = self.get_tree_no_parents_abc_content(work_tree)
76 expected = [('a', 'V', 'file', 'a-id'),
77 ('b', 'V', 'directory', 'b-id'),
78 ]
79 tree.lock_read()
80 try:
81 actual = [(path, status, kind, file_id)
82 for path, status, kind, file_id, ie in
83 tree.list_files(recursive=False)]
84 finally:
85 tree.unlock()
86 self.assertEqual(expected, actual)
87
88 def test_list_files_from_dir(self):
89 work_tree = self.make_branch_and_tree('wt')
90 tree = self.get_tree_no_parents_abc_content(work_tree)
91 expected = [('c', 'V', 'file', 'c-id'),
92 ]
93 tree.lock_read()
94 try:
95 actual = [(path, status, kind, file_id)
96 for path, status, kind, file_id, ie in
97 tree.list_files(from_dir='b')]
98 finally:
99 tree.unlock()
100 self.assertEqual(expected, actual)
101
102 def test_list_files_from_dir_no_recurse(self):
103 # The test trees don't have much nesting so test with an explicit root
104 work_tree = self.make_branch_and_tree('wt')
105 tree = self.get_tree_no_parents_abc_content(work_tree)
106 expected = [('a', 'V', 'file', 'a-id'),
107 ('b', 'V', 'directory', 'b-id'),
108 ]
109 tree.lock_read()
110 try:
111 actual = [(path, status, kind, file_id)
112 for path, status, kind, file_id, ie in
113 tree.list_files(from_dir='', recursive=False)]
53 finally:114 finally:
54 tree.unlock()115 tree.unlock()
55 self.assertEqual(expected, actual)116 self.assertEqual(expected, actual)
56117
=== modified file 'bzrlib/transform.py'
--- bzrlib/transform.py 2009-06-10 03:56:49 +0000
+++ bzrlib/transform.py 2009-06-16 02:37:53 +0000
@@ -1748,7 +1748,7 @@
1748 if self._transform.final_file_id(trans_id) is None:1748 if self._transform.final_file_id(trans_id) is None:
1749 yield self._final_paths._determine_path(trans_id)1749 yield self._final_paths._determine_path(trans_id)
17501750
1751 def _make_inv_entries(self, ordered_entries, specific_file_ids):1751 def _make_inv_entries(self, ordered_entries, specific_file_ids=None):
1752 for trans_id, parent_file_id in ordered_entries:1752 for trans_id, parent_file_id in ordered_entries:
1753 file_id = self._transform.final_file_id(trans_id)1753 file_id = self._transform.final_file_id(trans_id)
1754 if file_id is None:1754 if file_id is None:
@@ -1791,14 +1791,41 @@
1791 specific_file_ids):1791 specific_file_ids):
1792 yield unicode(self._final_paths.get_path(trans_id)), entry1792 yield unicode(self._final_paths.get_path(trans_id)), entry
17931793
1794 def list_files(self, include_root=False):1794 def _iter_entries_for_dir(self, dir_path):
1795 """See Tree.list_files."""1795 """Return path, entry for items in a directory without recursing down."""
1796 dir_file_id = self.path2id(dir_path)
1797 ordered_ids = []
1798 for file_id in self.iter_children(dir_file_id):
1799 trans_id = self._transform.trans_id_file_id(file_id)
1800 ordered_ids.append((trans_id, file_id))
1801 for entry, trans_id in self._make_inv_entries(ordered_ids):
1802 yield unicode(self._final_paths.get_path(trans_id)), entry
1803
1804 def list_files(self, include_root=False, from_dir=None, recursive=True):
1805 """See WorkingTree.list_files."""
1796 # XXX This should behave like WorkingTree.list_files, but is really1806 # XXX This should behave like WorkingTree.list_files, but is really
1797 # more like RevisionTree.list_files.1807 # more like RevisionTree.list_files.
1798 for path, entry in self.iter_entries_by_dir():1808 if recursive:
1799 if entry.name == '' and not include_root:1809 prefix = None
1800 continue1810 if from_dir:
1801 yield path, 'V', entry.kind, entry.file_id, entry1811 prefix = from_dir + '/'
1812 entries = self.iter_entries_by_dir()
1813 for path, entry in entries:
1814 if entry.name == '' and not include_root:
1815 continue
1816 if prefix:
1817 if not path.startswith(prefix):
1818 continue
1819 path = path[len(prefix):]
1820 yield path, 'V', entry.kind, entry.file_id, entry
1821 else:
1822 if from_dir is None and include_root is True:
1823 root_entry = inventory.make_entry('directory', '',
1824 ROOT_PARENT, self.get_root_id())
1825 yield '', 'V', 'directory', root_entry.file_id, root_entry
1826 entries = self._iter_entries_for_dir(from_dir or '')
1827 for path, entry in entries:
1828 yield path, 'V', entry.kind, entry.file_id, entry
18021829
1803 def kind(self, file_id):1830 def kind(self, file_id):
1804 trans_id = self._transform.trans_id_file_id(file_id)1831 trans_id = self._transform.trans_id_file_id(file_id)
18051832
=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py 2009-06-15 15:47:45 +0000
+++ bzrlib/workingtree.py 2009-06-16 02:37:53 +0000
@@ -1115,15 +1115,16 @@
1115 def _kind(self, relpath):1115 def _kind(self, relpath):
1116 return osutils.file_kind(self.abspath(relpath))1116 return osutils.file_kind(self.abspath(relpath))
11171117
1118 def list_files(self, include_root=False):1118 def list_files(self, include_root=False, from_dir=None, recursive=True):
1119 """Recursively list all files as (path, class, kind, id, entry).1119 """List all files as (path, class, kind, id, entry).
11201120
1121 Lists, but does not descend into unversioned directories.1121 Lists, but does not descend into unversioned directories.
1122
1123 This does not include files that have been deleted in this1122 This does not include files that have been deleted in this
1124 tree.1123 tree. Skips the control directory.
11251124
1126 Skips the control directory.1125 :param include_root: if True, do not return an entry for the root
1126 :param from_dir: start from this directory or None for the root
1127 :param recursive: whether to recurse into subdirectories or not
1127 """1128 """
1128 # list_files is an iterator, so @needs_read_lock doesn't work properly1129 # list_files is an iterator, so @needs_read_lock doesn't work properly
1129 # with it. So callers should be careful to always read_lock the tree.1130 # with it. So callers should be careful to always read_lock the tree.
@@ -1131,7 +1132,7 @@
1131 raise errors.ObjectNotLocked(self)1132 raise errors.ObjectNotLocked(self)
11321133
1133 inv = self.inventory1134 inv = self.inventory
1134 if include_root is True:1135 if from_dir is None and include_root is True:
1135 yield ('', 'V', 'directory', inv.root.file_id, inv.root)1136 yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1136 # Convert these into local objects to save lookup times1137 # Convert these into local objects to save lookup times
1137 pathjoin = osutils.pathjoin1138 pathjoin = osutils.pathjoin
@@ -1144,13 +1145,22 @@
1144 fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}1145 fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
11451146
1146 # directory file_id, relative path, absolute path, reverse sorted children1147 # directory file_id, relative path, absolute path, reverse sorted children
1147 children = os.listdir(self.basedir)1148 if from_dir is not None:
1149 from_dir_id = inv.path2id(from_dir)
1150 if from_dir_id is None:
1151 # Directory not versioned
1152 return
1153 from_dir_abspath = pathjoin(self.basedir, from_dir)
1154 else:
1155 from_dir_id = inv.root.file_id
1156 from_dir_abspath = self.basedir
1157 children = os.listdir(from_dir_abspath)
1148 children.sort()1158 children.sort()
1149 # jam 20060527 The kernel sized tree seems equivalent whether we1159 # jam 20060527 The kernel sized tree seems equivalent whether we
1150 # use a deque and popleft to keep them sorted, or if we use a plain1160 # use a deque and popleft to keep them sorted, or if we use a plain
1151 # list and just reverse() them.1161 # list and just reverse() them.
1152 children = collections.deque(children)1162 children = collections.deque(children)
1153 stack = [(inv.root.file_id, u'', self.basedir, children)]1163 stack = [(from_dir_id, u'', from_dir_abspath, children)]
1154 while stack:1164 while stack:
1155 from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]1165 from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
11561166
@@ -1214,14 +1224,15 @@
1214 if fk != 'directory':1224 if fk != 'directory':
1215 continue1225 continue
12161226
1217 # But do this child first1227 # But do this child first if recursing down
1218 new_children = os.listdir(fap)1228 if recursive:
1219 new_children.sort()1229 new_children = os.listdir(fap)
1220 new_children = collections.deque(new_children)1230 new_children.sort()
1221 stack.append((f_ie.file_id, fp, fap, new_children))1231 new_children = collections.deque(new_children)
1222 # Break out of inner loop,1232 stack.append((f_ie.file_id, fp, fap, new_children))
1223 # so that we start outer loop with child1233 # Break out of inner loop,
1224 break1234 # so that we start outer loop with child
1235 break
1225 else:1236 else:
1226 # if we finished all children, pop it off the stack1237 # if we finished all children, pop it off the stack
1227 stack.pop()1238 stack.pop()
12281239
=== modified file 'bzrlib/workingtree_4.py'
--- bzrlib/workingtree_4.py 2009-06-11 04:23:53 +0000
+++ bzrlib/workingtree_4.py 2009-06-16 02:37:53 +0000
@@ -1844,12 +1844,19 @@
1844 return None1844 return None
1845 return ie.executable1845 return ie.executable
18461846
1847 def list_files(self, include_root=False):1847 def list_files(self, include_root=False, from_dir=None, recursive=True):
1848 # We use a standard implementation, because DirStateRevisionTree is1848 # We use a standard implementation, because DirStateRevisionTree is
1849 # dealing with one of the parents of the current state1849 # dealing with one of the parents of the current state
1850 inv = self._get_inventory()1850 inv = self._get_inventory()
1851 entries = inv.iter_entries()1851 if from_dir is None:
1852 if self.inventory.root is not None and not include_root:1852 from_dir_id = None
1853 else:
1854 from_dir_id = inv.path2id(from_dir)
1855 if from_dir_id is None:
1856 # Directory not versioned
1857 return
1858 entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1859 if inv.root is not None and not include_root and from_dir is None:
1853 entries.next()1860 entries.next()
1854 for path, entry in entries:1861 for path, entry in entries:
1855 yield path, 'V', entry.kind, entry.file_id, entry1862 yield path, 'V', entry.kind, entry.file_id, entry