/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
17
"""Tag strategies.
18
19
These are contained within a branch and normally constructed 
20
when the branch is opened.  Clients should typically do 
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
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
31
from bzrlib import (
32
    errors,
2220.2.27 by Martin Pool
Start adding tags to Branch6
33
    trace,
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
34
    )
2220.2.15 by mbp at sourcefrog
Store tag dictionary in bencode and accomodate non-ascii tags
35
from bzrlib.util import bencode
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
36
37
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
38
class _Tags(object):
2220.2.28 by Martin Pool
Integrate tags with Branch6:
39
2220.2.14 by mbp at sourcefrog
cleanup
40
    def __init__(self, branch):
41
        self.branch = branch
42
2220.2.42 by Martin Pool
Tag command refuses to replace existing tags unless you force it.
43
    def has_tag(self, tag_name):
44
        return self.get_tag_dict().has_key(tag_name)
45
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
46
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
47
class DisabledTags(_Tags):
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
48
    """Tag storage that refuses to store anything.
49
50
    This is used by older formats that can't store tags.
51
    """
52
53
    def _not_supported(self, *a, **k):
2220.2.14 by mbp at sourcefrog
cleanup
54
        raise errors.TagsNotSupported(self.branch)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
55
56
    def supports_tags(self):
57
        return False
58
59
    set_tag = _not_supported
60
    get_tag_dict = _not_supported
61
    _set_tag_dict = _not_supported
62
    lookup_tag = _not_supported
2220.2.21 by Martin Pool
Add tag --delete command and implementation
63
    delete_tag = _not_supported
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
64
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
65
    def merge_to(self, to_tags):
66
        # we never have anything to copy
67
        pass
68
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
69
2220.2.20 by Martin Pool
Tag methods now available through Branch.tags.add_tag, etc
70
class BasicTags(_Tags):
2220.2.14 by mbp at sourcefrog
cleanup
71
    """Tag storage in an unversioned branch control file.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
72
    """
73
74
    def supports_tags(self):
75
        return True
76
77
    def set_tag(self, tag_name, tag_target):
2220.2.14 by mbp at sourcefrog
cleanup
78
        """Add a tag definition to the branch.
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
79
80
        Behaviour if the tag is already present is not defined (yet).
81
        """
82
        # all done with a write lock held, so this looks atomic
2220.2.14 by mbp at sourcefrog
cleanup
83
        self.branch.lock_write()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
84
        try:
85
            td = self.get_tag_dict()
86
            td[tag_name] = tag_target
87
            self._set_tag_dict(td)
88
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
89
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
90
91
    def lookup_tag(self, tag_name):
92
        """Return the referent string of a tag"""
93
        td = self.get_tag_dict()
94
        try:
95
            return td[tag_name]
96
        except KeyError:
97
            raise errors.NoSuchTag(tag_name)
98
99
    def get_tag_dict(self):
2220.2.14 by mbp at sourcefrog
cleanup
100
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
101
        try:
2220.2.27 by Martin Pool
Start adding tags to Branch6
102
            try:
103
                tag_content = self.branch._transport.get_bytes('tags')
104
            except errors.NoSuchFile, e:
2220.2.28 by Martin Pool
Integrate tags with Branch6:
105
                # ugly, but only abentley should see this :)
106
                trace.warning('No branch/tags file in %s.  '
2220.2.27 by Martin Pool
Start adding tags to Branch6
107
                     'This branch was probably created by bzr 0.15pre.  '
2220.2.28 by Martin Pool
Integrate tags with Branch6:
108
                     'Create an empty file to silence this message.'
109
                     % (self.branch, ))
2220.2.27 by Martin Pool
Start adding tags to Branch6
110
                return {}
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
111
            return self._deserialize_tag_dict(tag_content)
112
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
113
            self.branch.unlock()
2388.1.11 by Alexander Belchenko
changes after John's review
114
2388.1.1 by Erik Bagfors
created reverse_tag_dict in tags.py
115
    def get_reverse_tag_dict(self):
116
        """Returns a dict with revisions as keys
117
           and a list of tags for that revision as value"""
118
        d = self.get_tag_dict()
119
        rev = {}
120
        for key in d:
121
            try:
122
                rev[d[key]].append(key)
123
            except KeyError:
124
                rev[d[key]] = [key]
125
        return rev
126
2220.2.21 by Martin Pool
Add tag --delete command and implementation
127
    def delete_tag(self, tag_name):
128
        """Delete a tag definition.
