Merge lp:~piastucki/bzr-xmloutput/xml-shelve-list into lp:bzr-xmloutput

Proposed by Piotr Piastucki
Status: Merged
Approved by: Guillermo Gonzalez
Approved revision: 167
Merged at revision: 169
Proposed branch: lp:~piastucki/bzr-xmloutput/xml-shelve-list
Merge into: lp:bzr-xmloutput
Diff against target: 209 lines (+170/-2)
5 files modified
__init__.py (+2/-1)
cmds.py (+22/-0)
info.py (+1/-1)
shelvexml.py (+44/-0)
tests/test_shelve_xml.py (+101/-0)
To merge this branch: bzr merge lp:~piastucki/bzr-xmloutput/xml-shelve-list
Reviewer Review Type Date Requested Status
Guillermo Gonzalez Approve
Review via email: mp+161167@code.launchpad.net

This proposal supersedes a proposal from 2013-02-27.

Description of the change

The changes in this branch add xmlshelvelist command, an XML version of "bzr shelve --list".

To post a comment you must log in.
Revision history for this message
Guillermo Gonzalez (verterok) wrote : Posted in a previous version of this proposal

The code looks good.
Just one question, any reason to add the specific xmlshelvelist command instead of a command + option like the shelve command, e.g: xmlshelve --list?

Thanks

review: Needs Information
Revision history for this message
Piotr Piastucki (piastucki) wrote : Posted in a previous version of this proposal

Good point. I was also thinking about that. I decided to add a new command as the main purpose of the shelve command is shelving files and --list option seems to be secondary. I think it is a good idea to provide XML versions of the commands that generate complex output and avoid re-implementing commands that modify anything. In this case I could add xmlshelve command that would support only --list option and throw an error otherwise. (or maybe delegate to built-in commands instead?). Instead of adding a very limited xmlshelve command I added xmlshelvelist to make it more obvious what the command actually does. However, I am not fully convinced this is the best apporach either. Please let me know what you think and I will change the code accordingly.

Revision history for this message
Piotr Piastucki (piastucki) wrote : Posted in a previous version of this proposal

> The code looks good.
> Just one question, any reason to add the specific xmlshelvelist command
> instead of a command + option like the shelve command, e.g: xmlshelve --list?
>
> Thanks

Good point. I was also thinking about that. I decided to add a new command as the main purpose of the shelve command is shelving files and --list option seems to be secondary. I think it is a good idea to provide XML versions of the commands that generate complex output and avoid re-implementing commands that modify anything. In this case I could add xmlshelve command that would support only --list option and throw an error otherwise. (or maybe delegate to built-in commands instead?). Instead of adding a very limited xmlshelve command I added xmlshelvelist to make it more obvious what the command actually does. However, I am not fully convinced this is the best apporach either. Please let me know what you think and I will change the code accordingly.

