/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()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
114
2220.2.21 by Martin Pool
Add tag --delete command and implementation
115
    def delete_tag(self, tag_name):
116
        """Delete a tag definition.
117
        """
118
        self.branch.lock_write()
119
        try:
120
            d = self.get_tag_dict()
121
            try:
122
                del d[tag_name]
123
            except KeyError:
124
                raise errors.NoSuchTag(tag_name)
125
            self._set_tag_dict(d)
126
        finally:
127
            self.branch.unlock()
128
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
129
    def _set_tag_dict(self, new_dict):
130
        """Replace all tag definitions
131
132
        :param new_dict: Dictionary from tag name to target.
133
        """
2220.2.14 by mbp at sourcefrog
cleanup
134
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
135
        try:
2220.2.16 by mbp at sourcefrog
Make Branch._transport be the branch's control file transport
136
            self.branch._transport.put_bytes('tags',
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
137
                self._serialize_tag_dict(new_dict))
138
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
139
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
140
141
    def _serialize_tag_dict(self, tag_dict):
2220.2.21 by Martin Pool
Add tag --delete command and implementation
142
        td = dict((k.encode('utf-8'), v)
143
                for k,v in tag_dict.items())
144
        return bencode.bencode(td)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
145
146
    def _deserialize_tag_dict(self, tag_content):
147
        """Convert the tag file into a dictionary of tags"""
2220.2.28 by Martin Pool
Integrate tags with Branch6:
148
        # 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
149
        # is an empty dictionary
150
        if tag_content == '':
151
            return {}
152
        try:
2220.2.21 by Martin Pool
Add tag --delete command and implementation
153
            r = {}
154
            for k, v in bencode.bdecode(tag_content).items():
155
                r[k.decode('utf-8')] = v
156
            return r
157
        except ValueError, e:
158
            raise ValueError("failed to deserialize tag dictionary %r: %s"
2220.2.32 by Martin Pool
Slightly smarter tag merge
159
                % (tag_content, e))
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
160
2220.2.32 by Martin Pool
Slightly smarter tag merge
161
    def merge_to(self, to_tags):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
162
        """Copy tags between repositories if necessary and possible.
163
        
164
        This method has common command-line behaviour about handling 
165
        error cases.
166
2220.2.32 by Martin Pool
Slightly smarter tag merge
167
        All new definitions are copied across, except that tags that already
168
        exist keep their existing definitions.
169
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
170
        :param to_tags: Branch to receive these tags
171
        :param just_warn: If the destination doesn't support tags and the 
172
            source does have tags, just give a warning.  Otherwise, raise
173
            TagsNotSupported (default).
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
174
175
        :returns: A list of tags that conflicted, each of which is 
176
            (tagname, source_target, dest_target).
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
177
        """
178
        if self.branch == to_tags.branch:
179
            return
180
        if not self.supports_tags():
181
            # obviously nothing to copy
182
            return
2220.2.32 by Martin Pool
Slightly smarter tag merge
183
        source_dict = self.get_tag_dict()
184
        if not source_dict:
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
185
            # no tags in the source, and we don't want to clobber anything
186
            # that's in the destination
187
            return
188
        to_tags.branch.lock_write()
189
        try:
2220.2.32 by Martin Pool
Slightly smarter tag merge
190
            dest_dict = to_tags.get_tag_dict()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
191
            result, conflicts = self._reconcile_tags(source_dict, dest_dict)
2220.2.32 by Martin Pool
Slightly smarter tag merge
192
            if result != dest_dict:
193
                to_tags._set_tag_dict(result)
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
194
        finally:
195
            to_tags.branch.unlock()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
196
        return conflicts
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
197
2220.2.32 by Martin Pool
Slightly smarter tag merge
198
    def _reconcile_tags(self, source_dict, dest_dict):
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
199
        """Do a two-way merge of two tag dictionaries.
200
201
        only in source => source value
202
        only in destination => destination value
203
        same definitions => that
204
        different definitions => keep destination value, give a warning
205
206
        :returns: (result_dict,
207
            [(conflicting_tag, source_target, dest_target)])
208
        """
209
        conflicts = []
210
        result = dict(dest_dict) # copy
211
        for name, target in source_dict.items():
212
            if name not in result:
213
                result[name] = target
214
            elif result[name] == target:
215
                pass
216
            else:
217
                conflicts.append((name, target, result[name]))
218
        return result, conflicts
2220.2.32 by Martin Pool
Slightly smarter tag merge
219
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
220
221
def _merge_tags_if_possible(from_branch, to_branch):
222
    from_branch.tags.merge_to(to_branch.tags)