129
        """
130
        self.branch.lock_write()
131
        try:
132
            d = self.get_tag_dict()
133
            try:
134
                del d[tag_name]
135
            except KeyError:
136
                raise errors.NoSuchTag(tag_name)
137
            self._set_tag_dict(d)
138
        finally:
139
            self.branch.unlock()
140
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
141
    def _set_tag_dict(self, new_dict):
142
        """Replace all tag definitions
143
144
        :param new_dict: Dictionary from tag name to target.
145
        """
2220.2.14 by mbp at sourcefrog
cleanup
146
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
147
        try:
2220.2.16 by mbp at sourcefrog
Make Branch._transport be the branch's control file transport
148
            self.branch._transport.put_bytes('tags',
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
149
                self._serialize_tag_dict(new_dict))
150
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
151
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
152
153
    def _serialize_tag_dict(self, tag_dict):
2220.2.21 by Martin Pool
Add tag --delete command and implementation
154
        td = dict((k.encode('utf-8'), v)
155
                for k,v in tag_dict.items())
156
        return bencode.bencode(td)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
157
158
    def _deserialize_tag_dict(self, tag_content):
159
        """Convert the tag file into a dictionary of tags"""
2220.2.28 by Martin Pool
Integrate tags with Branch6:
160
        # 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
161
        # is an empty dictionary
162
        if tag_content == '':
163
            return {}
164
        try:
2220.2.21 by Martin Pool
Add tag --delete command and implementation
165
            r = {}
166
            for k, v in bencode.bdecode(tag_content).items():
167
                r[k.decode('utf-8')] = v
168
            return r
169
        except ValueError, e:
170
            raise ValueError("failed to deserialize tag dictionary %r: %s"
2220.2.32 by Martin Pool
Slightly smarter tag merge
171
                % (tag_content, e))
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
172
2220.2.32 by Martin Pool
Slightly smarter tag merge
173
    def merge_to(self, to_tags):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
174
        """Copy tags between repositories if necessary and possible.
175
        
176
        This method has common command-line behaviour about handling 
177
        error cases.
178
2220.2.32 by Martin Pool
Slightly smarter tag merge
179
        All new definitions are copied across, except that tags that already
180
        exist keep their existing definitions.
181
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
182
        :param to_tags: Branch to receive these tags
183
        :param just_warn: If the destination doesn't support tags and the 
184
            source does have tags, just give a warning.  Otherwise, raise
185
            TagsNotSupported (default).
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
186
187
        :returns: A list of tags that conflicted, each of which is 
188
            (tagname, source_target, dest_target).
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
189
        """
190
        if self.branch == to_tags.branch:
191
            return
192
        if not self.supports_tags():
193
            # obviously nothing to copy
194
            return
2220.2.32 by Martin Pool
Slightly smarter tag merge
195
        source_dict = self.get_tag_dict()
196
        if not source_dict:
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
197
            # no tags in the source, and we don't want to clobber anything
198
            # that's in the destination
199
            return
200
        to_tags.branch.lock_write()
201
        try:
2220.2.32 by Martin Pool
Slightly smarter tag merge
202
            dest_dict = to_tags.get_tag_dict()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
203
            result, conflicts = self._reconcile_tags(source_dict, dest_dict)
2220.2.32 by Martin Pool
Slightly smarter tag merge
204
            if result != dest_dict:
205
                to_tags._set_tag_dict(result)
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
206
        finally:
207
            to_tags.branch.unlock()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
208
        return conflicts
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
209
2220.2.32 by Martin Pool
Slightly smarter tag merge
210
    def _reconcile_tags(self, source_dict, dest_dict):
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
211
        """Do a two-way merge of two tag dictionaries.
212
213
        only in source => source value
214
        only in destination => destination value
215
        same definitions => that
216
        different definitions => keep destination value, give a warning
217
218
        :returns: (result_dict,
219
            [(conflicting_tag, source_target, dest_target)])
220
        """
221
        conflicts = []
222
        result = dict(dest_dict) # copy
223
        for name, target in source_dict.items():
224
            if name not in result:
225
                result[name] = target
226
            elif result[name] == target:
227
                pass
228
            else:
229
                conflicts.append((name, target, result[name]))
230
        return result, conflicts
2220.2.32 by Martin Pool
Slightly smarter tag merge
231
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
232
233
def _merge_tags_if_possible(from_branch, to_branch):
234
    from_branch.tags.merge_to(to_branch.tags)