Merge lp:~jelmer/brz/intertags into lp:brz/3.1

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/intertags
Merge into: lp:brz/3.1
Diff against target: 728 lines (+284/-207)
5 files modified
breezy/bzr/branch.py (+2/-0)
breezy/bzr/tag.py (+113/-0)
breezy/git/branch.py (+103/-63)
breezy/tag.py (+63/-143)
breezy/tests/test_tag.py (+3/-1)
To merge this branch: bzr merge lp:~jelmer/brz/intertags
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+379368@code.launchpad.net

Commit message

Factor out an InterTags object.

Description of the change

Factor out an InterTags object.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/bzr/branch.py'
2--- breezy/bzr/branch.py 2020-01-19 14:49:03 +0000
3+++ breezy/bzr/branch.py 2020-02-19 03:31:03 +0000
4@@ -28,6 +28,8 @@
5 lockdir,
6 rio,
7 shelf,
8+ )
9+from breezy.bzr import (
10 tag as _mod_tag,
11 )
12 """)
13
14=== added file 'breezy/bzr/tag.py'
15--- breezy/bzr/tag.py 1970-01-01 00:00:00 +0000
16+++ breezy/bzr/tag.py 2020-02-19 03:31:03 +0000
17@@ -0,0 +1,113 @@
18+# Copyright (C) 2005-2012 Canonical Ltd
19+# Copyright (C) 2020 Breezy Developers
20+#
21+# This program is free software; you can redistribute it and/or modify
22+# it under the terms of the GNU General Public License as published by
23+# the Free Software Foundation; either version 2 of the License, or
24+# (at your option) any later version.
25+#
26+# This program is distributed in the hope that it will be useful,
27+# but WITHOUT ANY WARRANTY; without even the implied warranty of
28+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+# GNU General Public License for more details.
30+#
31+# You should have received a copy of the GNU General Public License
32+# along with this program; if not, write to the Free Software
33+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
34+
35+from __future__ import absolute_import
36+
37+from ..tag import Tags
38+
39+from .. import (
40+ bencode,
41+ errors,
42+ trace,
43+ )
44+
45+
46+class BasicTags(Tags):
47+ """Tag storage in an unversioned branch control file.
48+ """
49+
50+ def set_tag(self, tag_name, tag_target):
51+ """Add a tag definition to the branch.
52+
53+ Behaviour if the tag is already present is not defined (yet).
54+ """
55+ # all done with a write lock held, so this looks atomic
56+ with self.branch.lock_write():
57+ master = self.branch.get_master_branch()
58+ if master is not None:
59+ master.tags.set_tag(tag_name, tag_target)
60+ td = self.get_tag_dict()
61+ td[tag_name] = tag_target
62+ self._set_tag_dict(td)
63+
64+ def lookup_tag(self, tag_name):
65+ """Return the referent string of a tag"""
66+ td = self.get_tag_dict()
67+ try:
68+ return td[tag_name]
69+ except KeyError:
70+ raise errors.NoSuchTag(tag_name)
71+
72+ def get_tag_dict(self):
73+ with self.branch.lock_read():
74+ try:
75+ tag_content = self.branch._get_tags_bytes()
76+ except errors.NoSuchFile:
77+ # ugly, but only abentley should see this :)
78+ trace.warning('No branch/tags file in %s. '
79+ 'This branch was probably created by bzr 0.15pre. '
80+ 'Create an empty file to silence this message.'
81+ % (self.branch, ))
82+ return {}
83+ return self._deserialize_tag_dict(tag_content)
84+
85+ def delete_tag(self, tag_name):
86+ """Delete a tag definition.
87+ """
88+ with self.branch.lock_write():
89+ d = self.get_tag_dict()
90+ try:
91+ del d[tag_name]
92+ except KeyError:
93+ raise errors.NoSuchTag(tag_name)
94+ master = self.branch.get_master_branch()
95+ if master is not None:
96+ try:
97+ master.tags.delete_tag(tag_name)
98+ except errors.NoSuchTag:
99+ pass
100+ self._set_tag_dict(d)
101+
102+ def _set_tag_dict(self, new_dict):
103+ """Replace all tag definitions
104+
105+ WARNING: Calling this on an unlocked branch will lock it, and will
106+ replace the tags without warning on conflicts.
107+
108+ :param new_dict: Dictionary from tag name to target.
109+ """
110+ return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict))
111+
112+ def _serialize_tag_dict(self, tag_dict):
113+ td = dict((k.encode('utf-8'), v)
114+ for k, v in tag_dict.items())
115+ return bencode.bencode(td)
116+
117+ def _deserialize_tag_dict(self, tag_content):
118+ """Convert the tag file into a dictionary of tags"""
119+ # was a special case to make initialization easy, an empty definition
120+ # is an empty dictionary
121+ if tag_content == b'':
122+ return {}
123+ try:
124+ r = {}
125+ for k, v in bencode.bdecode(tag_content).items():
126+ r[k.decode('utf-8')] = v
127+ return r
128+ except ValueError as e:
129+ raise ValueError("failed to deserialize tag dictionary %r: %s"
130+ % (tag_content, e))
131
132=== modified file 'breezy/git/branch.py'
133--- breezy/git/branch.py 2020-01-24 00:06:05 +0000
134+++ breezy/git/branch.py 2020-02-19 03:31:03 +0000
135@@ -42,7 +42,6 @@
136 lock,
137 repository as _mod_repository,
138 revision,
139- tag,
140 trace,
141 transport,
142 urlutils,
143@@ -55,6 +54,10 @@
144 text_type,
145 viewitems,
146 )
147+from ..tag import (
148+ Tags,
149+ InterTags,
150+ )
151 from ..trace import (
152 is_quiet,
153 mutter,
154@@ -116,17 +119,24 @@
155 return self._lookup_revno(self.new_revid)
156
157
158-class GitTags(tag.BasicTags):
159- """Ref-based tag dictionary."""
160-
161- def __init__(self, branch):
162- self.branch = branch
163- self.repository = branch.repository
164-
165- def _merge_to_remote_git(self, target_repo, source_tag_refs,
166- overwrite=False):
167+class InterTagsFromGitToRemoteGit(InterTags):
168+
169+ @classmethod
170+ def is_compatible(klass, source, target):
171+ if not isinstance(source, GitTags):
172+ return False
173+ if not isinstance(target, GitTags):
174+ return False
175+ if getattr(target.branch.repository, "_git", None) is not None:
176+ return False
177+ return True
178+
179+ def merge(self, overwrite=False, ignore_master=False):
180+ if self.source.branch.repository.has_same_location(self.target.branch.repository):
181+ return {}, []
182 updates = {}
183 conflicts = []
184+ source_tag_refs = self.source.branch.get_tag_refs()
185
186 def get_changed_refs(old_refs):
187 ret = dict(old_refs)
188@@ -136,23 +146,43 @@
189 pass
190 elif overwrite or ref_name not in old_refs:
191 ret[ref_name] = unpeeled
192- updates[tag_name] = target_repo.lookup_foreign_revision_id(
193+ updates[tag_name] = self.target.branch.repository.lookup_foreign_revision_id(
194 peeled)
195+ self.target.branch._tag_refs = None
196 else:
197 conflicts.append(
198 (tag_name,
199 self.repository.lookup_foreign_revision_id(peeled),
200- target_repo.lookup_foreign_revision_id(
201+ self.target.branch.repository.lookup_foreign_revision_id(
202 old_refs[ref_name])))
203 return ret
204- target_repo.controldir.send_pack(
205+ self.target.branch.repository.controldir.send_pack(
206 get_changed_refs, lambda have, want: [])
207 return updates, conflicts
208
209- def _merge_to_local_git(self, target_repo, source_tag_refs,
210- overwrite=False):
211+
212+class InterTagsFromGitToLocalGit(InterTags):
213+
214+ @classmethod
215+ def is_compatible(klass, source, target):
216+ if not isinstance(source, GitTags):
217+ return False
218+ if not isinstance(target, GitTags):
219+ return False
220+ if getattr(target.branch.repository, "_git", None) is None:
221+ return False
222+ return True
223+
224+ def merge(self, overwrite=False, ignore_master=False):
225+ if self.source.branch.repository.has_same_location(self.target.branch.repository):
226+ return {}, []
227+
228 conflicts = []
229 updates = {}
230+ source_tag_refs = self.source.branch.get_tag_refs()
231+
232+ target_repo = self.target.branch.repository
233+
234 for ref_name, tag_name, peeled, unpeeled in source_tag_refs:
235 if target_repo._git.refs.get(ref_name) == unpeeled:
236 pass
237@@ -169,9 +199,10 @@
238 tag_name)
239 continue
240 target_repo._git.refs[ref_name] = unpeeled or peeled
241+ self.target.branch._tag_refs = None
242 else:
243 try:
244- source_revid = self.repository.lookup_foreign_revision_id(
245+ source_revid = self.source.branch.repository.lookup_foreign_revision_id(
246 peeled)
247 target_revid = target_repo.lookup_foreign_revision_id(
248 target_repo._git.refs[ref_name])
249@@ -186,21 +217,39 @@
250 conflicts.append((tag_name, source_revid, target_revid))
251 return updates, conflicts
252
253- def _merge_to_git(self, to_tags, source_tag_refs, overwrite=False):
254- target_repo = to_tags.repository
255- if self.repository.has_same_location(target_repo):
256- return {}, []
257- try:
258- if getattr(target_repo, "_git", None):
259- return self._merge_to_local_git(
260- target_repo, source_tag_refs, overwrite)
261- else:
262- return self._merge_to_remote_git(
263- target_repo, source_tag_refs, overwrite)
264- finally:
265- to_tags.branch._tag_refs = None
266-
267- def _merge_to_non_git(self, to_tags, source_tag_refs, overwrite=False):
268+
269+class InterTagsFromGitToNonGit(InterTags):
270+
271+ @classmethod
272+ def is_compatible(klass, source, target):
273+ if not isinstance(source, GitTags):
274+ return False
275+ if isinstance(target, GitTags):
276+ return False
277+ return True
278+
279+ def merge(self, overwrite=False, ignore_master=False):
280+ """See Tags.merge_to."""
281+ source_tag_refs = self.source.branch.get_tag_refs()
282+ if ignore_master:
283+ master = None
284+ else:
285+ master = self.target.branch.get_master_branch()
286+ with cleanup.ExitStack() as es:
287+ if master is not None:
288+ es.enter_context(master.lock_write())
289+ updates, conflicts = self._merge_to(
290+ self.target, source_tag_refs, overwrite=overwrite)
291+ if master is not None:
292+ extra_updates, extra_conflicts = self._merge_to(
293+ master.tags, overwrite=overwrite,
294+ source_tag_refs=source_tag_refs,
295+ ignore_master=ignore_master)
296+ updates.update(extra_updates)
297+ conflicts += extra_conflicts
298+ return updates, conflicts
299+
300+ def _merge_to(self, to_tags, source_tag_refs, overwrite=False):
301 unpeeled_map = defaultdict(set)
302 conflicts = []
303 updates = {}
304@@ -209,7 +258,7 @@
305 if unpeeled is not None:
306 unpeeled_map[peeled].add(unpeeled)
307 try:
308- bzr_revid = self.branch.lookup_foreign_revision_id(peeled)
309+ bzr_revid = self.source.branch.lookup_foreign_revision_id(peeled)
310 except NotCommitError:
311 continue
312 if result.get(tag_name) == bzr_revid:
313@@ -226,34 +275,18 @@
314 map_file.save_in_repository(to_tags.branch.repository)
315 return updates, conflicts
316
317- def merge_to(self, to_tags, overwrite=False, ignore_master=False,
318- source_tag_refs=None):
319- """See Tags.merge_to."""
320- if source_tag_refs is None:
321- source_tag_refs = self.branch.get_tag_refs()
322- if self == to_tags:
323- return {}, []
324- if isinstance(to_tags, GitTags):
325- return self._merge_to_git(to_tags, source_tag_refs,
326- overwrite=overwrite)
327- else:
328- if ignore_master:
329- master = None
330- else:
331- master = to_tags.branch.get_master_branch()
332- with cleanup.ExitStack() as es:
333- if master is not None:
334- es.enter_context(master.lock_write())
335- updates, conflicts = self._merge_to_non_git(
336- to_tags, source_tag_refs, overwrite=overwrite)
337- if master is not None:
338- extra_updates, extra_conflicts = self.merge_to(
339- master.tags, overwrite=overwrite,
340- source_tag_refs=source_tag_refs,
341- ignore_master=ignore_master)
342- updates.update(extra_updates)
343- conflicts += extra_conflicts
344- return updates, conflicts
345+
346+InterTags.register_optimiser(InterTagsFromGitToRemoteGit)
347+InterTags.register_optimiser(InterTagsFromGitToLocalGit)
348+InterTags.register_optimiser(InterTagsFromGitToNonGit)
349+
350+
351+class GitTags(Tags):
352+ """Ref-based tag dictionary."""
353+
354+ def __init__(self, branch):
355+ self.branch = branch
356+ self.repository = branch.repository
357
358 def get_tag_dict(self):
359 ret = {}
360@@ -267,6 +300,15 @@
361 ret[tag_name] = bzr_revid
362 return ret
363
364+ def lookup_tag(self, tag_name):
365+ """Return the referent string of a tag"""
366+ # TODO(jelmer): Replace with something more efficient for local tags.
367+ td = self.get_tag_dict()
368+ try:
369+ return td[tag_name]
370+ except KeyError:
371+ raise errors.NoSuchTag(tag_name)
372+
373
374 class LocalGitTagDict(GitTags):
375 """Dictionary with tags in a local repository."""
376@@ -1194,7 +1236,6 @@
377 other_branch=self.source)
378 tags_ret = self.source.tags.merge_to(
379 self.target.tags,
380- source_tag_refs=remote_refs_dict_to_tag_refs(refs),
381 overwrite=("tags" in overwrite))
382 if isinstance(tags_ret, tuple):
383 (result.tag_updates, result.tag_conflicts) = tags_ret
384@@ -1243,8 +1284,7 @@
385 (result.old_revid if ("history" not in overwrite) else None),
386 other_branch=self.source)
387 tags_ret = self.source.tags.merge_to(
388- self.target.tags, overwrite=("tags" in overwrite),
389- source_tag_refs=remote_refs_dict_to_tag_refs(refs))
390+ self.target.tags, overwrite=("tags" in overwrite))
391 if isinstance(tags_ret, tuple):
392 (result.tag_updates, result.tag_conflicts) = tags_ret
393 else:
394
395=== modified file 'breezy/tag.py'
396--- breezy/tag.py 2020-02-18 00:46:00 +0000
397+++ breezy/tag.py 2020-02-19 03:31:03 +0000
398@@ -25,22 +25,21 @@
399 from __future__ import absolute_import
400
401 from collections import defaultdict
402+import itertools
403+import re
404+import sys
405
406 # NOTE: I was going to call this tags.py, but vim seems to think all files
407 # called tags* are ctags files... mbp 20070220.
408
409+from .inter import InterObject
410 from .registry import Registry
411 from .sixish import text_type
412 from .lazy_import import lazy_import
413 lazy_import(globals(), """
414-import itertools
415-import re
416-import sys
417
418 from breezy import (
419- bencode,
420 cleanup,
421- trace,
422 )
423 """)
424
425@@ -75,7 +74,7 @@
426 return result, updates, conflicts
427
428
429-class _Tags(object):
430+class Tags(object):
431
432 def __init__(self, branch):
433 self.branch = branch
434@@ -86,20 +85,37 @@
435 raise NotImplementedError(self.get_tag_dict)
436
437 def get_reverse_tag_dict(self):
438- """Return a dictionary mapping revision ids to list of tags.
439- """
440- raise NotImplementedError(self.get_reverse_tag_dict)
441+ """Returns a dict with revisions as keys
442+ and a list of tags for that revision as value"""
443+ d = self.get_tag_dict()
444+ rev = defaultdict(set)
445+ for key in d:
446+ rev[d[key]].add(key)
447+ return rev
448
449 def merge_to(self, to_tags, overwrite=False, ignore_master=False):
450- """Merge new tags from this tags container into another.
451-
452- :param to_tags: Tags container to merge into
453- :param overwrite: Whether to overwrite existing, divergent, tags.
454+ """Copy tags between repositories if necessary and possible.
455+
456+ This method has common command-line behaviour about handling
457+ error cases.
458+
459+ All new definitions are copied across, except that tags that already
460+ exist keep their existing definitions.
461+
462+ :param to_tags: Branch to receive these tags
463+ :param overwrite: Overwrite conflicting tags in the target branch
464 :param ignore_master: Do not modify the tags in the target's master
465 branch (if any). Default is false (so the master will be updated).
466- :return: Tuple with tag updates as dictionary and tag conflicts
467+
468+ :returns: Tuple with tag_updates and tag_conflicts.
469+ tag_updates is a dictionary with new tags, None is used for
470+ removed tags
471+ tag_conflicts is a set of tags that conflicted, each of which is
472+ (tagname, source_target, dest_target), or None if no copying was
473+ done.
474 """
475- raise NotImplementedError(self.merge_to)
476+ intertags = InterTags.get(self, to_tags)
477+ return intertags.merge(overwrite=overwrite, ignore_master=ignore_master)
478
479 def set_tag(self, tag_name, revision):
480 """Set a tag.
481@@ -129,18 +145,21 @@
482 raise NotImplementedError(self.delete_tag)
483
484 def rename_revisions(self, rename_map):
485- """Replace revision ids according to a rename map.
486+ """Rename revisions in this tags dictionary.
487
488- :param rename_map: Dictionary mapping old revision ids to
489- new revision ids.
490+ :param rename_map: Dictionary mapping old revids to new revids
491 """
492- raise NotImplementedError(self.rename_revisions)
493+ reverse_tags = self.get_reverse_tag_dict()
494+ for revid, names in reverse_tags.items():
495+ if revid in rename_map:
496+ for name in names:
497+ self.set_tag(name, rename_map[revid])
498
499 def has_tag(self, tag_name):
500 return tag_name in self.get_tag_dict()
501
502
503-class DisabledTags(_Tags):
504+class DisabledTags(Tags):
505 """Tag storage that refuses to store anything.
506
507 This is used by older formats that can't store tags.
508@@ -168,102 +187,19 @@
509 return {}
510
511
512-class BasicTags(_Tags):
513- """Tag storage in an unversioned branch control file.
514+class InterTags(InterObject):
515+ """Operations between sets of tags.
516 """
517
518- def set_tag(self, tag_name, tag_target):
519- """Add a tag definition to the branch.
520-
521- Behaviour if the tag is already present is not defined (yet).
522- """
523- # all done with a write lock held, so this looks atomic
524- with self.branch.lock_write():
525- master = self.branch.get_master_branch()
526- if master is not None:
527- master.tags.set_tag(tag_name, tag_target)
528- td = self.get_tag_dict()
529- td[tag_name] = tag_target
530- self._set_tag_dict(td)
531-
532- def lookup_tag(self, tag_name):
533- """Return the referent string of a tag"""
534- td = self.get_tag_dict()
535- try:
536- return td[tag_name]
537- except KeyError:
538- raise errors.NoSuchTag(tag_name)
539-
540- def get_tag_dict(self):
541- with self.branch.lock_read():
542- try:
543- tag_content = self.branch._get_tags_bytes()
544- except errors.NoSuchFile:
545- # ugly, but only abentley should see this :)
546- trace.warning('No branch/tags file in %s. '
547- 'This branch was probably created by bzr 0.15pre. '
548- 'Create an empty file to silence this message.'
549- % (self.branch, ))
550- return {}
551- return self._deserialize_tag_dict(tag_content)
552-
553- def get_reverse_tag_dict(self):
554- """Returns a dict with revisions as keys
555- and a list of tags for that revision as value"""
556- d = self.get_tag_dict()
557- rev = defaultdict(set)
558- for key in d:
559- rev[d[key]].add(key)
560- return rev
561-
562- def delete_tag(self, tag_name):
563- """Delete a tag definition.
564- """
565- with self.branch.lock_write():
566- d = self.get_tag_dict()
567- try:
568- del d[tag_name]
569- except KeyError:
570- raise errors.NoSuchTag(tag_name)
571- master = self.branch.get_master_branch()
572- if master is not None:
573- try:
574- master.tags.delete_tag(tag_name)
575- except errors.NoSuchTag:
576- pass
577- self._set_tag_dict(d)
578-
579- def _set_tag_dict(self, new_dict):
580- """Replace all tag definitions
581-
582- WARNING: Calling this on an unlocked branch will lock it, and will
583- replace the tags without warning on conflicts.
584-
585- :param new_dict: Dictionary from tag name to target.
586- """
587- return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict))
588-
589- def _serialize_tag_dict(self, tag_dict):
590- td = dict((k.encode('utf-8'), v)
591- for k, v in tag_dict.items())
592- return bencode.bencode(td)
593-
594- def _deserialize_tag_dict(self, tag_content):
595- """Convert the tag file into a dictionary of tags"""
596- # was a special case to make initialization easy, an empty definition
597- # is an empty dictionary
598- if tag_content == b'':
599- return {}
600- try:
601- r = {}
602- for k, v in bencode.bdecode(tag_content).items():
603- r[k.decode('utf-8')] = v
604- return r
605- except ValueError as e:
606- raise ValueError("failed to deserialize tag dictionary %r: %s"
607- % (tag_content, e))
608-
609- def merge_to(self, to_tags, overwrite=False, ignore_master=False):
610+ _optimisers = []
611+ """The available optimised InterTags types."""
612+
613+ @classmethod
614+ def is_compatible(klass, source, target):
615+ # This is the default implementation
616+ return True
617+
618+ def merge(self, overwrite=False, ignore_master=False):
619 """Copy tags between repositories if necessary and possible.
620
621 This method has common command-line behaviour about handling
622@@ -285,12 +221,12 @@
623 done.
624 """
625 with cleanup.ExitStack() as stack:
626- if self.branch == to_tags.branch:
627+ if self.source.branch == self.target.branch:
628 return {}, []
629- if not self.branch.supports_tags():
630+ if not self.source.branch.supports_tags():
631 # obviously nothing to copy
632 return {}, []
633- source_dict = self.get_tag_dict()
634+ source_dict = self.source.get_tag_dict()
635 if not source_dict:
636 # no tags in the source, and we don't want to clobber anything
637 # that's in the destination
638@@ -307,14 +243,14 @@
639 # Ideally we'd improve this API to report the different conflicts
640 # more clearly to the caller, but we don't want to break plugins
641 # such as bzr-builddeb that use this API.
642- stack.enter_context(to_tags.branch.lock_write())
643+ stack.enter_context(self.target.branch.lock_write())
644 if ignore_master:
645 master = None
646 else:
647- master = to_tags.branch.get_master_branch()
648+ master = self.target.branch.get_master_branch()
649 if master is not None:
650 stack.enter_context(master.lock_write())
651- updates, conflicts = self._merge_to(to_tags, source_dict, overwrite)
652+ updates, conflicts = self._merge_to(self.target, source_dict, overwrite)
653 if master is not None:
654 extra_updates, extra_conflicts = self._merge_to(master.tags,
655 source_dict, overwrite)
656@@ -324,7 +260,8 @@
657 # branch.
658 return updates, set(conflicts)
659
660- def _merge_to(self, to_tags, source_dict, overwrite):
661+ @classmethod
662+ def _merge_to(cls, to_tags, source_dict, overwrite):
663 dest_dict = to_tags.get_tag_dict()
664 result, updates, conflicts = _reconcile_tags(
665 source_dict, dest_dict, overwrite)
666@@ -332,19 +269,8 @@
667 to_tags._set_tag_dict(result)
668 return updates, conflicts
669
670- def rename_revisions(self, rename_map):
671- """Rename revisions in this tags dictionary.
672-
673- :param rename_map: Dictionary mapping old revids to new revids
674- """
675- reverse_tags = self.get_reverse_tag_dict()
676- for revid, names in reverse_tags.items():
677- if revid in rename_map:
678- for name in names:
679- self.set_tag(name, rename_map[revid])
680-
681-
682-class MemoryTags(_Tags):
683+
684+class MemoryTags(Tags):
685
686 def __init__(self, tag_dict):
687 self._tag_dict = tag_dict
688@@ -360,15 +286,6 @@
689 except KeyError:
690 raise errors.NoSuchTag(tag_name)
691
692- def get_reverse_tag_dict(self):
693- """Returns a dict with revisions as keys
694- and a list of tags for that revision as value"""
695- d = self.get_tag_dict()
696- rev = defaultdict(set)
697- for key in d:
698- rev[d[key]].add(key)
699- return rev
700-
701 def set_tag(self, name, revid):
702 self._tag_dict[name] = revid
703
704@@ -442,3 +359,6 @@
705 tag_sort_methods.register("alpha", sort_alpha, 'Sort tags lexicographically.')
706 tag_sort_methods.register("time", sort_time, 'Sort tags chronologically.')
707 tag_sort_methods.default_key = "natural"
708+
709+
710+InterTags.register_optimiser(InterTags)
711
712=== modified file 'breezy/tests/test_tag.py'
713--- breezy/tests/test_tag.py 2019-02-01 16:29:45 +0000
714+++ breezy/tests/test_tag.py 2020-02-19 03:31:03 +0000
715@@ -22,10 +22,12 @@
716 errors,
717 )
718 from breezy.tag import (
719- BasicTags,
720 DisabledTags,
721 MemoryTags,
722 )
723+from breezy.bzr.tag import (
724+ BasicTags,
725+ )
726 from breezy.tests import (
727 TestCase,
728 TestCaseWithTransport,

Subscribers

People subscribed via source and target branches