/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
2810.1.1 by Martin Pool
merge push|pull --overwrite and tweak
65
    def merge_to(self, to_tags, overwrite=False):
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
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:
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
85
            master = self.branch.get_master_branch()
86
            if master is not None:
87
                master.tags.set_tag(tag_name, tag_target)
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
88
            td = self.get_tag_dict()
89
            td[tag_name] = tag_target
90
            self._set_tag_dict(td)
91
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
92
            self.branch.unlock()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
93
94
    def lookup_tag(self, tag_name):
95
        """Return the referent string of a tag"""
96
        td = self.get_tag_dict()
97
        try:
98
            return td[tag_name]
99
        except KeyError:
100
            raise errors.NoSuchTag(tag_name)
101
102
    def get_tag_dict(self):
2220.2.14 by mbp at sourcefrog
cleanup
103
        self.branch.lock_read()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
104
        try:
2220.2.27 by Martin Pool
Start adding tags to Branch6
105
            try:
106
                tag_content = self.branch._transport.get_bytes('tags')
107
            except errors.NoSuchFile, e:
2220.2.28 by Martin Pool
Integrate tags with Branch6:
108
                # ugly, but only abentley should see this :)
109
                trace.warning('No branch/tags file in %s.  '
2220.2.27 by Martin Pool
Start adding tags to Branch6
110
                     'This branch was probably created by bzr 0.15pre.  '
2220.2.28 by Martin Pool
Integrate tags with Branch6:
111
                     'Create an empty file to silence this message.'
112
                     % (self.branch, ))
2220.2.27 by Martin Pool
Start adding tags to Branch6
113
                return {}
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
114
            return self._deserialize_tag_dict(tag_content)
115
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
116
            self.branch.unlock()
2388.1.11 by Alexander Belchenko
changes after John's review
117
2388.1.1 by Erik Bagfors
created reverse_tag_dict in tags.py
118
    def get_reverse_tag_dict(self):
119
        """Returns a dict with revisions as keys
120
           and a list of tags for that revision as value"""
121
        d = self.get_tag_dict()
122
        rev = {}
123
        for key in d:
124
            try:
125
                rev[d[key]].append(key)
126
            except KeyError:
127
                rev[d[key]] = [key]
128
        return rev
129
2220.2.21 by Martin Pool
Add tag --delete command and implementation
130
    def delete_tag(self, tag_name):
131
        """Delete a tag definition.
132
        """
133
        self.branch.lock_write()
134
        try:
135
            d = self.get_tag_dict()
136
            try:
137
                del d[tag_name]
138
            except KeyError:
139
                raise errors.NoSuchTag(tag_name)
2805.5.2 by Martin Pool
Setting and deleting tags should also update the master branch, if any.
140
            master = self.branch.get_master_branch()
141
            if master is not None:
142
                try:
143
                    master.tags.delete_tag(tag_name)
144
                except errors.NoSuchTag:
145
                    pass
2220.2.21 by Martin Pool
Add tag --delete command and implementation
146
            self._set_tag_dict(d)
147
        finally:
148
            self.branch.unlock()
149
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
150
    def _set_tag_dict(self, new_dict):
151
        """Replace all tag definitions
152
153
        :param new_dict: Dictionary from tag name to target.
154
        """
2805.4.1 by Martin Pool
Old fix from Alexander: set_tag_dict should lock the branch
155
        self.branch.lock_write()
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
156
        try:
2220.2.16 by mbp at sourcefrog
Make Branch._transport be the branch's control file transport
157
            self.branch._transport.put_bytes('tags',
2220.2.11 by mbp at sourcefrog
Get tag tests working again, stored in the Branch
158
                self._serialize_tag_dict(new_dict))
159
        finally:
2220.2.14 by mbp at sourcefrog
cleanup
160
            self.branch.unlock()
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.
184
        
185
        This method has common command-line behaviour about handling 
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
194
        :returns: A list of tags that conflicted, each of which is 
195
            (tagname, source_target, dest_target).
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
196
        """
197
        if self.branch == to_tags.branch:
198
            return
199
        if not self.supports_tags():
200
            # obviously nothing to copy
201
            return
2220.2.32 by Martin Pool
Slightly smarter tag merge
202
        source_dict = self.get_tag_dict()
203
        if not source_dict:
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
204
            # no tags in the source, and we don't want to clobber anything
205
            # that's in the destination
206
            return
207
        to_tags.branch.lock_write()
208
        try:
2220.2.32 by Martin Pool
Slightly smarter tag merge
209
            dest_dict = to_tags.get_tag_dict()
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
210
            result, conflicts = self._reconcile_tags(source_dict, dest_dict,
211
                                                     overwrite)
2220.2.32 by Martin Pool
Slightly smarter tag merge
212
            if result != dest_dict:
213
                to_tags._set_tag_dict(result)
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
214
        finally:
215
            to_tags.branch.unlock()
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
216
        return conflicts
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
217
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
218
    def _reconcile_tags(self, source_dict, dest_dict, overwrite):
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
219
        """Do a two-way merge of two tag dictionaries.
220
221
        only in source => source value
222
        only in destination => destination value
223
        same definitions => that
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
224
        different definitions => if overwrite is False, keep destination
225
            value and give a warning, otherwise use the source value
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
226
227
        :returns: (result_dict,
228
            [(conflicting_tag, source_target, dest_target)])
229
        """
230
        conflicts = []
231
        result = dict(dest_dict) # copy
232
        for name, target in source_dict.items():
2804.3.1 by Lukáš Lalinský
Overwrite conflicting tags by push|pull --overwrite.
233
            if name not in result or overwrite:
2220.2.33 by Martin Pool
Start support for flagging tag conflicts
234
                result[name] = target
235
            elif result[name] == target:
236
                pass
237
            else:
238
                conflicts.append((name, target, result[name]))
239
        return result, conflicts
2220.2.32 by Martin Pool
Slightly smarter tag merge
240
2220.2.30 by Martin Pool
split out tag-merging code and add some tests
241
242
def _merge_tags_if_possible(from_branch, to_branch):
243
    from_branch.tags.merge_to(to_branch.tags)