/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tag.py

  • Committer: Jelmer Vernooij
  • Date: 2019-06-02 02:35:46 UTC
  • mfrom: (7309 work)
  • mto: This revision was merged to the branch mainline in revision 7319.
  • Revision ID: jelmer@jelmer.uk-20190602023546-lqco868tnv26d8ow
merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
 
25
25
from __future__ import absolute_import
26
26
 
 
27
from collections import defaultdict
 
28
 
27
29
# NOTE: I was going to call this tags.py, but vim seems to think all files
28
30
# called tags* are ctags files... mbp 20070220.
29
31
 
44
46
""")
45
47
 
46
48
 
 
49
def _reconcile_tags(source_dict, dest_dict, overwrite):
 
50
    """Do a two-way merge of two tag dictionaries.
 
51
 
 
52
    * only in source => source value
 
53
    * only in destination => destination value
 
54
    * same definitions => that
 
55
    * different definitions => if overwrite is False, keep destination
 
56
      value and add to conflict list, otherwise use the source value
 
57
 
 
58
    :returns: (result_dict, updates,
 
59
        [(conflicting_tag, source_target, dest_target)])
 
60
    """
 
61
    conflicts = []
 
62
    updates = {}
 
63
    result = dict(dest_dict)  # copy
 
64
    for name, target in source_dict.items():
 
65
        if result.get(name) == target:
 
66
            pass
 
67
        elif name not in result or overwrite:
 
68
            updates[name] = target
 
69
            result[name] = target
 
70
        else:
 
71
            conflicts.append((name, target, result[name]))
 
72
    return result, updates, conflicts
 
73
 
 
74
 
47
75
class _Tags(object):
48
76
 
49
77
    def __init__(self, branch):
148
176
        Behaviour if the tag is already present is not defined (yet).
149
177
        """
150
178
        # all done with a write lock held, so this looks atomic
151
 
        self.branch.lock_write()
152
 
        try:
 
179
        with self.branch.lock_write():
153
180
            master = self.branch.get_master_branch()
154
181
            if master is not None:
155
182
                master.tags.set_tag(tag_name, tag_target)
156
183
            td = self.get_tag_dict()
157
184
            td[tag_name] = tag_target
158
185
            self._set_tag_dict(td)
159
 
        finally:
160
 
            self.branch.unlock()
161
186
 
162
187
    def lookup_tag(self, tag_name):
163
188
        """Return the referent string of a tag"""
171
196
        with self.branch.lock_read():
172
197
            try:
173
198
                tag_content = self.branch._get_tags_bytes()
174
 
            except errors.NoSuchFile as e:
 
199
            except errors.NoSuchFile:
175
200
                # ugly, but only abentley should see this :)
176
201
                trace.warning('No branch/tags file in %s.  '
177
 
                     'This branch was probably created by bzr 0.15pre.  '
178
 
                     'Create an empty file to silence this message.'
179
 
                     % (self.branch, ))
 
202
                              'This branch was probably created by bzr 0.15pre.  '
 
203
                              'Create an empty file to silence this message.'
 
204
                              % (self.branch, ))
180
205
                return {}
181
206
            return self._deserialize_tag_dict(tag_content)
182
207
 
184
209
        """Returns a dict with revisions as keys
185
210
           and a list of tags for that revision as value"""
186
211
        d = self.get_tag_dict()
187
 
        rev = {}
 
212
        rev = defaultdict(set)
188
213
        for key in d:
189
 
            try:
190
 
                rev[d[key]].append(key)
191
 
            except KeyError:
192
 
                rev[d[key]] = [key]
 
214
            rev[d[key]].add(key)
193
215
        return rev
194
216
 
195
217
    def delete_tag(self, tag_name):
196
218
        """Delete a tag definition.
197
219
        """
198
 
        self.branch.lock_write()
199
 
        try:
 
220
        with self.branch.lock_write():
200
221
            d = self.get_tag_dict()
201
222
            try:
202
223
                del d[tag_name]
209
230
                except errors.NoSuchTag:
210
231
                    pass
211
232
            self._set_tag_dict(d)
212
 
        finally:
213
 
            self.branch.unlock()
214
233
 
215
234
    def _set_tag_dict(self, new_dict):
216
235
        """Replace all tag definitions
240
259
            return r
241
260
        except ValueError as e:
242
261
            raise ValueError("failed to deserialize tag dictionary %r: %s"
243
 
                % (tag_content, e))
 
262
                             % (tag_content, e))
244
263
 
245
264
    def merge_to(self, to_tags, overwrite=False, ignore_master=False):
246
265
        """Copy tags between repositories if necessary and possible.
301
320
        updates, conflicts = self._merge_to(to_tags, source_dict, overwrite)
302
321
        if master is not None:
303
322
            extra_updates, extra_conflicts = self._merge_to(master.tags,
304
 
                source_dict, overwrite)
 
323
                                                            source_dict, overwrite)
305
324
            updates.update(extra_updates)
306
325
            conflicts += extra_conflicts
