Merge lp:~ajeans/bzr/status-callback into lp:bzr

Proposed by Arnaud Jeansen
Status: Merged
Approved by: Martin Pool
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~ajeans/bzr/status-callback
Merge into: lp:bzr
Diff against target: 439 lines (+181/-135)
4 files modified
bzrlib/delta.py (+104/-104)
bzrlib/log.py (+9/-5)
bzrlib/status.py (+61/-19)
bzrlib/tests/test_delta.py (+7/-7)
To merge this branch: bzr merge lp:~ajeans/bzr/status-callback
Reviewer Review Type Date Requested Status
Martin Pool Approve
bzr-core Pending
Review via email: mp+20828@code.launchpad.net

Commit message

(mbp, for ajeans) refactor status code

Description of the change

As explained on the mailing list, the "status" command output is implemented inside TreeDelta, which makes customization difficult (see https://lists.ubuntu.com/archives/bazaar/2010q1/067157.html )
This branch is a simple effort to :
- relocate the output from delta.py to status.py and pass the behaviour to TreeDelta with a callback
- the output code handles normal and short style, but the short style is never called (and would not pass the unit tests if called). As reported on the list, the short style is handled by the _ChangeReporter class (see https://lists.ubuntu.com/archives/bazaar/2010q1/067271.html )

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

Nice cleanup, thanks very much.

Can you please execute our contributor agreement at <http://www.canonical.com/contributors>?

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

agreement received

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

This seems to fail like so:

Traceback (most recent call last):
 File "/usr/lib/python2.4/site-packages/testtools/runtest.py", line 128, in _run_user
   return fn(*args)
 File "/usr/lib/python2.4/site-packages/testtools/testcase.py", line 368, in _run_test_method
   testMethod()
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/blackbox/test_log.py", line 424, in test_log_long_verbose
   self.assertUseLongDeltaFormat(['log', '--long', '-v'])
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/blackbox/test_log.py", line 410, in assertUseLongDeltaFormat
   log = self.run_bzr(cmd)[0]
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1842, in run_bzr
   working_dir=working_dir,
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1741, in _run_bzr_autosplit
   encoding=encoding, stdin=stdin, working_dir=working_dir,
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1775, in _run_bzr_core
   args)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 2080, in apply_redirected
   return a_callable(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1146, in run_bzr_catch_user_errors
   return run_bzr(argv)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1039, in run_bzr
   ret = run(*run_argv)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 643, in run_argv_aliases
   return self.run_direct(**all_cmd_args)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 647, in run_direct
   return self._operation.run_simple(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 121, in run_simple
   return _do_with_cleanups(
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 156, in _do_with_cleanups
   result = func(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1054, in ignore_pipe
   result = func(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/builtins.py", line 2412, in run
   Logger(b, rqst).show(lf)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 335, in show
   self._show_body(lf)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 360, in _show_body
   lf.log_revision(lr)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 1521, in log_revision
   revision.delta.show(to_file, self.show_ids, indent=indent,
AttributeError: 'TreeDelta' object has no attribute 'show'

Revision history for this message
Arnaud Jeansen (ajeans) wrote :

> This seems to fail like so:
>
> Traceback (most recent call last):
> File "/usr/lib/python2.4/site-packages/testtools/runtest.py", line 128, in
> _run_user
> return fn(*args)
> File "/usr/lib/python2.4/site-packages/testtools/testcase.py", line 368, in
> _run_test_method
> testMethod()
> File "/home/pqm/bzr-pqm-
> workdir/home/+trunk/bzrlib/tests/blackbox/test_log.py", line 424, in
> test_log_long_verbose
> self.assertUseLongDeltaFormat(['log', '--long', '-v'])
> File "/home/pqm/bzr-pqm-
> workdir/home/+trunk/bzrlib/tests/blackbox/test_log.py", line 410, in
> assertUseLongDeltaFormat
> log = self.run_bzr(cmd)[0]
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line
> 1842, in run_bzr
> working_dir=working_dir,
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line
> 1741, in _run_bzr_autosplit
> encoding=encoding, stdin=stdin, working_dir=working_dir,
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line
> 1775, in _run_bzr_core
> args)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line
> 2080, in apply_redirected
> return a_callable(*args, **kwargs)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1146,
> in run_bzr_catch_user_errors
> return run_bzr(argv)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1039,
> in run_bzr
> ret = run(*run_argv)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 643, in
> run_argv_aliases
> return self.run_direct(**all_cmd_args)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 647, in
> run_direct
> return self._operation.run_simple(*args, **kwargs)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 121, in
> run_simple
> return _do_with_cleanups(
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 156, in
> _do_with_cleanups
> result = func(*args, **kwargs)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1054,
> in ignore_pipe
> result = func(*args, **kwargs)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/builtins.py", line 2412,
> in run
> Logger(b, rqst).show(lf)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 335, in show
> self._show_body(lf)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 360, in
> _show_body
> lf.log_revision(lr)
> File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/log.py", line 1521, in
> log_revision
> revision.delta.show(to_file, self.show_ids, indent=indent,
> AttributeError: 'TreeDelta' object has no attribute 'show'

Martin,

I didn't see that "log" also called the TreeDelta.show(). It is probably used to do "log -v" and I didn't run the log unit tests (I only checked that all the status unit tests still passed).

Can you please revert the change for now, I will look at porting the log code as soon as I can, and update the merge proposal when it is done.

Sorry for the problem.
Arnaud

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

No need to revert, this is just not merged for now. See if you can get it to pass the log tests in your branch, then push to Launchpad again and add a note on this proposal. If you want help fixing the tests just ask.

Revision history for this message
Arnaud Jeansen (ajeans) wrote :

> No need to revert, this is just not merged for now. See if you can get it to
> pass the log tests in your branch, then push to Launchpad again and add a note
> on this proposal. If you want help fixing the tests just ask.

Martin,
It is impressive to see pre_merge hooks in action. At least I do not have to feel too guilty if I break something. :)

I updated the branch and tested it with a bigger subset of unit tests (including the one that failed). I also grepped through the tree to make sure I left nothing behind.
I'll let the whole test suite run on my laptop overnight, but last time I tried, it core dumped in the middle of it so I am not entirely sure that it will be useful.

As for the code, its only goal now is to expose status output as a callback (the dead code was not dead yet). However, I will probably have to file a bug regarding the different short outputs between status and log :
status uses ChangeReporter on a change_iterator whereas log uses a method on a TreeDelta. And they disagree on a few things (is short for 'added' a 'N' or a 'A', ...)

Thanks for your patience.

Revision history for this message
Arnaud Jeansen (ajeans) wrote :

The test suite on my computer is definitely not working well. It core dumped during the night because of a lack of free memory.

Aborted (core dumped), 854 failed] bzrlib.plugins.svn.tests.test_config.SvnBpCo

Among the tests at the beginning, some fail saying that they can't operate on a bare repo, and others saying that they couldn't create a new thread.

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

On 20 March 2010 18:33, Arnaud Jeansen <email address hidden> wrote:
> The test suite on my computer is definitely not working well. It core dumped during the night because of a lack of free memory.
>
> Aborted (core dumped), 854 failed] bzrlib.plugins.svn.tests.test_config.SvnBpCo
>
> Among the tests at the beginning, some fail saying that they can't operate on a bare repo, and others saying that they couldn't create a new thread.

If I were you I would try running with 'bzr --no-plugins selftest',
and also just selectively run the tests with 'bzr selftest log'.

If you get stuck just ask.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Arnaud Jeansen (ajeans) wrote :

Martin,

I ran "./bzr --no-plugins selftest", which finished on the branch (yay) but with 612 errors (all of those due to no new thread (apparently due to thread leaks in
bzrlib.tests.blackbox.test_branch.TestBranchStacked.test_branch_stacked_from_smart_server).
None of the failures look related to the changes here.

I locally tested with the following:
./bzr selftest -v -s bzrlib.tests.test_status.TestStatus -s bzrlib.tests.blackbox.test_status.CheckoutStatus -s bzrlib.tests.blackbox.test_status.BranchStatus -s bzrlib.tests.blackbox.test_status.TestStatus -s bzrlib.tests.blackbox.test_status.TestStatusEncodings -s bzrlib.tests.test_delta.TestReportChanges -s bzrlib.tests.test_delta.TestDeltaShow -s bzrlib.tests.blackbox.test_log.TestLogVerbose -s bzrlib.tests.test_errors.TestErrors

This works fine here.

Martin, do you think this is enough ?

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

This looks reasonable to me.

review: Approve
Revision history for this message
Martin Pool (mbp) wrote :
Download full text (3.1 KiB)

fails again, sorry:

(it's not immediately obvious to me why your code would fail here)

Traceback (most recent call last):
 File "/usr/lib/python2.4/site-packages/testtools/runtest.py", line 128, in _run_user
   return fn(*args)
 File "/usr/lib/python2.4/site-packages/testtools/testcase.py", line 368, in _run_test_method
   testMethod()
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/blackbox/test_branch.py", line 301, in test_branch_stacked
   out, err = self.run_bzr(['branch', '--stacked', 'mainline',
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1844, in run_bzr
   working_dir=working_dir,
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1743, in _run_bzr_autosplit
   encoding=encoding, stdin=stdin, working_dir=working_dir,
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 1777, in _run_bzr_core
   args)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/__init__.py", line 2082, in apply_redirected
   return a_callable(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1190, in run_bzr_catch_user_errors
   return run_bzr(argv)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 1082, in run_bzr
   ret = run(*run_argv)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 686, in run_argv_aliases
   return self.run_direct(**all_cmd_args)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/commands.py", line 690, in run_direct
   return self._operation.run_simple(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 121, in run_simple
   return _do_with_cleanups(
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/cleanup.py", line 156, in _do_with_cleanups
   result = func(*args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/builtins.py", line 1264, in run
   source_branch=br_from)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/bzrdir.py", line 1214, in sprout
   revision_id=revision_id, repository_policy=repository_policy)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/decorators.py", line 140, in read_locked
   result = unbound(self, *args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/branch.py", line 1168, in sprout
   repository_policy.configure_branch(result)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/bzrdir.py", line 3626, in configure_branch
   branch.set_stacked_on_url(stack_on)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/decorators.py", line 194, in write_locked
   result = unbound(self, *args, **kwargs)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/branch.py", line 705, in set_stacked_on_url
   self._activate_fallback_location(url)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/branch.py", line 108, in _activate_fallback_location
   repo = self._get_fallback_repository(url)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/branch.py", line 219, in _get_fallback_repository
   url = urlutils.join(self.base, url)
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/urlutils.py", line 122, in join
   path = base_split.path
AttributeError: 'tuple' object has ...

Read more...

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

uh, it's unobvious because I pasted the wrong failure; it should be

Traceback (most recent call last):
 File "/usr/lib/python2.4/site-packages/testtools/runtest.py", line 128, in _run_user
   return fn(*args)
 File "/usr/lib/python2.4/site-packages/testtools/testcase.py", line 368, in _run_test_method
   testMethod()
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/test_delta.py", line 246, in test_kind_change
   self.show_string(delta))
 File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/test_delta.py", line 224, in show_string
   delta.show(to_file, *args, **kwargs)
AttributeError: 'TreeDelta' object has no attribute 'show'

Revision history for this message
Arnaud Jeansen (ajeans) wrote :

Martin,

Well this is a lot more painful than I expected for such a small change. I hope I am not taking too much of your time with the failed merge attempts...

Anyway, sorry for the slip, I didn't call bzrlib.tests.test_delta.TestChangesFrom in my tests. The fix is a one liner in test_delta.

Tested with
$./bzr selftest -v -s bzrlib.tests.test_status.TestStatus \
-s bzrlib.tests.blackbox.test_status.CheckoutStatus \
-s bzrlib.tests.blackbox.test_status.BranchStatus \
-s bzrlib.tests.blackbox.test_status.TestStatus \
-s bzrlib.tests.blackbox.test_status.TestStatusEncodings \
-s bzrlib.tests.test_delta.TestReportChanges \
-s bzrlib.tests.test_delta.TestChangesFrom \
-s bzrlib.tests.test_delta.TestDeltaShow \
-s bzrlib.tests.blackbox.test_log.TestLogVerbose \
-s bzrlib.tests.test_errors.TestErrors
[...]
----------------------------------------------------------------------
Ran 147 tests in 10.011s

OK

Can you feed it again to pqm ? I grepped for more ".show(" calls, and nothing more turned up. It *should* be ok.

Thanks,
Arnaud

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

that's ok, thanks for your persistence

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/delta.py'
2--- bzrlib/delta.py 2010-02-17 17:11:16 +0000
3+++ bzrlib/delta.py 2010-03-30 17:54:35 +0000
4@@ -107,112 +107,11 @@
5 return True
6 return False
7
8-
9- def show(self, to_file, show_ids=False, show_unchanged=False,
10- short_status=False, indent='',
11- filter=None):
12- """Output this delta in status-like form to to_file.
13-
14- :param to_file: A file-like object where the output is displayed.
15-
16- :param show_ids: Output the file ids if True.
17-
18- :param show_unchanged: Output the unchanged files if True.
19-
20- :param short_status: Single-line status if True.
21-
22- :param indent: Added at the beginning of all output lines (for merged
23- revisions).
24-
25- :param filter: A callable receiving a path and a file id and
26- returning True if the path should be displayed.
27- """
28-
29- def decorate_path(path, kind, meta_modified=None):
30- if kind == 'directory':
31- path += '/'
32- elif kind == 'symlink':
33- path += '@'
34- if meta_modified:
35- path += '*'
36- return path
37-
38- def show_more_renamed(item):
39- (oldpath, file_id, kind,
40- text_modified, meta_modified, newpath) = item
41- dec_new_path = decorate_path(newpath, kind, meta_modified)
42- to_file.write(' => %s' % dec_new_path)
43- if text_modified or meta_modified:
44- extra_modified.append((newpath, file_id, kind,
45- text_modified, meta_modified))
46-
47- def show_more_kind_changed(item):
48- (path, file_id, old_kind, new_kind) = item
49- to_file.write(' (%s => %s)' % (old_kind, new_kind))
50-
51- def show_path(path, file_id, kind, meta_modified,
52- default_format, with_file_id_format):
53- dec_path = decorate_path(path, kind, meta_modified)
54- if show_ids:
55- to_file.write(with_file_id_format % dec_path)
56- else:
57- to_file.write(default_format % dec_path)
58-
59- def show_list(files, long_status_name, short_status_letter,
60- default_format='%s', with_file_id_format='%-30s',
61- show_more=None):
62- if files:
63- header_shown = False
64- if short_status:
65- prefix = short_status_letter
66- else:
67- prefix = ''
68- prefix = indent + prefix + ' '
69-
70- for item in files:
71- path, file_id, kind = item[:3]
72- if (filter is not None and not filter(path, file_id)):
73- continue
74- if not header_shown and not short_status:
75- to_file.write(indent + long_status_name + ':\n')
76- header_shown = True
77- meta_modified = None
78- if len(item) == 5:
79- meta_modified = item[4]
80-
81- to_file.write(prefix)
82- show_path(path, file_id, kind, meta_modified,
83- default_format, with_file_id_format)
84- if show_more is not None:
85- show_more(item)
86- if show_ids:
87- to_file.write(' %s' % file_id)
88- to_file.write('\n')
89-
90- show_list(self.removed, 'removed', 'D')#
91- show_list(self.added, 'added', 'A')
92- extra_modified = []
93- # Reorder self.renamed tuples so that all lists share the same
94- # order for their 3 first fields and that they also begin like
95- # the self.modified tuples
96- renamed = [(p, i, k, tm, mm, np)
97- for p, np, i, k, tm, mm in self.renamed]
98- show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
99- show_more=show_more_renamed)
100- show_list(self.kind_changed, 'kind changed', 'K',
101- with_file_id_format='%s',
102- show_more=show_more_kind_changed)
103- show_list(self.modified + extra_modified, 'modified', 'M')
104- if show_unchanged:
105- show_list(self.unchanged, 'unchanged', 'S')
106-
107- show_list(self.unversioned, 'unknown', ' ')
108-
109 def get_changes_as_text(self, show_ids=False, show_unchanged=False,
110- short_status=False):
111+ short_status=False):
112 import StringIO
113 output = StringIO.StringIO()
114- self.show(output, show_ids, show_unchanged, short_status)
115+ report_delta(output, self, short_status, show_ids, show_unchanged)
116 return output.getvalue()
117
118
119@@ -393,7 +292,6 @@
120 self.output("%s%s%s %s%s", rename, self.modified_map[modified], exe,
121 old_path, path)
122
123-
124 def report_changes(change_iterator, reporter):
125 """Report the changes from a change iterator.
126
127@@ -437,3 +335,105 @@
128 versioned_change = versioned_change_map[versioned]
129 reporter.report(file_id, path, versioned_change, renamed, modified,
130 exe_change, kind)
131+
132+def report_delta(to_file, delta, short_status=False, show_ids=False,
133+ show_unchanged=False, indent='', filter=None):
134+ """Output this delta in status-like form to to_file.
135+
136+ :param to_file: A file-like object where the output is displayed.
137+
138+ :param delta: A TreeDelta containing the changes to be displayed
139+
140+ :param short_status: Single-line status if True.
141+
142+ :param show_ids: Output the file ids if True.
143+
144+ :param show_unchanged: Output the unchanged files if True.
145+
146+ :param indent: Added at the beginning of all output lines (for merged
147+ revisions).
148+
149+ :param filter: A callable receiving a path and a file id and
150+ returning True if the path should be displayed.
151+ """
152+
153+ def decorate_path(path, kind, meta_modified=None):
154+ if kind == 'directory':
155+ path += '/'
156+ elif kind == 'symlink':
157+ path += '@'
158+ if meta_modified:
159+ path += '*'
160+ return path
161+
162+ def show_more_renamed(item):
163+ (oldpath, file_id, kind,
164+ text_modified, meta_modified, newpath) = item
165+ dec_new_path = decorate_path(newpath, kind, meta_modified)
166+ to_file.write(' => %s' % dec_new_path)
167+ if text_modified or meta_modified:
168+ extra_modified.append((newpath, file_id, kind,
169+ text_modified, meta_modified))
170+
171+ def show_more_kind_changed(item):
172+ (path, file_id, old_kind, new_kind) = item
173+ to_file.write(' (%s => %s)' % (old_kind, new_kind))
174+
175+ def show_path(path, file_id, kind, meta_modified,
176+ default_format, with_file_id_format):
177+ dec_path = decorate_path(path, kind, meta_modified)
178+ if show_ids:
179+ to_file.write(with_file_id_format % dec_path)
180+ else:
181+ to_file.write(default_format % dec_path)
182+
183+ def show_list(files, long_status_name, short_status_letter,
184+ default_format='%s', with_file_id_format='%-30s',
185+ show_more=None):
186+ if files:
187+ header_shown = False
188+ if short_status:
189+ prefix = short_status_letter
190+ else:
191+ prefix = ''
192+ prefix = indent + prefix + ' '
193+
194+ for item in files:
195+ path, file_id, kind = item[:3]
196+ if (filter is not None and not filter(path, file_id)):
197+ continue
198+ if not header_shown and not short_status:
199+ to_file.write(indent + long_status_name + ':\n')
200+ header_shown = True
201+ meta_modified = None
202+ if len(item) == 5:
203+ meta_modified = item[4]
204+
205+ to_file.write(prefix)
206+ show_path(path, file_id, kind, meta_modified,
207+ default_format, with_file_id_format)
208+ if show_more is not None:
209+ show_more(item)
210+ if show_ids:
211+ to_file.write(' %s' % file_id)
212+ to_file.write('\n')
213+
214+ show_list(delta.removed, 'removed', 'D')
215+ show_list(delta.added, 'added', 'A')
216+ extra_modified = []
217+ # Reorder delta.renamed tuples so that all lists share the same
218+ # order for their 3 first fields and that they also begin like
219+ # the delta.modified tuples
220+ renamed = [(p, i, k, tm, mm, np)
221+ for p, np, i, k, tm, mm in delta.renamed]
222+ show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
223+ show_more=show_more_renamed)
224+ show_list(delta.kind_changed, 'kind changed', 'K',
225+ with_file_id_format='%s',
226+ show_more=show_more_kind_changed)
227+ show_list(delta.modified + extra_modified, 'modified', 'M')
228+ if show_unchanged:
229+ show_list(delta.unchanged, 'unchanged', 'S')
230+
231+ show_list(delta.unversioned, 'unknown', ' ')
232+
233
234=== modified file 'bzrlib/log.py'
235--- bzrlib/log.py 2010-03-19 10:16:06 +0000
236+++ bzrlib/log.py 2010-03-30 17:54:35 +0000
237@@ -1526,9 +1526,10 @@
238 to_file = self.to_file
239 to_file.write("%s%s\n" % (indent, ('\n' + indent).join(lines)))
240 if revision.delta is not None:
241- # We don't respect delta_format for compatibility
242- revision.delta.show(to_file, self.show_ids, indent=indent,
243- short_status=False)
244+ # Use the standard status output to display changes
245+ from bzrlib.delta import report_delta
246+ report_delta(to_file, revision.delta, short_status=False,
247+ show_ids=self.show_ids, indent=indent)
248 if revision.diff is not None:
249 to_file.write(indent + 'diff:\n')
250 to_file.flush()
251@@ -1597,8 +1598,11 @@
252 to_file.write(indent + offset + '%s\n' % (l,))
253
254 if revision.delta is not None:
255- revision.delta.show(to_file, self.show_ids, indent=indent + offset,
256- short_status=self.delta_format==1)
257+ # Use the standard status output to display changes
258+ from bzrlib.delta import report_delta
259+ report_delta(to_file, revision.delta,
260+ short_status=self.delta_format==1,
261+ show_ids=self.show_ids, indent=indent + offset)
262 if revision.diff is not None:
263 self.show_diff(self.to_exact_file, revision.diff, ' ')
264 to_file.write('\n')
265
266=== modified file 'bzrlib/status.py'
267--- bzrlib/status.py 2010-02-17 17:11:16 +0000
268+++ bzrlib/status.py 2010-03-30 17:54:35 +0000
269@@ -34,6 +34,54 @@
270 # if known, but only if really going to the terminal (not into a file)
271
272
273+def report_changes(to_file, old, new, specific_files,
274+ show_short_reporter, show_long_callback,
275+ short=False, want_unchanged=False,
276+ want_unversioned=False, show_ids=False):
277+ """Display summary of changes.
278+
279+ This compares two trees with regards to a list of files, and delegates
280+ the display to underlying elements.
281+
282+ For short output, it creates an iterator on all changes, and lets a given
283+ reporter display these changes.
284+
285+ For stantard output, it creates a delta of the changes, and forwards it
286+ to a callback
287+
288+ :param to_file: If set, write to this file (default stdout.)
289+ :param old: Start tree for the comparison
290+ :param end: End tree for the comparison
291+ :param specific_files: If set, a list of filenames whose status should be
292+ shown. It is an error to give a filename that is not in the working
293+ tree, or in the working inventory or in the basis inventory.
294+ :param show_short_reporter: Reporter in charge of display for short output
295+ :param show_long_callback: Callback in charge of display for normal output
296+ :param short: If True, gives short SVN-style status lines.
297+ :param want_unchanged: Deprecated parameter. If set, includes unchanged
298+ files.
299+ :param show_ids: If set, includes each file's id.
300+ :param want_unversioned: If False, only shows versioned files.
301+ """
302+
303+ if short:
304+ changes = new.iter_changes(old, want_unchanged, specific_files,
305+ require_versioned=False, want_unversioned=want_unversioned)
306+ _mod_delta.report_changes(changes, show_short_reporter)
307+
308+ else:
309+ delta = new.changes_from(old, want_unchanged=want_unchanged,
310+ specific_files=specific_files,
311+ want_unversioned=want_unversioned)
312+ # filter out unknown files. We may want a tree method for
313+ # this
314+ delta.unversioned = [unversioned for unversioned in
315+ delta.unversioned if not new.is_ignored(unversioned[0])]
316+ show_long_callback(to_file, delta,
317+ show_ids=show_ids,
318+ show_unchanged=want_unchanged)
319+
320+
321 def show_tree_status(wt, show_unchanged=None,
322 specific_files=None,
323 show_ids=False,
324@@ -42,7 +90,8 @@
325 revision=None,
326 short=False,
327 verbose=False,
328- versioned=False):
329+ versioned=False,
330+ show_long_callback=_mod_delta.report_delta):
331 """Display summary of changes.
332
333 By default this compares the working tree to a previous revision.
334@@ -71,6 +120,8 @@
335 :param verbose: If True, show all merged revisions, not just
336 the merge tips
337 :param versioned: If True, only shows versioned files.
338+ :param show_long_callback: A callback: message = show_long_callback(to_file, delta,
339+ show_ids, show_unchanged, indent, filter), only used with the long output
340 """
341 if show_unchanged is not None:
342 warn("show_tree_status with show_unchanged has been deprecated "
343@@ -106,24 +157,15 @@
344 specific_files, nonexistents \
345 = _filter_nonexistent(specific_files, old, new)
346 want_unversioned = not versioned
347- if short:
348- changes = new.iter_changes(old, show_unchanged, specific_files,
349- require_versioned=False, want_unversioned=want_unversioned)
350- reporter = _mod_delta._ChangeReporter(output_file=to_file,
351- unversioned_filter=new.is_ignored)
352- _mod_delta.report_changes(changes, reporter)
353- else:
354- delta = new.changes_from(old, want_unchanged=show_unchanged,
355- specific_files=specific_files,
356- want_unversioned=want_unversioned)
357- # filter out unknown files. We may want a tree method for
358- # this
359- delta.unversioned = [unversioned for unversioned in
360- delta.unversioned if not new.is_ignored(unversioned[0])]
361- delta.show(to_file,
362- show_ids=show_ids,
363- show_unchanged=show_unchanged,
364- short_status=False)
365+
366+ # Reporter used for short outputs
367+ reporter = _mod_delta._ChangeReporter(output_file=to_file,
368+ unversioned_filter=new.is_ignored)
369+ report_changes(to_file, old, new, specific_files,
370+ reporter, show_long_callback,
371+ short=short, want_unchanged=show_unchanged,
372+ want_unversioned=want_unversioned, show_ids=show_ids)
373+
374 # show the new conflicts only for now. XXX: get them from the
375 # delta.
376 conflicts = new.conflicts()
377
378=== modified file 'bzrlib/tests/test_delta.py'
379--- bzrlib/tests/test_delta.py 2009-03-23 14:59:43 +0000
380+++ bzrlib/tests/test_delta.py 2010-03-30 17:54:35 +0000
381@@ -221,7 +221,7 @@
382
383 def show_string(self, delta, *args, **kwargs):
384 to_file = StringIO()
385- delta.show(to_file, *args, **kwargs)
386+ _mod_delta.report_delta(to_file, delta, *args, **kwargs)
387 return to_file.getvalue()
388
389 def test_kind_change(self):
390@@ -307,13 +307,13 @@
391 def test_delta_show_short_status_no_filter(self):
392 d, long_status, short_status = self._get_delta()
393 out = StringIO()
394- d.show(out, short_status=True)
395+ _mod_delta.report_delta(out, d, short_status=True)
396 self.assertEquals(short_status, out.getvalue())
397
398 def test_delta_show_long_status_no_filter(self):
399 d, long_status, short_status = self._get_delta()
400 out = StringIO()
401- d.show(out, short_status=False)
402+ _mod_delta.report_delta(out, d, short_status=False)
403 self.assertEquals(long_status, out.getvalue())
404
405 def test_delta_show_no_filter(self):
406@@ -321,7 +321,7 @@
407 out = StringIO()
408 def not_a_filter(path, file_id):
409 return True
410- d.show(out, short_status=True, filter=not_a_filter)
411+ _mod_delta.report_delta(out, d, short_status=True, filter=not_a_filter)
412 self.assertEquals(short_status, out.getvalue())
413
414 def test_delta_show_short_status_single_file_filter(self):
415@@ -329,7 +329,7 @@
416 out = StringIO()
417 def only_f2(path, file_id):
418 return path == 'f2'
419- d.show(out, short_status=True, filter=only_f2)
420+ _mod_delta.report_delta(out, d, short_status=True, filter=only_f2)
421 self.assertEquals("A f2\n", out.getvalue())
422
423 def test_delta_show_long_status_single_file_filter(self):
424@@ -337,7 +337,7 @@
425 out = StringIO()
426 def only_f2(path, file_id):
427 return path == 'f2'
428- d.show(out, short_status=False, filter=only_f2)
429+ _mod_delta.report_delta(out, d, short_status=False, filter=only_f2)
430 self.assertEquals("added:\n f2\n", out.getvalue())
431
432 def test_delta_show_short_status_single_file_id_filter(self):
433@@ -345,6 +345,6 @@
434 out = StringIO()
435 def only_f2_id(path, file_id):
436 return file_id == 'f2-id'
437- d.show(out, short_status=True, filter=only_f2_id)
438+ _mod_delta.report_delta(out, d, short_status=True, filter=only_f2_id)
439 self.assertEquals("A f2\n", out.getvalue())
440