/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Robert Collins
  • Date: 2006-03-01 02:01:47 UTC
  • mto: (1594.2.4 integration)
  • mto: This revision was merged to the branch mainline in revision 1596.
  • Revision ID: robertc@robertcollins.net-20060301020147-62bb5465f75fbf40
Start check tests for knits (pending), and remove dead code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
#
 
3
# Authors:
 
4
#   Johan Rydberg <jrydberg@gnu.org>
 
5
#
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; either version 2 of the License, or
 
9
# (at your option) any later version.
 
10
 
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
 
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program; if not, write to the Free Software
 
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 
 
20
# Remaing to do is to figure out if get_graph should return a simple
 
21
# map, or a graph object of some kind.
 
22
 
 
23
 
 
24
"""Versioned text file storage api."""
 
25
 
 
26
 
 
27
class VersionedFile(object):
 
28
    """Versioned text file storage.
 
29
    
 
30
    A versioned file manages versions of line-based text files,
 
31
    keeping track of the originating version for each line.
 
32
 
 
33
    To clients the "lines" of the file are represented as a list of
 
34
    strings. These strings will typically have terminal newline
 
35
    characters, but this is not required.  In particular files commonly
 
36
    do not have a newline at the end of the file.
 
37
 
 
38
    Texts are identified by a version-id string.
 
39
    """
 
40
 
 
41
    def versions(self):
 
42
        """Return a unsorted list of versions."""
 
43
        raise NotImplementedError(self.versions)
 
44
 
 
45
    def has_version(self, version_id):
 
46
        """Returns whether version is present."""
 
47
        raise NotImplementedError(self.has_version)
 
48
 
 
49
    def add_lines(self, version_id, parents, lines):
 
50
        """Add a single text on top of the versioned file.
 
51
 
 
52
        Must raise RevisionAlreadyPresent if the new version is
 
53
        already present in file history.
 
54
 
 
55
        Must raise RevisionNotPresent if any of the given parents are
 
56
        not present in file history."""
 
57
        raise NotImplementedError(self.add_lines)
 
58
 
 
59
    def clone_text(self, new_version_id, old_version_id, parents):
 
60
        """Add an identical text to old_version_id as new_version_id.
 
61
 
 
62
        Must raise RevisionNotPresent if the old version or any of the
 
63
        parents are not present in file history.
 
64
 
 
65
        Must raise RevisionAlreadyPresent if the new version is
 
66
        already present in file history."""
 
67
        raise NotImplementedError(self.clone_text)
 
68
 
 
69
    def get_text(self, version_id):
 
70
        """Return version contents as a text string.
 
71
 
 
72
        Raises RevisionNotPresent if version is not present in
 
73
        file history.
 
74
        """
 
75
        return ''.join(self.get_lines(version_id))
 
76
    get_string = get_text
 
77
 
 
78
    def get_lines(self, version_id):
 
79
        """Return version contents as a sequence of lines.
 
80
 
 
81
        Raises RevisionNotPresent if version is not present in
 
82
        file history.
 
83
        """
 
84
        raise NotImplementedError(self.get_lines)
 
85
 
 
86
    def get_ancestry(self, version_ids):
 
87
        """Return a list of all ancestors of given version(s). This
 
88
        will not include the null revision.
 
89
 
 
90
        Must raise RevisionNotPresent if any of the given versions are
 
91
        not present in file history."""
 
92
        if isinstance(version_ids, basestring):
 
93
            version_ids = [version_ids]
 
94
        raise NotImplementedError(self.get_ancestry)
 
95
        
 
96
    def get_graph(self, version_id):
 
97
        """Return a graph.
 
98
 
 
99
        Must raise RevisionNotPresent if version is not present in
 
100
        file history."""
 
101
        raise NotImplementedError(self.get_graph)
 
102
 
 
103
    def get_parents(self, version_id):
 
104
        """Return version names for parents of a version.
 
105
 
 
106
        Must raise RevisionNotPresent if version is not present in
 
107
        file history.
 
108
        """
 
109
        raise NotImplementedError(self.get_parents)
 
110
 
 
111
    def annotate_iter(self, version_id):
 
112
        """Yield list of (version-id, line) pairs for the specified
 
113
        version.
 
114
 
 
115
        Must raise RevisionNotPresent if any of the given versions are
 
116
        not present in file history.
 
117
        """
 
118
        raise NotImplementedError(self.annotate_iter)
 
119
 
 
120
    def annotate(self, version_id):
 
121
        return list(self.annotate_iter(version_id))
 
122
 
 
123
    def join(self, other, pb=None, msg=None, version_ids=None):
 
