Merge lp:~piastucki/bzr-xmloutput/xml-log-fix into lp:bzr-xmloutput
- xml-log-fix
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Guillermo Gonzalez | ||||
Approved revision: | 170 | ||||
Merged at revision: | 172 | ||||
Proposed branch: | lp:~piastucki/bzr-xmloutput/xml-log-fix | ||||
Merge into: | lp:bzr-xmloutput | ||||
Diff against target: |
252 lines (+48/-108) 2 files modified
logxml.py (+26/-92) tests/test_log_xml.py (+22/-16) |
||||
To merge this branch: | bzr merge lp:~piastucki/bzr-xmloutput/xml-log-fix | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guillermo Gonzalez | Approve | ||
Review via email: mp+154886@code.launchpad.net |
Commit message
Description of the change
The branch contains the following changes:
1) fixes for failing tests in test_log_xml.py module
2) refactoring of logxml.py module - since we already have a stack of open tags there does not seem to be any reason to keep additional flags/counters and logic to update them. I removed the counters completely and used the stack to properly close open tags.
3) added escaping for tag names
Changes in 2) should fix 517937 - tets case:
1) bzr branch lp:bzr-eclipse (rev 255)
2) cd bzr-eclispe
3) bzr xmllog --limit 230 org.vcs.
-> malformed XML (http://
4) apply fix
5) bzr xmllog --limit 230 org.vcs.
-> correct XML (http://
- 167. By Piotr Piastucki
-
Fix failing tests in test_log_xml
- 168. By Piotr Piastucki
-
Refactor and simplify logxml.
Remove unneeded counters and flags as having a stack of open tags is enough. - 169. By Piotr Piastucki
-
Make sure merge tag is inside log tag
- 170. By Piotr Piastucki
-
Avoid closing log in case of nested merges
Guillermo Gonzalez (verterok) wrote : | # |
Piotr Piastucki (piastucki) wrote : | # |
Actually I think this is a combination of nested merges, selected resources and --limit option. The issue is observed when some inner merges affecting the given resource are removed from the result due to --limit option.
I was not able to reproduce it with a simple test case but the following scenario is relatively simple:
1) bzr branch lp:bzr-eclipse -r 213
2) cd bzr-eclipse
3) bzr xmllog --include-merged --limit 5 org.vcs.
It looks like a bug in bzr.
Try the following bzr log commands in the above mentioned scenario:
1) bzr log --include-merged --limit 7
2) bzr log --include-merged --limit 5 org.vcs.
See the results attached below and note that revision 211.1.2 is missing in the second case.
I hope this results can be used by some bzr gurus to diagnose the problem.
However, the proposed patch simplifies the code in bzr-xmloutput and IMHO is still worth applying even if the actual issue is fixed somehow in bzr.
1) bzr log --include-merged --limit 7
-------
revno: 213 [merge]
committer: Guillermo Gonzalez <email address hidden>
branch nick: trunk
timestamp: Thu 2009-07-09 17:40:43 -0300
message:
merge lp:~verterok/bzr-eclipse/add-ignore
---
revno: 212.1.4
committer: Guillermo Gonzalez <email address hidden>
branch nick: add-ignore
timestamp: Thu 2009-07-09 17:29:33 -0300
message:
fix IgnoredCommand to be compatible with eclipse >= 3.3
include commons-logging in core.tests classpath
---
revno: 212.1.3
committer: Guillermo Gonzalez <email address hidden>
branch nick: add-ignore
timestamp: Thu 2009-07-09 15:33:49 -0300
message:
update bzr-java-lib deps
---
revno: 212.1.2
committer: Guillermo Gonzalez <email address hidden>
branch nick: add-ignore
timestamp: Thu 2009-07-09 02:51:20 -0300
message:
Use IResource and IBzrResource while handling Ignore/d actions, all resources handling is now in the core
IgnoredCo
Fix ignored and ignore (commands and actions) to handle projects that aren't the branch root
---
revno: 212.1.1 [merge]
committer: Guillermo Gonzalez <email address hidden>
branch nick: add-ignore
timestamp: Wed 2009-07-01 00:52:46 -0300
message:
merge lp:~javierder/bzr-eclipse/add-ignore
revno: 211.1.2 [merge]
committer: Javier Derderian <email address hidden>
branch nick: bzr-eclipse-tomerge
timestamp: Mon 2009-06-01 12:52:23 -0300
message:
Merged with lp:~javierder/bzr-eclipse/add-ignore
revno: 210.4.6 [merge]
branch nick: bzr-eclipse-...
Guillermo Gonzalez (verterok) wrote : | # |
Thanks for working on this.
I'll talk with the bzr devs to see if this is a bzr core issue.
Cheers
Preview Diff
1 | === modified file 'logxml.py' | |||
2 | --- logxml.py 2011-12-12 15:11:38 +0000 | |||
3 | +++ logxml.py 2013-03-22 16:31:28 +0000 | |||
4 | @@ -24,11 +24,8 @@ | |||
5 | 24 | super(XMLLogFormatter, self).__init__(*args, **kwargs) | 24 | super(XMLLogFormatter, self).__init__(*args, **kwargs) |
6 | 25 | self.log_count = 0 | 25 | self.log_count = 0 |
7 | 26 | self.start_with_merge = False | 26 | self.start_with_merge = False |
8 | 27 | self.nested_merge_count = 0 | ||
9 | 28 | self.previous_merge_depth = 0 | 27 | self.previous_merge_depth = 0 |
10 | 29 | self.debug_enabled = 'debug' in debug.debug_flags | 28 | self.debug_enabled = 'debug' in debug.debug_flags |
11 | 30 | self.open_logs = 0 | ||
12 | 31 | self.open_merges = 0 | ||
13 | 32 | self.stack = [] | 29 | self.stack = [] |
14 | 33 | 30 | ||
15 | 34 | def show(self, revno, rev, delta, tags=None): | 31 | def show(self, revno, rev, delta, tags=None): |
16 | @@ -47,89 +44,45 @@ | |||
17 | 47 | self.__debug(revision) | 44 | self.__debug(revision) |
18 | 48 | actions = [] | 45 | actions = [] |
19 | 49 | # to handle merge revision as childs | 46 | # to handle merge revision as childs |
66 | 50 | if revision.merge_depth > 0 and not self.start_with_merge: | 47 | if self.previous_merge_depth < revision.merge_depth: |
67 | 51 | if self.previous_merge_depth < revision.merge_depth: | 48 | if self.log_count > 0: |
68 | 52 | if self.log_count > 0: | 49 | self.__open_merge(revision.merge_depth - self.previous_merge_depth) |
69 | 53 | merge_depth_diference = revision.merge_depth - \ | 50 | elif self.previous_merge_depth > revision.merge_depth: |
70 | 54 | self.previous_merge_depth | 51 | self.__close_merge(self.previous_merge_depth - revision.merge_depth) |
71 | 55 | for m in range(merge_depth_diference): | 52 | if len(self.stack) > 0 and self.stack[len(self.stack) - 1] == 'log': |
72 | 56 | actions.append(self.__open_merge) | 53 | self.__close_log() |
73 | 57 | if merge_depth_diference > 1: | 54 | else: |
74 | 58 | self.nested_merge_count += 1 | 55 | self.__close_log() |
29 | 59 | elif self.log_count == 0: | ||
30 | 60 | # first log is inside a merge, we show it as a top level | ||
31 | 61 | # we could support a merge tag without parent log. | ||
32 | 62 | self.start_with_merge = True | ||
33 | 63 | elif self.previous_merge_depth > revision.merge_depth: | ||
34 | 64 | # TODO: testcase for more than one level of nested merges | ||
35 | 65 | actions.append({self.__close_merge:self.previous_merge_depth - \ | ||
36 | 66 | revision.merge_depth}) | ||
37 | 67 | if self.nested_merge_count > 0: | ||
38 | 68 | self.nested_merge_count -= 1 | ||
39 | 69 | else: | ||
40 | 70 | actions.append(self.__close_log) | ||
41 | 71 | else: | ||
42 | 72 | if self.open_logs > 0: | ||
43 | 73 | actions.append(self.__close_log) | ||
44 | 74 | elif self.previous_merge_depth < revision.merge_depth: | ||
45 | 75 | actions.append({self.__close_merge:self.previous_merge_depth - \ | ||
46 | 76 | revision.merge_depth}) | ||
47 | 77 | if self.nested_merge_count > 0: | ||
48 | 78 | self.nested_merge_count -= 1 | ||
49 | 79 | else: | ||
50 | 80 | actions.append(self.__close_log) | ||
51 | 81 | elif self.open_merges > 0: | ||
52 | 82 | actions.append({self.__close_merge:self.open_merges}) | ||
53 | 83 | #actions.append(self.__close_merge) | ||
54 | 84 | actions.append(self.__close_log) | ||
55 | 85 | else: | ||
56 | 86 | actions.append(self.__close_log) | ||
57 | 87 | if self.start_with_merge: | ||
58 | 88 | # we only care about the first log, the following logs are | ||
59 | 89 | # handlend in the logic of nested merges | ||
60 | 90 | self.start_with_merge = False | ||
61 | 91 | for action in actions: | ||
62 | 92 | if type(action) == dict: | ||
63 | 93 | action.keys()[0](action[action.keys()[0]]) | ||
64 | 94 | else: | ||
65 | 95 | action() | ||
75 | 96 | self.__open_log() | 56 | self.__open_log() |
76 | 97 | self.__log_revision(revision) | 57 | self.__log_revision(revision) |
77 | 98 | 58 | ||
78 | 99 | self.log_count = self.log_count + 1 | 59 | self.log_count = self.log_count + 1 |
79 | 100 | self.previous_merge_depth = revision.merge_depth | 60 | self.previous_merge_depth = revision.merge_depth |
80 | 101 | 61 | ||
97 | 102 | def __open_merge(self): | 62 | def __open_merge(self, levels): |
98 | 103 | self.to_file.write('<merge>') | 63 | for i in range(levels): |
99 | 104 | self.open_merges += 1 | 64 | self.to_file.write('<merge>') |
100 | 105 | self.stack.append('merge') | 65 | self.stack.append('merge') |
85 | 106 | |||
86 | 107 | def __close_merge(self, num=1): | ||
87 | 108 | for item in self.stack.__reversed__(): | ||
88 | 109 | self.to_file.write('</%s>' % item) | ||
89 | 110 | self.stack.pop() | ||
90 | 111 | if item == 'merge': | ||
91 | 112 | self.open_merges -= 1 | ||
92 | 113 | num -= 1 | ||
93 | 114 | if num == 0: | ||
94 | 115 | return | ||
95 | 116 | if item == 'log': | ||
96 | 117 | self.open_logs -= 1 | ||
101 | 118 | 66 | ||
102 | 119 | def __open_log(self): | 67 | def __open_log(self): |
103 | 120 | self.to_file.write('<log>',) | 68 | self.to_file.write('<log>',) |
104 | 121 | self.open_logs = self.open_logs + 1 | ||
105 | 122 | self.stack.append('log') | 69 | self.stack.append('log') |
106 | 123 | 70 | ||
107 | 71 | def __close_merge(self, levels): | ||
108 | 72 | while len(self.stack) > 0: | ||
109 | 73 | item = self.stack.pop(len(self.stack) - 1) | ||
110 | 74 | self.to_file.write('</%s>' % item) | ||
111 | 75 | if item == 'merge': | ||
112 | 76 | levels -= 1 | ||
113 | 77 | if levels == 0: | ||
114 | 78 | return | ||
115 | 79 | |||
116 | 124 | def __close_log(self): | 80 | def __close_log(self): |
118 | 125 | for item in self.stack.__reversed__(): | 81 | while len(self.stack) > 0: |
119 | 82 | item = self.stack.pop(len(self.stack) - 1) | ||
120 | 126 | self.to_file.write('</%s>' % item) | 83 | self.to_file.write('</%s>' % item) |
121 | 127 | self.stack.pop() | ||
122 | 128 | if item == 'log': | 84 | if item == 'log': |
123 | 129 | self.open_logs -= 1 | ||
124 | 130 | return | 85 | return |
125 | 131 | if item == 'merge': | ||
126 | 132 | self.open_merges -= 1 | ||
127 | 133 | 86 | ||
128 | 134 | def __log_revision(self, revision): | 87 | def __log_revision(self, revision): |
129 | 135 | if revision.revno is not None: | 88 | if revision.revno is not None: |
130 | @@ -137,7 +90,7 @@ | |||
131 | 137 | if revision.tags: | 90 | if revision.tags: |
132 | 138 | self.to_file.write('<tags>') | 91 | self.to_file.write('<tags>') |
133 | 139 | for tag in revision.tags: | 92 | for tag in revision.tags: |
135 | 140 | self.to_file.write('<tag>%s</tag>' % tag) | 93 | self.to_file.write('<tag>%s</tag>' % _escape_cdata(tag)) |
136 | 141 | self.to_file.write('</tags>') | 94 | self.to_file.write('</tags>') |
137 | 142 | if self.show_ids: | 95 | if self.show_ids: |
138 | 143 | self.to_file.write('<revisionid>%s</revisionid>' % | 96 | self.to_file.write('<revisionid>%s</revisionid>' % |
139 | @@ -181,26 +134,7 @@ | |||
140 | 181 | 134 | ||
141 | 182 | def end_log(self): | 135 | def end_log(self): |
142 | 183 | #if the last logged was inside a merge (and it was only one log) | 136 | #if the last logged was inside a merge (and it was only one log) |
163 | 184 | if self.open_logs > 1 and self.open_merges > 0: | 137 | self.__close_merge(len(self.stack)) |
144 | 185 | self.to_file.write('</log>') | ||
145 | 186 | self.open_logs = self.open_logs - 1 | ||
146 | 187 | if not self.start_with_merge: | ||
147 | 188 | # In case that the last log was inside a merge we need to close it | ||
148 | 189 | if self.open_merges > 0: | ||
149 | 190 | for merge in range(self.open_merges): | ||
150 | 191 | self.to_file.write('</merge>') | ||
151 | 192 | if self.open_logs > 0: | ||
152 | 193 | self.to_file.write('</log>') | ||
153 | 194 | self.open_logs -= 1 | ||
154 | 195 | self.open_merges = self.open_merges - 1 | ||
155 | 196 | # to close the last opened log | ||
156 | 197 | if self.open_logs > 0: | ||
157 | 198 | self.to_file.write('</log>') | ||
158 | 199 | self.open_logs = self.open_logs - 1 | ||
159 | 200 | else: | ||
160 | 201 | if self.open_logs > 0: | ||
161 | 202 | self.to_file.write('</log>') | ||
162 | 203 | self.open_logs = self.open_logs - 1 | ||
164 | 204 | self.to_file.write('</logs>') | 138 | self.to_file.write('</logs>') |
165 | 205 | 139 | ||
166 | 206 | 140 | ||
167 | 207 | 141 | ||
168 | === modified file 'tests/test_log_xml.py' | |||
169 | --- tests/test_log_xml.py 2012-02-21 10:14:16 +0000 | |||
170 | +++ tests/test_log_xml.py 2013-03-22 16:31:28 +0000 | |||
171 | @@ -184,11 +184,13 @@ | |||
172 | 184 | branch1_tree = self._prepare(path='branch1', format='dirstate-tags') | 184 | branch1_tree = self._prepare(path='branch1', format='dirstate-tags') |
173 | 185 | branch1 = branch1_tree.branch | 185 | branch1 = branch1_tree.branch |
174 | 186 | branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree() | 186 | branch2_tree = branch1_tree.bzrdir.sprout('branch2').open_workingtree() |
175 | 187 | branch2 = branch2_tree.branch | ||
176 | 187 | branch1_tree.commit(message='foobar', allow_pointless=True) | 188 | branch1_tree.commit(message='foobar', allow_pointless=True) |
177 | 188 | branch1.tags.set_tag('tag1', branch1.last_revision()) | 189 | branch1.tags.set_tag('tag1', branch1.last_revision()) |
178 | 190 | branch2_tree.merge_from_branch(branch1) | ||
179 | 191 | branch1.tags.merge_to(branch2.tags) | ||
180 | 192 | branch2_tree.commit(message='merge branch 1') | ||
181 | 189 | os.chdir('branch2') | 193 | os.chdir('branch2') |
182 | 190 | self.run_bzr('merge ../branch1') # tags don't propagate otherwise | ||
183 | 191 | branch2_tree.commit(message='merge branch 1') | ||
184 | 192 | log_xml = fromstring(self.run_bzr("xmllog -r-1")[0]) | 194 | log_xml = fromstring(self.run_bzr("xmllog -r-1")[0]) |
185 | 193 | for tag in log_xml.findall('log/merge/log/tags/tag'): | 195 | for tag in log_xml.findall('log/merge/log/tags/tag'): |
186 | 194 | self.assertEquals(tag.text, 'tag1') | 196 | self.assertEquals(tag.text, 'tag1') |
187 | @@ -460,7 +462,7 @@ | |||
188 | 460 | for message in log_xml.findall('log/merge/merge/log/message'): | 462 | for message in log_xml.findall('log/merge/merge/log/message'): |
189 | 461 | self.assertEquals(message.text.strip(), 'merge first post') | 463 | self.assertEquals(message.text.strip(), 'merge first post') |
190 | 462 | 464 | ||
192 | 463 | class TestLogEncodings(TestCaseInTempDir): | 465 | class TestLogEncodings(ExternalBase): |
193 | 464 | 466 | ||
194 | 465 | _mu = u'\xb5' | 467 | _mu = u'\xb5' |
195 | 466 | _message = u'Message with \xb5' | 468 | _message = u'Message with \xb5' |
196 | @@ -482,7 +484,7 @@ | |||
197 | 482 | ] | 484 | ] |
198 | 483 | 485 | ||
199 | 484 | def setUp(self): | 486 | def setUp(self): |
201 | 485 | TestCaseInTempDir.setUp(self) | 487 | ExternalBase.setUp(self) |
202 | 486 | self.old_user_encoding = osutils._cached_user_encoding | 488 | self.old_user_encoding = osutils._cached_user_encoding |
203 | 487 | self.old_get_user_encoding = osutils.get_user_encoding | 489 | self.old_get_user_encoding = osutils.get_user_encoding |
204 | 488 | 490 | ||
205 | @@ -492,14 +494,21 @@ | |||
206 | 492 | bzrlib.user_encoding = self.old_user_encoding | 494 | bzrlib.user_encoding = self.old_user_encoding |
207 | 493 | osutils._cached_user_encoding = self.old_user_encoding | 495 | osutils._cached_user_encoding = self.old_user_encoding |
208 | 494 | osutils.get_user_encoding = self.old_get_user_encoding | 496 | osutils.get_user_encoding = self.old_get_user_encoding |
217 | 495 | TestCaseInTempDir.tearDown(self) | 497 | ExternalBase.tearDown(self) |
218 | 496 | 498 | ||
219 | 497 | def create_branch(self): | 499 | def create_branch(self, path='.', format=None): |
220 | 498 | bzr = self.run_bzr | 500 | tree = self.make_branch_and_tree(path, format=format) |
221 | 499 | bzr('init') | 501 | self.build_tree( |
222 | 500 | open('a', 'wb').write('some stuff\n') | 502 | [path + '/hello.txt']) |
223 | 501 | bzr('add a') | 503 | tree.add('hello.txt') |
224 | 502 | bzr(['commit', '-m', self._message]) | 504 | tree.commit(message=self._message) |
225 | 505 | |||
226 | 506 | def create_branch2(self, path='.', format=None): | ||
227 | 507 | tree = self.make_branch_and_tree(path, format=format) | ||
228 | 508 | self.build_tree( | ||
229 | 509 | [path + '/hello.txt']) | ||
230 | 510 | tree.add('hello.txt') | ||
231 | 511 | tree.commit(message=u'\u0422\u0435\u0441\u0442') | ||
232 | 503 | 512 | ||
233 | 504 | def try_encoding(self, encoding, fail=False): | 513 | def try_encoding(self, encoding, fail=False): |
234 | 505 | bzr = self.run_bzr | 514 | bzr = self.run_bzr |
235 | @@ -546,16 +555,13 @@ | |||
236 | 546 | self.try_encoding(encoding, fail=True) | 555 | self.try_encoding(encoding, fail=True) |
237 | 547 | 556 | ||
238 | 548 | def test_stdout_encoding(self): | 557 | def test_stdout_encoding(self): |
239 | 558 | self.create_branch2() | ||
240 | 549 | bzr = self.run_bzr | 559 | bzr = self.run_bzr |
241 | 550 | osutils._cached_user_encoding = 'cp1251' | 560 | osutils._cached_user_encoding = 'cp1251' |
242 | 551 | bzrlib.osutils.get_user_encoding = lambda: 'cp1251' | 561 | bzrlib.osutils.get_user_encoding = lambda: 'cp1251' |
243 | 552 | if hasattr(bzrlib, 'user_encoding'): | 562 | if hasattr(bzrlib, 'user_encoding'): |
244 | 553 | bzrlib.user_encoding = 'cp1251' | 563 | bzrlib.user_encoding = 'cp1251' |
245 | 554 | 564 | ||
246 | 555 | bzr('init') | ||
247 | 556 | self.build_tree(['a']) | ||
248 | 557 | bzr('add a') | ||
249 | 558 | bzr(['commit', '-m', u'\u0422\u0435\u0441\u0442']) | ||
250 | 559 | stdout, stderr = self.run_bzr('xmllog', encoding='cp866') | 565 | stdout, stderr = self.run_bzr('xmllog', encoding='cp866') |
251 | 560 | 566 | ||
252 | 561 | #message = stdout.splitlines()[-1] | 567 | #message = stdout.splitlines()[-1] |
Thanks for fixing this.
It would be ideal to have a testcase to reproduce the bad xml, but I wasn't able to write one yet (can't reproduce the nested merges in the test to trigger this condition).
Please let me know if can do it, if not I'll try to ask one of the bzr gurus @ #bzr.
Cheers,