~bzr-pqm/bzr/bzr.dev

5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2007-2011 Canonical Ltd
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
16
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
17
"""Tag strategies.
18
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
19
These are contained within a branch and normally constructed
20
when the branch is opened.  Clients should typically do
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
21
22
  Branch.tags.add('name', 'value')
23
"""
24
25
# NOTE: I was going to call this tags.py, but vim seems to think all files
26
# called tags* are ctags files... mbp 20070220.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
27
5582.2.1 by Jelmer Vernooij
support extending --sort argument to 'bzr tags'.
28
from bzrlib.registry import Registry
29
from bzrlib.lazy_import import lazy_import
30
lazy_import(globals(), """
31
import itertools
32
import re
33
import sys
2220.2.14 by mbp at sourcefrog
cleanup
34
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
35
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
36
    bencode,
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
37
    cleanup,
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
38
    errors,
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
39
    symbol_versioning,
2220.2.27 by Martin Pool
Start adding tags to Branch6
40
    trace,
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
41
    )
5582.2.1 by Jelmer Vernooij
support extending --sort argument to 'bzr tags'.
42
""")
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
43
44
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
45
class _Tags(object):
2220.2.28 by Martin Pool
Integrate tags with Branch6:
46
2220.2.14 by mbp at sourcefrog
cleanup
47
    def __init__(self, branch):
48
        self.branch = branch
49
2220.2.42 by Martin Pool
Tag command refuses to replace existing tags unless you force it.
50
    def has_tag(self, tag_name):
51
        return self.get_tag_dict().has_key(tag_name)
52
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
53
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
54
class DisabledTags(_Tags):
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
55
    """Tag storage that refuses to store anything.
56
57
    This is used by older formats that can't store tags.
58
    """
59
60
    def _not_supported(self, *a, **k):
2220.2.14 by mbp at sourcefrog
cleanup
61
        raise errors.TagsNotSupported(self.branch)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
62
63
    set_tag = _not_supported
64
    get_tag_dict = _not_supported
65
    _set_tag_dict = _not_supported
66
    lookup_tag = _not_supported
2220.2.21 by Martin Pool
Add tag --delete command and implementation
67
    delete_tag = _not_supported
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
68
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
69
    def merge_to(self, to_tags, overwrite=False, ignore_master=False):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
70
        # we never have anything to copy
71
        pass
72
4325.2.1 by Jelmer Vernooij
Add Tags.rename_revisions().
73
    def rename_revisions(self, rename_map):
74
        # No tags, so nothing to rename
75
        pass
76
2831.8.1 by James Westby
Fix log against smart server branches that don't support tags. (#140615)
77
    def get_reverse_tag_dict(self):
78
        # There aren't any tags, so the reverse mapping is empty.
79
        return {}
80
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
81
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
82
class BasicTags(_Tags):
2220.2.14 by mbp at sourcefrog
cleanup
83
    """Tag storage in an unversioned branch control file.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
84
    """
85
86
    def set_tag(self, tag_name, tag_target):
2220.2.14 by mbp at sourcefrog
cleanup
87
        """Add a tag definition to the branch.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
88
89
        Behaviour if the tag is already present is not defined (yet).
90
        """
91
        # all done with a write lock held, so this looks atomic
2220.2.14 by mbp at sourcefrog
cleanup
92
        self.branch.lock_write()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
93
        try:
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
94
            master = self.branch.get_master_branch()
95
            if master is not None:
96
                master.tags.set_tag(tag_name, tag_target)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
97
            td = self.get_tag_dict()
98
            td[tag_name] = tag_target
99
            self._set_tag_dict(td)
100
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
101
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
102
103
    def lookup_tag(self, tag_name):
104
        """Return the referent string of a tag"""
105
        td = self.get_tag_dict()
106
        try:
107
            return td[tag_name]
108
        except KeyError:
109
            raise errors.NoSuchTag(tag_name)
110
111
    def get_tag_dict(self):
2220.2.14 by mbp at sourcefrog
cleanup
112
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
113
        try:
2220.2.27 by Martin Pool
Start adding tags to Branch6
114
            try:
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
115
                tag_content = self.branch._get_tags_bytes()
2220.2.27 by Martin Pool
Start adding tags to Branch6
116
            except errors.NoSuchFile, e:
2220.2.28 by Martin Pool
Integrate tags with Branch6:
117
                # ugly, but only abentley should see this :)
118
                trace.warning('No branch/tags file in %s.  '
2220.2.27 by Martin Pool
Start adding tags to Branch6
119
                     'This branch was probably created by bzr 0.15pre.  '
2220.2.28 by Martin Pool
Integrate tags with Branch6:
120
                     'Create an empty file to silence this message.'
121
                     % (self.branch, ))
2220.2.27 by Martin Pool
Start adding tags to Branch6
122
                return {}
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
123
            return self._deserialize_tag_dict(tag_content)
124
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
125
            self.branch.unlock()
2388.1.11 by Alexander Belchenko
changes after John's review
126
2388.1.1 by Erik Bagfors
created reverse_tag_dict in tags.py
127
    def get_reverse_tag_dict(self):
128
        """Returns a dict with revisions as keys
129
           and a list of tags for that revision as value"""
130
        d = self.get_tag_dict()
131
        rev = {}
132
        for key in d:
133
            try:
134
                rev[d[key]].append(key)
135
            except KeyError:
136
                rev[d[key]] = [key]
137
        return rev
138
2220.2.21 by Martin Pool
Add tag --delete command and implementation
139
    def delete_tag(self, tag_name):
140
        """Delete a tag definition.
141
        """
142
        self.branch.lock_write()
143
        try:
144
            d = self.get_tag_dict()
145
            try:
146
                del d[tag_name]
147
            except KeyError:
148
                raise errors.NoSuchTag(tag_name)
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
149
            master = self.branch.get_master_branch()
150
            if master is not None:
151
                try:
152
                    master.tags.delete_tag(tag_name)
153
                except errors.NoSuchTag:
154
                    pass
2220.2.21 by Martin Pool
Add tag --delete command and implementation
155
            self._set_tag_dict(d)
156
        finally:
157
            self.branch.unlock()
158
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
159
    def _set_tag_dict(self, new_dict):
160
        """Replace all tag definitions
161
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
162
        WARNING: Calling this on an unlocked branch will lock it, and will
163
        replace the tags without warning on conflicts.
164
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
165
        :param new_dict: Dictionary from tag name to target.
166
        """
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
167
        return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict))
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
168
169
    def _serialize_tag_dict(self, tag_dict):
2220.2.21 by Martin Pool
Add tag --delete command and implementation
170
        td = dict((k.encode('utf-8'), v)
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
171
                  for k,v in tag_dict.items())
2220.2.21 by Martin Pool
Add tag --delete command and implementation
172
        return bencode.bencode(td)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
173
174
    def _deserialize_tag_dict(self, tag_content):
175
        """Convert the tag file into a dictionary of tags"""
2220.2.28 by Martin Pool
Integrate tags with Branch6:
176
        # was a special case to make initialization easy, an empty definition
2220.2.15 by mbp at sourcefrog
Store tag dictionary in bencode and accomodate non-ascii tags
177
        # is an empty dictionary
178
        if tag_content == '':
179
            return {}
180
        try:
2220.2.21 by Martin Pool
Add tag --delete command and implementation
181
            r = {}
182
            for k, v in bencode.bdecode(tag_content).items():
183
                r[k.decode('utf-8')] = v
184
            return r
185
        except ValueError, e:
186
            raise ValueError("failed to deserialize tag dictionary %r: %s"
2220.2.32 by Martin Pool
Slightly smarter tag merge
187
                % (tag_content, e))
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
188
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
189
    def merge_to(self, to_tags, overwrite=False, ignore_master=False):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
190
        """Copy tags between repositories if necessary and possible.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
191
192
        This method has common command-line behaviour about handling
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
193
        error cases.
194
2220.2.32 by Martin Pool
Slightly smarter tag merge
195
        All new definitions are copied across, except that tags that already
196
        exist keep their existing definitions.
197
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
198
        :param to_tags: Branch to receive these tags
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
199
        :param overwrite: Overwrite conflicting tags in the target branch
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
200
        :param ignore_master: Do not modify the tags in the target's master
201
            branch (if any).  Default is false (so the master will be updated).
5540.4.2 by Andrew Bennetts
Remove references to 2.2.2 in comments and warnings: this patch was not accepted for 2.2.
202
            New in bzr 2.3.
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
203
5540.4.3 by Andrew Bennetts
Simplify slightly, we don't need to be quite so paranoid about API breakage in the development branch.
204
        :returns: A set of tags that conflicted, each of which is
3482.1.1 by John Arbash Meinel
Fix bug #238149, RemoteBranch.pull needs to return the _real_branch's pull result.
205
            (tagname, source_target, dest_target), or None if no copying was
206
            done.
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
207
        """
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
208
        operation = cleanup.OperationWithCleanups(self._merge_to_operation)
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
209
        return operation.run(to_tags, overwrite, ignore_master)
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
210
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
211
    def _merge_to_operation(self, operation, to_tags, overwrite, ignore_master):
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
212
        add_cleanup = operation.add_cleanup
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
213
        if self.branch == to_tags.branch:
214
            return
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
215
        if not self.branch.supports_tags():
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
216
            # obviously nothing to copy
217
            return
2220.2.32 by Martin Pool
Slightly smarter tag merge
218
        source_dict = self.get_tag_dict()
219
        if not source_dict:
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
220
            # no tags in the source, and we don't want to clobber anything
221
            # that's in the destination
222
            return
5050.53.1 by Andrew Bennetts
Tags.merge_to now updates the master branch as well, if any.
223
        # We merge_to both master and child individually.
5050.53.2 by Andrew Bennetts
More tests, more description of test intent, and specify exactly what happens with duplicate vs. different conflicts.
224
        #
225
        # It's possible for master and child to have differing sets of
5050.53.1 by Andrew Bennetts
Tags.merge_to now updates the master branch as well, if any.
226
        # tags, in which case it's possible to have different sets of
227
        # conflicts.  We report the union of both conflict sets.  In
228
        # that case it's likely the child and master have accepted
5050.53.2 by Andrew Bennetts
More tests, more description of test intent, and specify exactly what happens with duplicate vs. different conflicts.
229
        # different tags from the source, which may be a surprising result, but
230
        # the best we can do in the circumstances.
231
        #
5050.53.1 by Andrew Bennetts
Tags.merge_to now updates the master branch as well, if any.
232
        # Ideally we'd improve this API to report the different conflicts
233
        # more clearly to the caller, but we don't want to break plugins
234
        # such as bzr-builddeb that use this API.
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
235
        add_cleanup(to_tags.branch.lock_write().unlock)
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
236
        if ignore_master:
237
            master = None
238
        else:
239
            master = to_tags.branch.get_master_branch()
5050.53.3 by Andrew Bennetts
Use add_cleanup for simpler and more correct unlocking.
240
        if master is not None:
241
            add_cleanup(master.lock_write().unlock)
242
        conflicts = self._merge_to(to_tags, source_dict, overwrite)
243
        if master is not None:
244
            conflicts += self._merge_to(master.tags, source_dict,
245
                overwrite)
5050.53.2 by Andrew Bennetts
More tests, more description of test intent, and specify exactly what happens with duplicate vs. different conflicts.
246
        # We use set() to remove any duplicate conflicts from the master
5540.4.3 by Andrew Bennetts
Simplify slightly, we don't need to be quite so paranoid about API breakage in the development branch.
247
        # branch.
248
        return set(conflicts)
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
249
5050.53.1 by Andrew Bennetts
Tags.merge_to now updates the master branch as well, if any.
250
    def _merge_to(self, to_tags, source_dict, overwrite):
251
        dest_dict = to_tags.get_tag_dict()
252
        result, conflicts = self._reconcile_tags(source_dict, dest_dict,
253
                                                 overwrite)
254
        if result != dest_dict:
255
            to_tags._set_tag_dict(result)
256
        return conflicts
257
4325.2.1 by Jelmer Vernooij
Add Tags.rename_revisions().
258
    def rename_revisions(self, rename_map):
259
        """Rename revisions in this tags dictionary.
260
        
261
        :param rename_map: Dictionary mapping old revids to new revids
262
        """
263
        reverse_tags = self.get_reverse_tag_dict()
264
        for revid, names in reverse_tags.iteritems():
265
            if revid in rename_map:
266
                for name in names:
267
                    self.set_tag(name, rename_map[revid])
268
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
269
    def _reconcile_tags(self, source_dict, dest_dict, overwrite):
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
270
        """Do a two-way merge of two tag dictionaries.
271
272
        only in source => source value
273
        only in destination => destination value
274
        same definitions => that
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
275
        different definitions => if overwrite is False, keep destination
276
            value and give a warning, otherwise use the source value
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
277
278
        :returns: (result_dict,
279
            [(conflicting_tag, source_target, dest_target)])
280
        """
281
        conflicts = []
282
        result = dict(dest_dict) # copy
283
        for name, target in source_dict.items():
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
284
            if name not in result or overwrite:
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
285
                result[name] = target
286
            elif result[name] == target:
287
                pass
288
            else:
289
                conflicts.append((name, target, result[name]))
290
        return result, conflicts
2220.2.32 by Martin Pool
Slightly smarter tag merge
291
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
292
5609.25.3 by Andrew Bennetts
Alternative fix: cache the result of get_master_branch for the lifetime of the branch lock.
293
def _merge_tags_if_possible(from_branch, to_branch, ignore_master=False):
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
294
    # Try hard to support merge_to implementations that don't expect
5540.4.2 by Andrew Bennetts
Remove references to 2.2.2 in comments and warnings: this patch was not accepted for 2.2.
295
    # 'ignore_master' (new in bzr 2.3).  First, if the flag isn't set then we
296
    # can safely avoid passing ignore_master at all.
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
297
    if not ignore_master:
5609.25.3 by Andrew Bennetts
Alternative fix: cache the result of get_master_branch for the lifetime of the branch lock.
298
        from_branch.tags.merge_to(to_branch.tags)
299
        return
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
300
    # If the flag is set, try to pass it, but be ready to catch TypeError.
301
    try:
5609.25.3 by Andrew Bennetts
Alternative fix: cache the result of get_master_branch for the lifetime of the branch lock.
302
        from_branch.tags.merge_to(to_branch.tags, ignore_master=ignore_master)
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
303
    except TypeError:
304
        # Probably this implementation of 'merge_to' is from a plugin that
305
        # doesn't expect the 'ignore_master' keyword argument (e.g. bzr-svn
306
        # 1.0.4).  There's a small risk that the TypeError is actually caused
307
        # by a completely different problem (which is why we don't catch it for
308
        # the ignore_master=False case), but even then there's probably no harm
309
        # in calling a second time.
310
        symbol_versioning.warn(
5540.4.2 by Andrew Bennetts
Remove references to 2.2.2 in comments and warnings: this patch was not accepted for 2.2.
311
            symbol_versioning.deprecated_in((2,3)) % (
5050.53.9 by Andrew Bennetts
Cosmetic tweak to deprecation.
312
                "Tags.merge_to (of %r) that doesn't accept ignore_master kwarg"
313
                % (from_branch.tags,),),
5050.53.4 by Andrew Bennetts
Don't propagate tags to the master branch during cmd_merge.
314
            DeprecationWarning)
5609.25.3 by Andrew Bennetts
Alternative fix: cache the result of get_master_branch for the lifetime of the branch lock.
315
        from_branch.tags.merge_to(to_branch.tags)
5086.4.1 by Jelmer Vernooij
New function ``determine_tag_name`` that can be used to find a
316
5582.2.1 by Jelmer Vernooij
support extending --sort argument to 'bzr tags'.
317
318
def sort_natural(branch, tags):
319
    """Sort tags, with numeric substrings as numbers.
320
321
    :param branch: Branch
322
    :param tags: List of tuples with tag name and revision id.
323
    """
324
    def natural_sort_key(tag):
325
        return [f(s) for f,s in
326
                zip(itertools.cycle((unicode.lower,int)),
327
                                    re.split('([0-9]+)', tag[0]))]
328
    tags.sort(key=natural_sort_key)
329
330
331
def sort_alpha(branch, tags):
332
    """Sort tags lexicographically, in place.
333
334
    :param branch: Branch
335
    :param tags: List of tuples with tag name and revision id.
336
    """
337
    tags.sort()
338
339
340
def sort_time(branch, tags):
341
    """Sort tags by time inline.
342
343
    :param branch: Branch
344
    :param tags: List of tuples with tag name and revision id.
345
    """
346
    timestamps = {}
347
    for tag, revid in tags:
348
        try:
349
            revobj = branch.repository.get_revision(revid)
350
        except errors.NoSuchRevision:
351
            timestamp = sys.maxint # place them at the end
352
        else:
353
            timestamp = revobj.timestamp
354
        timestamps[revid] = timestamp
355
    tags.sort(key=lambda x: timestamps[x[1]])
356
357
358
tag_sort_methods = Registry()
359
tag_sort_methods.register("natural", sort_natural,
360
    'Sort numeric substrings as numbers. (default)')
361
tag_sort_methods.register("alpha", sort_alpha, 'Sort tags lexicographically.')
362
tag_sort_methods.register("time", sort_time, 'Sort tags chronologically.')
363
tag_sort_methods.default_key = "natural"