307
326
        # We use set() to remove any duplicate conflicts from the master
310
329
 
311
330
    def _merge_to(self, to_tags, source_dict, overwrite):
312
331
        dest_dict = to_tags.get_tag_dict()
313
 
        result, updates, conflicts = self._reconcile_tags(source_dict,
314
 
            dest_dict, overwrite)
 
332
        result, updates, conflicts = _reconcile_tags(
 
333
            source_dict, dest_dict, overwrite)
315
334
        if result != dest_dict:
316
335
            to_tags._set_tag_dict(result)
317
336
        return updates, conflicts
327
346
                for name in names:
328
347
                    self.set_tag(name, rename_map[revid])
329
348
 
330
 
    def _reconcile_tags(self, source_dict, dest_dict, overwrite):
331
 
        """Do a two-way merge of two tag dictionaries.
332
 
 
333
 
        * only in source => source value
334
 
        * only in destination => destination value
335
 
        * same definitions => that
336
 
        * different definitions => if overwrite is False, keep destination
337
 
          value and give a warning, otherwise use the source value
338
 
 
339
 
        :returns: (result_dict, updates,
340
 
            [(conflicting_tag, source_target, dest_target)])
341
 
        """
342
 
        conflicts = []
343
 
        updates = {}
344
 
        result = dict(dest_dict) # copy
345
 
        for name, target in source_dict.items():
346
 
            if result.get(name) == target:
347
 
                pass
348
 
            elif name not in result or overwrite:
349
 
                updates[name] = target
350
 
                result[name] = target
351
 
            else:
352
 
                conflicts.append((name, target, result[name]))
353
 
        return result, updates, conflicts
 
349
 
 
350
class MemoryTags(_Tags):
 
351
 
 
352
    def __init__(self, tag_dict):
 
353
        self._tag_dict = tag_dict
 
354
 
 
355
    def get_tag_dict(self):
 
356
        return self._tag_dict
 
357
 
 
358
    def lookup_tag(self, tag_name):
 
359
        """Return the referent string of a tag"""
 
360
        td = self.get_tag_dict()
 
361
        try:
 
362
            return td[tag_name]
 
363
        except KeyError:
 
364
            raise errors.NoSuchTag(tag_name)
 
365
 
 
366
    def get_reverse_tag_dict(self):
 
367
        """Returns a dict with revisions as keys
 
368
           and a list of tags for that revision as value"""
 
369
        d = self.get_tag_dict()
 
370
        rev = defaultdict(set)
 
371
        for key in d:
 
372
            rev[d[key]].add(key)
 
373
        return rev
 
374
 
 
375
    def set_tag(self, name, revid):
 
376
        self._tag_dict[name] = revid
 
377
 
 
378
    def delete_tag(self, name):
 
379
        try:
 
380
            del self._tag_dict[name]
 
381
        except KeyError:
 
382
            raise errors.NoSuchTag(name)
 
383
 
 
384
    def rename_revisions(self, revid_map):
 
385
        self._tag_dict = {
 
386
            name: revid_map.get(revid, revid)
 
387
            for name, revid in self._tag_dict.items()}
 
388
 
 
389
    def _set_tag_dict(self, result):
 
390
        self._tag_dict = dict(result.items())
 
391
 
 
392
    def merge_to(self, to_tags, overwrite=False, ignore_master=False):
 
393
        source_dict = self.get_tag_dict()
 
394
        dest_dict = to_tags.get_tag_dict()
 
395
        result, updates, conflicts = _reconcile_tags(
 
396
            source_dict, dest_dict, overwrite)
 
397
        if result != dest_dict:
 
398
            to_tags._set_tag_dict(result)
 
399
        return updates, conflicts
354
400
 
355
401
 
356
402
def sort_natural(branch, tags):
362
408
    def natural_sort_key(tag):
363
409
        return [f(s) for f, s in
364
410
                zip(itertools.cycle((text_type.lower, int)),
365
 
                                    re.split('([0-9]+)', tag[0]))]
 
411
                    re.split('([0-9]+)', tag[0]))]
366
412
    tags.sort(key=natural_sort_key)
367
413
 
368
414
 
386
432
        try:
387
433
            revobj = branch.repository.get_revision(revid)
388
434
        except errors.NoSuchRevision:
389
 
            timestamp = sys.maxsize # place them at the end
 
435
            timestamp = sys.maxsize  # place them at the end
390
436
        else:
391
437
            timestamp = revobj.timestamp
392
438
        timestamps[revid] = timestamp
395
441
 
396
442
tag_sort_methods = Registry()
397
443
tag_sort_methods.register("natural", sort_natural,
398
 
    'Sort numeric substrings as numbers. (default)')
 
444
                          'Sort numeric substrings as numbers. (default)')
399
445
tag_sort_methods.register("alpha", sort_alpha, 'Sort tags lexicographically.')
400
446
tag_sort_methods.register("time", sort_time, 'Sort tags chronologically.')
401
447
tag_sort_methods.default_key = "natural"