124
        """Integrate versions from other into this versioned file.
 
125
 
 
126
        If version_ids is None all versions from other should be
 
127
        incorporated into this versioned file.
 
128
 
 
129
        Must raise RevisionNotPresent if any of the specified versions
 
130
        are not present in the other files history."""
 
131
        raise NotImplementedError(self.join)
 
132
 
 
133
    def walk(self, version_ids=None):
 
134
        """Walk the versioned file as a weave-like structure, for
 
135
        versions relative to version_ids.  Yields sequence of (lineno,
 
136
        insert, deletes, text) for each relevant line.
 
137
 
 
138
        Must raise RevisionNotPresent if any of the specified versions
 
139
        are not present in the file history.
 
140
 
 
141
        :param version_ids: the version_ids to walk with respect to. If not
 
142
                            supplied the entire weave-like structure is walked.
 
143
        """
 
144
        raise NotImplementedError(self.walk)
 
145
 
 
146
    def plan_merge(self, ver_a, ver_b):
 
147
        """Return pseudo-annotation indicating how the two versions merge.
 
148
 
 
149
        This is computed between versions a and b and their common
 
150
        base.
 
151
 
 
152
        Weave lines present in none of them are skipped entirely.
 
153
        """
 
154
        inc_a = set(self.inclusions([ver_a]))
 
155
        inc_b = set(self.inclusions([ver_b]))
 
156
        inc_c = inc_a & inc_b
 
157
 
 
158
        for lineno, insert, deleteset, line in self.walk():
 
159
            if deleteset & inc_c:
 
160
                # killed in parent; can't be in either a or b
 
161
                # not relevant to our work
 
162
                yield 'killed-base', line
 
163
            elif insert in inc_c:
 
164
                # was inserted in base
 
165
                killed_a = bool(deleteset & inc_a)
 
166
                killed_b = bool(deleteset & inc_b)
 
167
                if killed_a and killed_b:
 
168
                    yield 'killed-both', line
 
169
                elif killed_a:
 
170
                    yield 'killed-a', line
 
171
                elif killed_b:
 
172
                    yield 'killed-b', line
 
173
                else:
 
174
                    yield 'unchanged', line
 
175
            elif insert in inc_a:
 
176
                if deleteset & inc_a:
 
177
                    yield 'ghost-a', line
 
178
                else:
 
179
                    # new in A; not in B
 
180
                    yield 'new-a', line
 
181
            elif insert in inc_b:
 
182
                if deleteset & inc_b:
 
183
                    yield 'ghost-b', line
 
184
                else:
 
185
                    yield 'new-b', line
 
186
            else:
 
187
                # not in either revision
 
188
                yield 'irrelevant', line
 
189
 
 
190
        yield 'unchanged', ''           # terminator
 
191
 
 
192
    def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
 
193
        lines_a = []
 
194
        lines_b = []
 
195
        ch_a = ch_b = False
 
196
        # TODO: Return a structured form of the conflicts (e.g. 2-tuples for
 
197
        # conflicted regions), rather than just inserting the markers.
 
198
        # 
 
199
        # TODO: Show some version information (e.g. author, date) on 
 
200
        # conflicted regions.
 
201
        for state, line in plan:
 
202
            if state == 'unchanged' or state == 'killed-both':
 
203
                # resync and flush queued conflicts changes if any
 
204
                if not lines_a and not lines_b:
 
205
                    pass
 
206
                elif ch_a and not ch_b:
 
207
                    # one-sided change:                    
 
208
                    for l in lines_a: yield l
 
209
                elif ch_b and not ch_a:
 
210
                    for l in lines_b: yield l
 
211
                elif lines_a == lines_b:
 
212
                    for l in lines_a: yield l
 
213
                else:
 
214
                    yield a_marker
 
215
                    for l in lines_a: yield l
 
216
                    yield '=======\n'
 
217
                    for l in lines_b: yield l
 
218
                    yield b_marker
 
219
 
 
220
                del lines_a[:]
 
221
                del lines_b[:]
 
222
                ch_a = ch_b = False
 
223
                
 
224
            if state == 'unchanged':
 
225
                if line:
 
226
                    yield line
 
227
            elif state == 'killed-a':
 
228
                ch_a = True
 
229
                lines_b.append(line)
 
230
            elif state == 'killed-b':
 
231
                ch_b = True
 
232
                lines_a.append(line)
 
233
            elif state == 'new-a':
 
234
                ch_a = True
 
235
                lines_a.append(line)
 
236
            elif state == 'new-b':
 
237
                ch_b = True
 
238
                lines_b.append(line)
 
239
            else:
 
240
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
 
241
                                 'killed-both'), \
 
242
                       state