Merge lp:~jelmer/bzr/result-mentions-tags into lp:bzr

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merged at revision: 6125
Proposed branch: lp:~jelmer/bzr/result-mentions-tags
Merge into: lp:bzr
Diff against target: 447 lines (+102/-45)
12 files modified
bzrlib/branch.py (+32/-12)
bzrlib/commit.py (+2/-1)
bzrlib/tag.py (+21/-14)
bzrlib/tests/blackbox/test_non_ascii.py (+1/-1)
bzrlib/tests/blackbox/test_pull.py (+11/-1)
bzrlib/tests/blackbox/test_push.py (+3/-1)
bzrlib/tests/per_branch/test_pull.py (+1/-1)
bzrlib/tests/per_branch/test_tags.py (+19/-10)
bzrlib/tests/per_interbranch/test_pull.py (+1/-1)
bzrlib/tests/test_branch.py (+1/-1)
bzrlib/tests/test_tag.py (+4/-2)
doc/en/release-notes/bzr-2.5.txt (+6/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/result-mentions-tags
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Review via email: mp+73564@code.launchpad.net

Commit message

Mention the number of tags that was pushed/pull, if any.

Description of the change

Mention the number of tags that was pushed/pull, if any.

To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

[needsinfo] It seems merge_to changes caused problems in the past
(see bzrlib.tags._merge_tags_if_possible), what's your feeling on
changing the signature again and the possible fallouts ? (loom ?)

Or did I misread the diff and merge_to didn't return anything
before ? Or does it matter nevertheless ?

[needsfixing] There is a lot of duplication between
PullResult.report() and BranchPushResult.report()

But one is using to_file_write.write() and the other
trace.note(). This doesn't feel right, shouldn't they both
provide the same feedback to the user ?

[PEP8 nit]
75 + result.tag_updates, result.tag_conflicts = self.source.tags.merge_to(
76 + self.target.tags, overwrite, ignore_master=not merge_tags_to_master)

lines too long.

[2.4-worthy] Shouldn't that be backported for the benefit of the
package importer ?

review: Needs Fixing
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

I've filed a bug about the output inconsistency between "bzr pull" / "bzr push" and added a note in the code. r=vila on IRC.

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/branch.py'
2--- bzrlib/branch.py 2011-08-27 16:59:43 +0000
3+++ bzrlib/branch.py 2011-09-02 00:55:08 +0000
4@@ -3044,6 +3044,7 @@
5 :ivar local_branch: target branch if there is a Master, else None
6 :ivar target_branch: Target/destination branch object. (write locked)
7 :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
8+ :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
9 """
10
11 @deprecated_method(deprecated_in((2, 3, 0)))
12@@ -3055,11 +3056,18 @@
13 return self.new_revno - self.old_revno
14
15 def report(self, to_file):
16+ tag_conflicts = getattr(self, "tag_conflicts", None)
17+ tag_updates = getattr(self, "tag_updates", None)
18 if not is_quiet():
19- if self.old_revid == self.new_revid:
20- to_file.write('No revisions to pull.\n')
21- else:
22+ if self.old_revid != self.new_revid:
23 to_file.write('Now on revision %d.\n' % self.new_revno)
24+ if tag_updates:
25+ to_file.write('%d tag(s) updated.\n' % len(tag_updates))
26+ if self.old_revid == self.new_revid and not tag_updates:
27+ if not tag_conflicts:
28+ to_file.write('No revisions or tags to pull.\n')
29+ else:
30+ to_file.write('No revisions to pull.\n')
31 self._show_tag_conficts(to_file)
32
33
34@@ -3091,11 +3099,22 @@
35 return self.new_revno - self.old_revno
36
37 def report(self, to_file):
38- """Write a human-readable description of the result."""
39- if self.old_revid == self.new_revid:
40- note('No new revisions to push.')
41- else:
42- note('Pushed up to revision %d.' % self.new_revno)
43+ # TODO: This function gets passed a to_file, but then
44+ # ignores it and calls note() instead. This is also
45+ # inconsistent with PullResult(), which writes to stdout.
46+ # -- JRV20110901, bug #838853
47+ tag_conflicts = getattr(self, "tag_conflicts", None)
48+ tag_updates = getattr(self, "tag_updates", None)
49+ if not is_quiet():
50+ if self.old_revid != self.new_revid:
51+ note('Pushed up to revision %d.' % self.new_revno)
52+ if tag_updates:
53+ note('%d tag(s) updated.' % len(tag_updates))
54+ if self.old_revid == self.new_revid and not tag_updates:
55+ if not tag_conflicts:
56+ note('No new revisions or tags to push.')
57+ else:
58+ note('No new revisions to push.')
59 self._show_tag_conficts(to_file)
60
61
62@@ -3409,8 +3428,8 @@
63 self._update_revisions(stop_revision, overwrite=overwrite,
64 graph=graph)
65 if self.source._push_should_merge_tags():
66- result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
67- overwrite)
68+ result.tag_updates, result.tag_conflicts = (
69+ self.source.tags.merge_to(self.target.tags, overwrite))
70 result.new_revno, result.new_revid = self.target.last_revision_info()
71 return result
72
73@@ -3499,8 +3518,9 @@
74 # TODO: The old revid should be specified when merging tags,
75 # so a tags implementation that versions tags can only
76 # pull in the most recent changes. -- JRV20090506
77- result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
78- overwrite, ignore_master=not merge_tags_to_master)
79+ result.tag_updates, result.tag_conflicts = (
80+ self.source.tags.merge_to(self.target.tags, overwrite,
81+ ignore_master=not merge_tags_to_master))
82 result.new_revno, result.new_revid = self.target.last_revision_info()
83 if _hook_master:
84 result.master_branch = _hook_master
85
86=== modified file 'bzrlib/commit.py'
87--- bzrlib/commit.py 2011-06-28 17:25:26 +0000
88+++ bzrlib/commit.py 2011-09-02 00:55:08 +0000
89@@ -464,7 +464,8 @@
90 # Merge local tags to remote
91 if self.bound_branch:
92 self._set_progress_stage("Merging tags to master branch")
93- tag_conflicts = self.branch.tags.merge_to(self.master_branch.tags)
94+ tag_updates, tag_conflicts = self.branch.tags.merge_to(
95+ self.master_branch.tags)
96 if tag_conflicts:
97 warning_lines = [' ' + name for name, _, _ in tag_conflicts]
98 note("Conflicting tags in bound branch:\n" +
99
100=== modified file 'bzrlib/tag.py'
101--- bzrlib/tag.py 2011-05-18 16:42:48 +0000
102+++ bzrlib/tag.py 2011-09-02 00:55:08 +0000
103@@ -68,7 +68,7 @@
104
105 def merge_to(self, to_tags, overwrite=False, ignore_master=False):
106 # we never have anything to copy
107- pass
108+ return {}, []
109
110 def rename_revisions(self, rename_map):
111 # No tags, so nothing to rename
112@@ -201,7 +201,10 @@
113 branch (if any). Default is false (so the master will be updated).
114 New in bzr 2.3.
115
116- :returns: A set of tags that conflicted, each of which is
117+ :returns: Tuple with tag_updates and tag_conflicts.
118+ tag_updates is a dictionary with new tags, None is used for
119+ removed tags
120+ tag_conflicts is a set of tags that conflicted, each of which is
121 (tagname, source_target, dest_target), or None if no copying was
122 done.
123 """
124@@ -211,15 +214,15 @@
125 def _merge_to_operation(self, operation, to_tags, overwrite, ignore_master):
126 add_cleanup = operation.add_cleanup
127 if self.branch == to_tags.branch:
128- return
129+ return {}, []
130 if not self.branch.supports_tags():
131 # obviously nothing to copy
132- return
133+ return {}, []
134 source_dict = self.get_tag_dict()
135 if not source_dict:
136 # no tags in the source, and we don't want to clobber anything
137 # that's in the destination
138- return
139+ return {}, []
140 # We merge_to both master and child individually.
141 #
142 # It's possible for master and child to have differing sets of
143@@ -239,21 +242,23 @@
144 master = to_tags.branch.get_master_branch()
145 if master is not None:
146 add_cleanup(master.lock_write().unlock)
147- conflicts = self._merge_to(to_tags, source_dict, overwrite)
148+ updates, conflicts = self._merge_to(to_tags, source_dict, overwrite)
149 if master is not None:
150- conflicts += self._merge_to(master.tags, source_dict,
151- overwrite)
152+ extra_updates, extra_conflicts = self._merge_to(master.tags,
153+ source_dict, overwrite)
154+ updates.update(extra_updates)
155+ conflicts += extra_conflicts
156 # We use set() to remove any duplicate conflicts from the master
157 # branch.
158- return set(conflicts)
159+ return updates, set(conflicts)
160
161 def _merge_to(self, to_tags, source_dict, overwrite):
162 dest_dict = to_tags.get_tag_dict()
163- result, conflicts = self._reconcile_tags(source_dict, dest_dict,
164- overwrite)
165+ result, updates, conflicts = self._reconcile_tags(source_dict,
166+ dest_dict, overwrite)
167 if result != dest_dict:
168 to_tags._set_tag_dict(result)
169- return conflicts
170+ return updates, conflicts
171
172 def rename_revisions(self, rename_map):
173 """Rename revisions in this tags dictionary.
174@@ -275,19 +280,21 @@
175 * different definitions => if overwrite is False, keep destination
176 value and give a warning, otherwise use the source value
177
178- :returns: (result_dict,
179+ :returns: (result_dict, updates,
180 [(conflicting_tag, source_target, dest_target)])
181 """
182 conflicts = []
183+ updates = {}
184 result = dict(dest_dict) # copy
185 for name, target in source_dict.items():
186 if name not in result or overwrite:
187 result[name] = target
188+ updates[name] = target
189 elif result[name] == target:
190 pass
191 else:
192 conflicts.append((name, target, result[name]))
193- return result, conflicts
194+ return result, updates, conflicts
195
196
197 def _merge_tags_if_possible(from_branch, to_branch, ignore_master=False):
198
199=== modified file 'bzrlib/tests/blackbox/test_non_ascii.py'
200--- bzrlib/tests/blackbox/test_non_ascii.py 2011-08-04 00:17:53 +0000
201+++ bzrlib/tests/blackbox/test_non_ascii.py 2011-09-02 00:55:08 +0000
202@@ -267,7 +267,7 @@
203
204 expected = osutils.pathjoin(osutils.getcwd(), dirname1)
205 self.assertEqual(u'Using saved parent location: %s/\n'
206- 'No revisions to pull.\n' % (expected,), txt)
207+ 'No revisions or tags to pull.\n' % (expected,), txt)
208
209 self.build_tree_contents(
210 [(osutils.pathjoin(dirname1, 'a'), 'and yet more\n')])
211
212=== modified file 'bzrlib/tests/blackbox/test_pull.py'
213--- bzrlib/tests/blackbox/test_pull.py 2011-08-30 08:46:10 +0000
214+++ bzrlib/tests/blackbox/test_pull.py 2011-09-02 00:55:08 +0000
215@@ -318,7 +318,7 @@
216 # it is legal to attempt to pull an already-merged bundle
217 out, err = self.run_bzr('pull ../bundle')
218 self.assertEqual(err, '')
219- self.assertEqual(out, 'No revisions to pull.\n')
220+ self.assertEqual(out, 'No revisions or tags to pull.\n')
221
222 def test_pull_verbose_no_files(self):
223 """Pull --verbose should not list modified files"""
224@@ -533,3 +533,13 @@
225 out = self.run_bzr(['pull','-d','to','from'],retcode=1)
226 self.assertEqual(out,
227 ('No revisions to pull.\nConflicting tags:\n mytag\n', ''))
228+
229+ def test_pull_tag_notification(self):
230+ """pulling tags with conflicts will change the exit code"""
231+ # create a branch, see that --show-base fails
232+ from_tree = self.make_branch_and_tree('from')
233+ from_tree.branch.tags.set_tag("mytag", "somerevid")
234+ to_tree = self.make_branch_and_tree('to')
235+ out = self.run_bzr(['pull', '-d', 'to', 'from'])
236+ self.assertEqual(out,
237+ ('1 tag(s) updated.\n', ''))
238
239=== modified file 'bzrlib/tests/blackbox/test_push.py'
240--- bzrlib/tests/blackbox/test_push.py 2011-08-16 15:03:03 +0000
241+++ bzrlib/tests/blackbox/test_push.py 2011-09-02 00:55:08 +0000
242@@ -157,7 +157,9 @@
243 self.run_bzr('push -d tree pushed-to')
244 path = t.branch.get_push_location()
245 out, err = self.run_bzr('push', working_dir="tree")
246- self.assertEqual('Using saved push location: %s\nNo new revisions to push.\n' % urlutils.local_path_from_url(path), err)
247+ self.assertEqual('Using saved push location: %s\n'
248+ 'No new revisions or tags to push.\n' %
249+ urlutils.local_path_from_url(path), err)
250 out, err = self.run_bzr('push -q', working_dir="tree")
251 self.assertEqual('', out)
252 self.assertEqual('', err)
253
254=== modified file 'bzrlib/tests/per_branch/test_pull.py'
255--- bzrlib/tests/per_branch/test_pull.py 2011-08-09 14:18:05 +0000
256+++ bzrlib/tests/per_branch/test_pull.py 2011-09-02 00:55:08 +0000
257@@ -117,7 +117,7 @@
258 self.assertEqual('P1', result.old_revid)
259 self.assertEqual(2, result.new_revno)
260 self.assertEqual('M1', result.new_revid)
261- self.assertEqual(None, result.tag_conflicts)
262+ self.assertEqual([], result.tag_conflicts)
263
264 def test_pull_overwrite(self):
265 tree_a = self.make_branch_and_tree('tree_a')
266
267=== modified file 'bzrlib/tests/per_branch/test_tags.py'
268--- bzrlib/tests/per_branch/test_tags.py 2011-08-26 23:59:59 +0000
269+++ bzrlib/tests/per_branch/test_tags.py 2011-09-02 00:55:08 +0000
270@@ -93,17 +93,19 @@
271 # if a tag is in the destination and not in the source, it is not
272 # removed when we merge them
273 b2.tags.set_tag('in-destination', 'revid')
274- result = b1.tags.merge_to(b2.tags)
275- self.assertEquals(list(result), [])
276+ updates, conflicts = b1.tags.merge_to(b2.tags)
277+ self.assertEquals(list(conflicts), [])
278+ self.assertEquals(updates, {})
279 self.assertEquals(b2.tags.lookup_tag('in-destination'), 'revid')
280 # if there's a conflicting tag, it's reported -- the command line
281 # interface will say "these tags couldn't be copied"
282 b1.tags.set_tag('conflicts', 'revid-1')
283 b2.tags.set_tag('conflicts', 'revid-2')
284- result = b1.tags.merge_to(b2.tags)
285- self.assertEquals(list(result),
286+ updates, conflicts = b1.tags.merge_to(b2.tags)
287+ self.assertEquals(list(conflicts),
288 [('conflicts', 'revid-1', 'revid-2')])
289 # and it keeps the same value
290+ self.assertEquals(updates, {})
291 self.assertEquals(b2.tags.lookup_tag('conflicts'), 'revid-2')
292
293 def test_unicode_tag(self):
294@@ -292,9 +294,10 @@
295 child.bind(master)
296 child.update()
297 master.tags.set_tag('foo', 'rev-2')
298- tag_conflicts = other.tags.merge_to(child.tags, overwrite=True)
299+ tag_updates, tag_conflicts = other.tags.merge_to(child.tags, overwrite=True)
300 self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
301 self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
302+ self.assertEquals({"foo": "rev-1"}, tag_updates)
303 self.assertLength(0, tag_conflicts)
304
305 def test_merge_to_overwrite_conflict_in_child_and_master(self):
306@@ -308,9 +311,11 @@
307 child = self.make_branch('child')
308 child.bind(master)
309 child.update()
310- tag_conflicts = other.tags.merge_to(child.tags, overwrite=True)
311+ tag_updates, tag_conflicts = other.tags.merge_to(
312+ child.tags, overwrite=True)
313 self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
314 self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
315+ self.assertEquals({u'foo': 'rev-1'}, tag_updates)
316 self.assertLength(0, tag_conflicts)
317
318 def test_merge_to_conflict_in_child_only(self):
319@@ -325,13 +330,14 @@
320 child.bind(master)
321 child.update()
322 master.tags.delete_tag('foo')
323- tag_conflicts = other.tags.merge_to(child.tags)
324+ tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
325 # Conflict in child, so it is unchanged.
326 self.assertEquals('rev-2', child.tags.lookup_tag('foo'))
327 # No conflict in the master, so the 'foo' tag equals other's value here.
328 self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
329 # The conflict is reported.
330 self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
331+ self.assertEquals({u'foo': 'rev-1'}, tag_updates)
332
333 def test_merge_to_conflict_in_master_only(self):
334 """When new_tags.merge_to(child.tags) conflicts with the master but not
335@@ -344,12 +350,13 @@
336 child.bind(master)
337 child.update()
338 master.tags.set_tag('foo', 'rev-2')
339- tag_conflicts = other.tags.merge_to(child.tags)
340+ tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
341 # No conflict in the child, so the 'foo' tag equals other's value here.
342 self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
343 # Conflict in master, so it is unchanged.
344 self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
345 # The conflict is reported.
346+ self.assertEquals({u'foo': 'rev-1'}, tag_updates)
347 self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
348
349 def test_merge_to_same_conflict_in_master_and_child(self):
350@@ -363,12 +370,13 @@
351 child = self.make_branch('child')
352 child.bind(master)
353 child.update()
354- tag_conflicts = other.tags.merge_to(child.tags)
355+ tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
356 # Both master and child conflict, so both stay as rev-2
357 self.assertEquals('rev-2', child.tags.lookup_tag('foo'))
358 self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
359 # The conflict is reported exactly once, even though it occurs in both
360 # master and child.
361+ self.assertEquals({}, tag_updates)
362 self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
363
364 def test_merge_to_different_conflict_in_master_and_child(self):
365@@ -385,11 +393,12 @@
366 # We use the private method _set_tag_dict because normally bzr tries to
367 # avoid this scenario.
368 child.tags._set_tag_dict({'foo': 'rev-3'})
369- tag_conflicts = other.tags.merge_to(child.tags)
370+ tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
371 # Both master and child conflict, so both stay as they were.
372 self.assertEquals('rev-3', child.tags.lookup_tag('foo'))
373 self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
374 # Both conflicts are reported.
375+ self.assertEquals({}, tag_updates)
376 self.assertEquals(
377 [(u'foo', 'rev-1', 'rev-2'), (u'foo', 'rev-1', 'rev-3')],
378 sorted(tag_conflicts))
379
380=== modified file 'bzrlib/tests/per_interbranch/test_pull.py'
381--- bzrlib/tests/per_interbranch/test_pull.py 2010-06-17 09:23:19 +0000
382+++ bzrlib/tests/per_interbranch/test_pull.py 2011-09-02 00:55:08 +0000
383@@ -99,7 +99,7 @@
384 self.assertEqual('P1', result.old_revid)
385 self.assertEqual(2, result.new_revno)
386 self.assertEqual('M1', result.new_revid)
387- self.assertEqual(None, result.tag_conflicts)
388+ self.assertEqual([], result.tag_conflicts)
389
390 def test_pull_overwrite(self):
391 tree_a = self.make_from_branch_and_tree('tree_a')
392
393=== modified file 'bzrlib/tests/test_branch.py'
394--- bzrlib/tests/test_branch.py 2011-05-26 08:04:46 +0000
395+++ bzrlib/tests/test_branch.py 2011-09-02 00:55:08 +0000
396@@ -709,5 +709,5 @@
397 r.new_revid = "same-revid"
398 f = StringIO()
399 r.report(f)
400- self.assertEqual("No revisions to pull.\n", f.getvalue())
401+ self.assertEqual("No revisions or tags to pull.\n", f.getvalue())
402
403
404=== modified file 'bzrlib/tests/test_tag.py'
405--- bzrlib/tests/test_tag.py 2011-08-06 07:32:51 +0000
406+++ bzrlib/tests/test_tag.py 2011-09-02 00:55:08 +0000
407@@ -109,12 +109,14 @@
408 self.assertRaises(errors.NoSuchTag, a.tags.lookup_tag, 'tag-2')
409 # conflicting merge
410 a.tags.set_tag('tag-2', 'z')
411- conflicts = a.tags.merge_to(b.tags)
412+ updates, conflicts = a.tags.merge_to(b.tags)
413+ self.assertEqual({}, updates)
414 self.assertEqual(list(conflicts), [('tag-2', 'z', 'y')])
415 self.assertEqual('y', b.tags.lookup_tag('tag-2'))
416 # overwrite conflicts
417- conflicts = a.tags.merge_to(b.tags, overwrite=True)
418+ updates, conflicts = a.tags.merge_to(b.tags, overwrite=True)
419 self.assertEqual(list(conflicts), [])
420+ self.assertEqual({u'tag-1': 'x', u'tag-2': 'z'}, updates)
421 self.assertEqual('z', b.tags.lookup_tag('tag-2'))
422
423
424
425=== modified file 'doc/en/release-notes/bzr-2.5.txt'
426--- doc/en/release-notes/bzr-2.5.txt 2011-09-01 18:11:38 +0000
427+++ doc/en/release-notes/bzr-2.5.txt 2011-09-02 00:55:08 +0000
428@@ -102,6 +102,9 @@
429 Entering an empty commit message in the message editor still triggers
430 an error. (Jelmer Vernooij)
431
432+* ``bzr pull`` will now mention how many tags it has updated.
433+ (Jelmer Vernooij, #164450)
434+
435 * ``bzr tag`` no longer errors if a tag already exists but refers to the
436 same revision. (Jelmer Vernooij)
437
438@@ -214,6 +217,9 @@
439 value in SI format (i.e. "20MB", "1GB") into its integer equivalent.
440 (Shannon Weyrick)
441
442+* ``Tags.merge_to`` now returns a dictionary with the updated tags
443+ and a set of conflicts, rather than just conflicts. (Jelmer Vernooij)
444+
445 * ``Transport`` now has a ``_parsed_url`` attribute instead of
446 separate ``_user``, ``_password``, ``_port``, ``_scheme``, ``_host``
447 and ``_path`` attributes. Proxies are provided for the moment but