~bzr/ubuntu/lucid/bzr/beta-ppa

« back to all changes in this revision

Viewing changes to bzrlib/tag.py

  • Committer: Martin Pool
  • Date: 2010-07-02 07:29:40 UTC
  • mfrom: (129.1.7 packaging-karmic)
  • Revision ID: mbp@sourcefrog.net-20100702072940-hpzq5elg8wjve8rh
* PPA rebuild.
* PPA rebuild for Karmic.
* PPA rebuild for Jaunty.
* PPA rebuild for Hardy.
* From postinst, actually remove the example bash completion scripts.
  (LP: #249452)
* New upstream release.
* New upstream release.
* New upstream release.
* Revert change to Build-depends: Dapper does not have python-central.
  Should be python-support..
* Target ppa..
* Target ppa..
* Target ppa..
* Target ppa..
* New upstream release.
* Switch to dpkg-source 3.0 (quilt) format.
* Bump standards version to 3.8.4.
* Remove embedded copy of python-configobj. Closes: #555336
* Remove embedded copy of python-elementtree. Closes: #555343
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* Change section from 'Devel' to 'Vcs'..
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* debian/control: Fix obsolete-relation-form-in-source
  lintian warning. 
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split out docs into bzr-doc package.
* New upstream release.
* Added John Francesco Ferlito to Uploaders.
* Fix install path to quick-reference guide
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to doc paths changing
* New upstream release.
* Fix FTBFS due to path changes, again, again.
* Fix FTBFS due to path changes, again.
* Fix FTBFS due to path changes.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Bump standards version to 3.8.3.
* Remove unused patch system.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix copy and paste tab error in .install file
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
 + Fixes compatibility with Python 2.4. Closes: #537708
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream version.
* Bump standards version to 3.8.2.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add python-pyrex to build-deps to ensure C extensions are always build.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* Split documentation into bzr-doc package. ((LP: #385074)
* Multiple packaging changes to make us more linitan clean.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Fix API compatibility version. (Closes: #526233)
* New upstream release.
  + Fixes default format for upgrade command. (Closes: #464688)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add missing dependency on zlib development library. (Closes:
  #523595)
* Add zlib build-depends.
* Add zlib build-depends.
* Add zlib build-depends.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Move to section vcs.
* Bump standards version to 3.8.1.
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* New upstream release.
* Remove temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* Add temporary patch for missing .c files from distribution
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Recommend ca-certificates. (Closes: #452024)
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Update watch file. bazaar now uses launchpad to host its sources.
* Remove patch for inventory root revision copy, applied upstream.
* New upstream release.
* New upstream release.
* New upstream release
* Force removal of files installed in error to /etc/bash_completion.d/
  (LP: #249452)
* New upstream release.
* New upstream release
* New upstream release.
* Bump standards version.
* Include patch for inventory root revision copy, required for bzr-svn.
* New upstream release.
* Remove unused lintian overrides.
* Correct the package version not to be native.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* Final 1.5 release.
* New upstream release.
* New upstream release.
* New upstream release.
* Add myself as a co-maintainer.
* Add a Dm-Upload-Allowed: yes header.
* New upstream bugfix release.
* New upstream release.
* Final 1.3 release.
* New upstream release.
* First release candidate of the upcoming 1.3 release.
* Rebuild to fix the problem caused by a build with a broken python-central.
* New upstream release.
* Rebuild for dapper PPA.
* Apply Lamont's patches to fix build-dependencies on dapper.
  (See: https://bugs.launchpad.net/bzr/+bug/189915)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
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.
 
27
 
 
28
 
 
29
from warnings import warn
 
30
 
 
31
from bzrlib import (
 
32
    bencode,
 
33
    errors,
 
34
    trace,
 
35
    )
 
36
 
 
37
 
 
38
class _Tags(object):
 
39
 
 
40
    def __init__(self, branch):
 
41
        self.branch = branch
 
42
 
 
43
    def has_tag(self, tag_name):
 
44
        return self.get_tag_dict().has_key(tag_name)
 
45
 
 
46
 
 
47
class DisabledTags(_Tags):
 
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):
 
54
        raise errors.TagsNotSupported(self.branch)
 
55
 
 
56
    set_tag = _not_supported
 
57
    get_tag_dict = _not_supported
 
58
    _set_tag_dict = _not_supported
 
59
    lookup_tag = _not_supported
 
60
    delete_tag = _not_supported
 
61
 
 
62
    def merge_to(self, to_tags, overwrite=False):
 
63
        # we never have anything to copy
 
64
        pass
 
65
 
 
66
    def rename_revisions(self, rename_map):
 
67
        # No tags, so nothing to rename
 
68
        pass
 
69
 
 
70
    def get_reverse_tag_dict(self):
 
71
        # There aren't any tags, so the reverse mapping is empty.
 
72
        return {}
 
73
 
 
74
 
 
75
class BasicTags(_Tags):
 
76
    """Tag storage in an unversioned branch control file.
 
77
    """
 
78
 
 
79
    def set_tag(self, tag_name, tag_target):
 
80
        """Add a tag definition to 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
 
85
        self.branch.lock_write()
 
86
        try:
 
87
            master = self.branch.get_master_branch()
 
88
            if master is not None:
 
89
                master.tags.set_tag(tag_name, tag_target)
 
90
            td = self.get_tag_dict()
 
91
            td[tag_name] = tag_target
 
92
            self._set_tag_dict(td)
 
93
        finally:
 
94
            self.branch.unlock()
 
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):
 
105
        self.branch.lock_read()
 
106
        try:
 
107
            try:
 
108
                tag_content = self.branch._get_tags_bytes()
 
109
            except errors.NoSuchFile, e:
 
110
                # ugly, but only abentley should see this :)
 
111
                trace.warning('No branch/tags file in %s.  '
 
112
                     'This branch was probably created by bzr 0.15pre.  '
 
113
                     'Create an empty file to silence this message.'
 
114
                     % (self.branch, ))
 
115
                return {}
 
116
            return self._deserialize_tag_dict(tag_content)
 
117
        finally:
 
118
            self.branch.unlock()
 
119
 
 
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
 
 
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)
 
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
 
148
            self._set_tag_dict(d)
 
149
        finally:
 
150
            self.branch.unlock()
 
151
 
 
152
    def _set_tag_dict(self, new_dict):
 
153
        """Replace all tag definitions
 
154
 
 
155
        WARNING: Calling this on an unlocked branch will lock it, and will
 
156
        replace the tags without warning on conflicts.
 
157
 
 
158
        :param new_dict: Dictionary from tag name to target.
 
159
        """
 
160
        return self.branch._set_tags_bytes(self._serialize_tag_dict(new_dict))
 
161
 
 
162
    def _serialize_tag_dict(self, tag_dict):
 
163
        td = dict((k.encode('utf-8'), v)
 
164
                  for k,v in tag_dict.items())
 
165
        return bencode.bencode(td)
 
166
 
 
167
    def _deserialize_tag_dict(self, tag_content):
 
168
        """Convert the tag file into a dictionary of tags"""
 
169
        # was a special case to make initialization easy, an empty definition
 
170
        # is an empty dictionary
 
171
        if tag_content == '':
 
172
            return {}
 
173
        try:
 
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"
 
180
                % (tag_content, e))
 
181
 
 
182
    def merge_to(self, to_tags, overwrite=False):
 
183
        """Copy tags between repositories if necessary and possible.
 
184
 
 
185
        This method has common command-line behaviour about handling
 
186
        error cases.
 
187
 
 
188
        All new definitions are copied across, except that tags that already
 
189
        exist keep their existing definitions.
 
190
 
 
191
        :param to_tags: Branch to receive these tags
 
192
        :param overwrite: Overwrite conflicting tags in the target branch
 
193
 
 
194
        :returns: A list of tags that conflicted, each of which is
 
195
            (tagname, source_target, dest_target), or None if no copying was
 
196
            done.
 
197
        """
 
198
        if self.branch == to_tags.branch:
 
199
            return
 
200
        if not self.branch.supports_tags():
 
201
            # obviously nothing to copy
 
202
            return
 
203
        source_dict = self.get_tag_dict()
 
204
        if not source_dict:
 
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:
 
210
            dest_dict = to_tags.get_tag_dict()
 
211
            result, conflicts = self._reconcile_tags(source_dict, dest_dict,
 
212
                                                     overwrite)
 
213
            if result != dest_dict:
 
214
                to_tags._set_tag_dict(result)
 
215
        finally:
 
216
            to_tags.branch.unlock()
 
217
        return conflicts
 
218
 
 
219
    def rename_revisions(self, rename_map):
 
220
        """Rename revisions in this tags dictionary.
 
221
        
 
222
        :param rename_map: Dictionary mapping old revids to new revids
 
223
        """
 
224
        reverse_tags = self.get_reverse_tag_dict()
 
225
        for revid, names in reverse_tags.iteritems():
 
226
            if revid in rename_map:
 
227
                for name in names:
 
228
                    self.set_tag(name, rename_map[revid])
 
229
 
 
230
    def _reconcile_tags(self, source_dict, dest_dict, overwrite):
 
231
        """Do a two-way merge of two tag dictionaries.
 
232
 
 
233
        only in source => source value
 
234
        only in destination => destination value
 
235
        same definitions => that
 
236
        different definitions => if overwrite is False, keep destination
 
237
            value and give a warning, otherwise use the source value
 
238
 
 
239
        :returns: (result_dict,
 
240
            [(conflicting_tag, source_target, dest_target)])
 
241
        """
 
242
        conflicts = []
 
243
        result = dict(dest_dict) # copy
 
244
        for name, target in source_dict.items():
 
245
            if name not in result or overwrite:
 
246
                result[name] = target
 
247
            elif result[name] == target:
 
248
                pass
 
249
            else:
 
250
                conflicts.append((name, target, result[name]))
 
251
        return result, conflicts
 
252
 
 
253
 
 
254
def _merge_tags_if_possible(from_branch, to_branch):
 
255
    from_branch.tags.merge_to(to_branch.tags)