Revision history for this message
Guillermo Gonzalez (verterok) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '__init__.py'
2--- __init__.py 2013-02-12 18:38:52 +0000
3+++ __init__.py 2013-04-26 15:15:34 +0000
4@@ -50,7 +50,8 @@
5 "stop_xmlrpc",
6 "xmllog",
7 "xmlls",
8- "xmltags"]:
9+ "xmltags",
10+ "xmlshelvelist"]:
11 plugin_cmds.register_lazy(
12 "cmd_%s" % cmd, [],
13 "bzrlib.plugins.xmloutput.cmds")
14
15=== modified file 'cmds.py'
16--- cmds.py 2013-02-16 23:15:33 +0000
17+++ cmds.py 2013-04-26 15:15:34 +0000
18@@ -465,3 +465,25 @@
19 self.outf.write('\0')
20 self.outf.write('\n')
21
22+class cmd_xmlshelvelist(Command):
23+ """List shelved changes.
24+ This command lists previously-shelved changes.
25+ """
26+ hidden = True
27+ _see_also = ['unshelve', 'configuration']
28+ takes_options = [
29+ 'directory',
30+ null_option
31+ ]
32+ encoding_type = 'replace'
33+
34+ @display_command
35+ @handle_error_xml
36+ def run(self, *args, **kwargs):
37+ import shelvexml
38+ null = kwargs.pop('null', False)
39+ shelvexml.list_xml(self.outf, *args, **kwargs)
40+ if null:
41+ self.outf.write('\0')
42+ self.outf.write('\n')
43+
44
45=== modified file 'info.py'
46--- info.py 2013-02-12 18:38:52 +0000
47+++ info.py 2013-04-26 15:15:34 +0000
48@@ -20,5 +20,5 @@
49 bzr_plugin_version = (0, 8, 9, 'final', 0)
50
51 bzr_commands = ['xmlannotate', 'xmlinfo', 'xmlmissing', 'xmllog', 'xmlls',
52- 'xmlplugins', 'xmltags', 'xmlversion', 'start-xmlrpc', 'stop-xmlrpc']
53+ 'xmlplugins', 'xmlshelvelist', 'xmltags', 'xmlversion', 'start-xmlrpc', 'stop-xmlrpc']
54
55
56=== added file 'shelvexml.py'
57--- shelvexml.py 1970-01-01 00:00:00 +0000
58+++ shelvexml.py 2013-04-26 15:15:34 +0000
59@@ -0,0 +1,44 @@
60+#!/usr/bin/env python
61+# -*- encoding: utf-8 -*-
62+#
63+# Copyright (C) 2013 Piotr Piastucki
64+#
65+# The code taken from bzrlib is under: Copyright (C) 2005-2007 Canonical Ltd
66+#
67+# This program is free software; you can redistribute it and/or modify
68+# it under the terms of the GNU General Public License as published by
69+# the Free Software Foundation; either version 2 of the License, or
70+# (at your option) any later version.
71+#
72+# This program is distributed in the hope that it will be useful,
73+# but WITHOUT ANY WARRANTY; without even the implied warranty of
74+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
75+# GNU General Public License for more details.
76+#
77+# You should have received a copy of the GNU General Public License
78+# along with this program; if not, write to the Free Software
79+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
80+#
81+
82+"""modified shelve --list command to generate xml output"""
83+
84+import bzrlib
85+from bzrlib import errors
86+from bzrlib.workingtree import WorkingTree
87+from writer import _escape_cdata
88+
89+def list_xml(outf, directory='.'):
90+ tree = WorkingTree.open_containing(directory)[0]
91+ manager = tree.get_shelf_manager()
92+ shelves = manager.active_shelves()
93+ outf.write('<?xml version="1.0" encoding="%s"?>' % \
94+ bzrlib.osutils.get_user_encoding())
95+ outf.write('<shelves>')
96+ for shelf_id in reversed(shelves):
97+ message = manager.get_metadata(shelf_id).get('message')
98+ if message is None:
99+ message = ''
100+ outf.write('<shelf><id>%s</id><message>%s</message></shelf>' % (shelf_id, _escape_cdata(message)))
101+ outf.write('</shelves>')
102+
103+
104
105=== added file 'tests/test_shelve_xml.py'
106--- tests/test_shelve_xml.py 1970-01-01 00:00:00 +0000
107+++ tests/test_shelve_xml.py 2013-04-26 15:15:34 +0000
108@@ -0,0 +1,101 @@
109+# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
110+# Copyright (C) 2013 Piotr Piastucki
111+# -*- coding: utf-8 -*-
112+#
113+# This program is free software; you can redistribute it and/or modify
114+# it under the terms of the GNU General Public License as published by
115+# the Free Software Foundation; either version 2 of the License, or
116+# (at your option) any later version.
117+#
118+# This program is distributed in the hope that it will be useful,
119+# but WITHOUT ANY WARRANTY; without even the implied warranty of
120+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121+# GNU General Public License for more details.
122+#
123+# You should have received a copy of the GNU General Public License
124+# along with this program; if not, write to the Free Software
125+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
126+
127+
128+"""Black-box tests for bzr shelve list."""
129+
130+import os
131+import sys
132+import bzrlib
133+from bzrlib import osutils
134+from bzrlib import shelf
135+from bzrlib.tests.blackbox import ExternalBase
136+from bzrlib.tests import TestCaseInTempDir, TestCaseWithTransport
137+from bzrlib.xml_serializer import elementtree as elementtree
138+fromstring = elementtree.fromstring
139+elementtree_tostring = elementtree.tostring
140+
141+class TestShelveList(ExternalBase):
142+
143+ def prepare_shelves(self):
144+ tree = self.make_branch_and_tree('.')
145+ tree.lock_write()
146+ self.addCleanup(tree.unlock)
147+ self.build_tree(['foo/', 'bar/', 'foo/baz', 'foo/text'])
148+ self.build_tree_contents([('foo/text', 'a\n')])
149+ tree.add(['foo', 'bar', 'foo/baz', 'foo/text'], ['foo-id', 'bar-id', 'baz-id', 'text-id'])
150+ tree.commit('Committed foo')
151+ self.build_tree_contents([('foo/text', 'b\na\nc\n')])
152+ # shelf #1 - content change
153+ creator = shelf.ShelfCreator(tree, tree.basis_tree())
154+ creator.shelve_all()
155+ manager = tree.get_shelf_manager()
156+ manager.shelve_changes(creator, 'message1')
157+ creator.finalize()
158+ tree.rename_one('foo/baz', 'bar/baz')
159+ # shelf #2 - rename
160+ creator = shelf.ShelfCreator(tree, tree.basis_tree())
161+ creator.shelve_all()
162+ manager.shelve_changes(creator, 'message2')
163+ creator.finalize()
164+ return tree
165+
166+ def test_list_all(self):
167+ tree = self.prepare_shelves()
168+ shelves_xml = fromstring(self.run_bzr('xmlshelvelist')[0])
169+ # tree = WorkingTree.open_containing('.')[0]
170+ manager = tree.get_shelf_manager()
171+ shelves = manager.active_shelves()
172+ self.assertEquals(shelves_xml.tag, 'shelves')
173+ self.assertEquals(len(shelves_xml), 2)
174+ self.assertEquals(shelves_xml[0].tag, 'shelf')
175+ self.assertEquals(shelves_xml[0][0].tag, 'id')
176+ self.assertEquals(shelves_xml[0][0].text, '2')
177+ self.assertEquals(shelves_xml[0][1].tag, 'message')
178+ self.assertEquals(shelves_xml[0][1].text, 'message2')
179+ self.assertEquals(shelves_xml[1].tag, 'shelf')
180+ self.assertEquals(shelves_xml[1][0].tag, 'id')
181+ self.assertEquals(shelves_xml[1][0].text, '1')
182+ self.assertEquals(shelves_xml[1][1].tag, 'message')
183+ self.assertEquals(shelves_xml[1][1].text, 'message1')
184+
185+ def test_empty(self):
186+ shelves_xml = fromstring(self.run_bzr('xmlshelvelist')[0])
187+ self.assertEquals(shelves_xml.tag, 'shelves')
188+ self.assertEquals(len(shelves_xml), 0)
189+
190+ def test_shelve_list_outside_branch(self):
191+ # we disable isolation because the error we want to catch is triggered
192+ # outside of the jail
193+ self.disable_directory_isolation()
194+ if sys.platform == "win32":
195+ location = "C:/i/do/not/exist/"
196+ else:
197+ location = "/i/do/not/exist/"
198+ out, err = self.run_bzr('xmlshelvelist --directory='+ location, retcode=3)
199+ self.assertEqual(out, '')
200+ self.assertEqual(
201+ '<?xml version="1.0" encoding="%s"?><error>'
202+ '<class>NotBranchError</class><dict><key>path</key><value>'
203+ '%s</value><key>extra</key><value></value>'
204+ '<key>detail</key><value></value></dict>'
205+ '<message>Not a branch: "%s".</message>'
206+ '</error>' % (osutils.get_user_encoding(),
207+ location, location), err)
208+
209+

Subscribers

People subscribed via source and target branches

to all changes: