~bzr-pqm/bzr/bzr.dev

2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
1
# Copyright (C) 2007 Canonical Ltd
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
2220.2.14 by mbp at sourcefrog
cleanup
28
2220.2.28 by Martin Pool
Integrate tags with Branch6:
29
from warnings import warn
30
3224.5.20 by Andrew Bennetts
Remove or lazyify a couple more imports.
31
from bzrlib.lazy_import import lazy_import
32
lazy_import(globals(), """
33
from bzrlib.util import bencode
34
""")
35
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
36
from bzrlib import (
37
    errors,
2220.2.27 by Martin Pool
Start adding tags to Branch6
38
    trace,
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
39
    )
40
41
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
42
class _Tags(object):
2220.2.28 by Martin Pool
Integrate tags with Branch6:
43
2220.2.14 by mbp at sourcefrog
cleanup
44
    def __init__(self, branch):
45
        self.branch = branch
46
2220.2.42 by Martin Pool
Tag command refuses to replace existing tags unless you force it.
47
    def has_tag(self, tag_name):
48
        return self.get_tag_dict().has_key(tag_name)
49
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
50
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
51
class DisabledTags(_Tags):
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
52
    """Tag storage that refuses to store anything.
53
54
    This is used by older formats that can't store tags.
55
    """
56
57
    def _not_supported(self, *a, **k):
2220.2.14 by mbp at sourcefrog
cleanup
58
        raise errors.TagsNotSupported(self.branch)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
59
60
    set_tag = _not_supported
61
    get_tag_dict = _not_supported
62
    _set_tag_dict = _not_supported
63
    lookup_tag = _not_supported
2220.2.21 by Martin Pool
Add tag --delete command and implementation
64
    delete_tag = _not_supported
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
65
2810.1.1 by Martin Pool
merge push|pull --overwrite and tweak
66
    def merge_to(self, to_tags, overwrite=False):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
67
        # we never have anything to copy
68
        pass
69
2831.8.1 by James Westby
Fix log against smart server branches that don't support tags. (#140615)
70
    def get_reverse_tag_dict(self):
71
        # There aren't any tags, so the reverse mapping is empty.
72
        return {}
73
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
74
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
75
class BasicTags(_Tags):
2220.2.14 by mbp at sourcefrog
cleanup
76
    """Tag storage in an unversioned branch control file.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
77
    """
78
79
    def set_tag(self, tag_name, tag_target):
2220.2.14 by mbp at sourcefrog
cleanup
80
        """Add a tag definition to the branch.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
81
82
        Behaviour if the tag is already present is not defined (yet).
83
        """
84
        # all done with a write lock held, so this looks atomic
2220.2.14 by mbp at sourcefrog
cleanup
85
        self.branch.lock_write()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
86
        try:
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
87
            master = self.branch.get_master_branch()
88
            if master is not None:
89
                master.tags.set_tag(tag_name, tag_target)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
90
            td = self.get_tag_dict()
91
            td[tag_name] = tag_target
92
            self._set_tag_dict(td)
93
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
94
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
95
96
    def lookup_tag(self, tag_name):
97
        """Return the referent string of a tag"""
98
        td = self.get_tag_dict()
99
        try:
100
            return td[tag_name]
101
        except KeyError:
102
            raise errors.NoSuchTag(tag_name)
103
104
    def get_tag_dict(self):
2220.2.14 by mbp at sourcefrog
cleanup
105
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
106
        try:
2220.2.27 by Martin Pool
Start adding tags to Branch6
107
            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.
108
                tag_content = self.branch._get_tags_bytes()
2220.2.27 by Martin Pool
Start adding tags to Branch6
109
            except errors.NoSuchFile, e:
2220.2.28 by Martin Pool
Integrate tags with Branch6:
110
                # ugly, but only abentley should see this :)
111
                trace.warning('No branch/tags file in %s.  '
2220.2.27 by Martin Pool
Start adding tags to Branch6
112
                     'This branch was probably created by bzr 0.15pre.  '
2220.2.28 by Martin Pool
Integrate tags with Branch6:
113
                     'Create an empty file to silence this message.'
114
                     % (self.branch, ))
2220.2.27 by Martin Pool
Start adding tags to Branch6
115
                return {}
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
116
            return self._deserialize_tag_dict(tag_content)
117
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
118
            self.branch.unlock()
2388.1.11 by Alexander Belchenko
changes after John's review
119
2388.1.1 by Erik Bagfors
created reverse_tag_dict in tags.py
120
    def get_reverse_tag_dict(self):
121
        """Returns a dict with revisions as keys
122
           and a list of tags for that revision as value"""
123
        d = self.get_tag_dict()
124
        rev = {}
125
        for key in d:
126
            try:
127
                rev[d[key]].append(key)
128
            except KeyError:
129
                rev[d[key]] = [key]
130
        return rev
131
2220.2.21 by Martin Pool
Add tag --delete command and implementation
132
    def delete_tag(self, tag_name):
133
        """Delete a tag definition.
134
        """
135
        self.branch.lock_write()
136
        try:
137
            d = self.get_tag_dict()
138
            try:
139
                del d[tag_name]
140
            except KeyError:
141
                raise errors.NoSuchTag(tag_name)
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
142
            master = self.branch.get_master_branch()
143
            if master is not None:
144
                try:
145
                    master.tags.delete_tag(tag_name)
146
                except errors.NoSuchTag:
147
                    pass
2220.2.21 by Martin Pool
Add tag --delete command and implementation
148
            self._set_tag_dict(d)
149
        finally:
150
            self.branch.unlock()
151
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
152
    def _set_tag_dict(self, new_dict):
153
        """Replace all tag definitions
154
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.
155
        WARNING: Calling this on an unlocked branch will lock it, and will
156
        replace the tags without warning on conflicts.
157
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
158
        :param new_dict: Dictionary from tag name to target.
159
        """
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.
160
        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
161
162
    def _serialize_tag_dict(self, tag_dict):
2220.2.21 by Martin Pool
Add tag --delete command and implementation
163
        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.
164
                  for k,v in tag_dict.items())
2220.2.21 by Martin Pool
Add tag --delete command and implementation
165
        return bencode.bencode(td)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
166
167
    def _deserialize_tag_dict(self, tag_content):
168
        """Convert the tag file into a dictionary of tags"""
2220.2.28 by Martin Pool
Integrate tags with Branch6:
169
        # 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
170
        # is an empty dictionary
171
        if tag_content == '':
172
            return {}
173
        try:
2220.2.21 by Martin Pool
Add tag --delete command and implementation
174
            r = {}
175
            for k, v in bencode.bdecode(tag_content).items():
176
                r[k.decode('utf-8')] = v
177
            return r
178
        except ValueError, e:
179
            raise ValueError("failed to deserialize tag dictionary %r: %s"
2220.2.32 by Martin Pool
Slightly smarter tag merge
180
                % (tag_content, e))
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
181
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
182
    def merge_to(self, to_tags, overwrite=False):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
183
        """Copy tags between repositories if necessary and possible.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
184
185
        This method has common command-line behaviour about handling
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
186
        error cases.
187
2220.2.32 by Martin Pool
Slightly smarter tag merge
188
        All new definitions are copied across, except that tags that already
189
        exist keep their existing definitions.
190
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
191
        :param to_tags: Branch to receive these tags
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
192
        :param overwrite: Overwrite conflicting tags in the target branch
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
193
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
194
        :returns: A list 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.
195
            (tagname, source_target, dest_target), or None if no copying was
196
            done.
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
197
        """
198
        if self.branch == to_tags.branch:
199
            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.
200
        if not self.branch.supports_tags():
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
201
            # obviously nothing to copy
202
            return
2220.2.32 by Martin Pool
Slightly smarter tag merge
203
        source_dict = self.get_tag_dict()
204
        if not source_dict:
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
205
            # no tags in the source, and we don't want to clobber anything
206
            # that's in the destination
207
            return
208
        to_tags.branch.lock_write()
209
        try:
2220.2.32 by Martin Pool
Slightly smarter tag merge
210
            dest_dict = to_tags.get_tag_dict()
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
211
            result, conflicts = self._reconcile_tags(source_dict, dest_dict,
212
                                                     overwrite)
2220.2.32 by Martin Pool
Slightly smarter tag merge
213
            if result != dest_dict:
214
                to_tags._set_tag_dict(result)
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
215
        finally:
216
            to_tags.branch.unlock()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
217
        return conflicts
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
218
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
219
    def _reconcile_tags(self, source_dict, dest_dict, overwrite):
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
220
        """Do a two-way merge of two tag dictionaries.
221
222
        only in source => source value
223
        only in destination => destination value
224
        same definitions => that
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
225
        different definitions => if overwrite is False, keep destination
226
            value and give a warning, otherwise use the source value
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
227
228
        :returns: (result_dict,
229
            [(conflicting_tag, source_target, dest_target)])
230
        """
231
        conflicts = []
232
        result = dict(dest_dict) # copy
233
        for name, target in source_dict.items():
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
234
            if name not in result or overwrite:
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
235
                result[name] = target
236
            elif result[name] == target:
237
                pass
238
            else:
239
                conflicts.append((name, target, result[name]))
240
        return result, conflicts
2220.2.32 by Martin Pool
Slightly smarter tag merge
241
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
242
243
def _merge_tags_if_possible(from_branch, to_branch):
244
    from_branch.tags.merge_to(to_branch.tags)