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 |
Related bugs: |
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 : | # |
Merging failed
https:/
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, |
There was nothing to merge /ci.breezy- vcs.org/ job/brz- 3.1/job/ brz-3.1- land/6/
https:/