/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
1
# (C) 2005 Canonical Development 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
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
17
"""Serializer factory for reading and writing bundles.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
18
"""
19
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
20
import os
1185.82.78 by Aaron Bentley
Cleanups
21
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
22
from bzrlib.bundle.serializer import (BundleSerializer, 
23
                                      BUNDLE_HEADER, 
24
                                      format_highres_date,
25
                                      unpack_highres_date,
26
                                     )
27
from bzrlib.bundle.serializer import binary_diff
1185.82.78 by Aaron Bentley
Cleanups
28
from bzrlib.delta import compare_trees
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
29
from bzrlib.diff import internal_diff
1185.82.78 by Aaron Bentley
Cleanups
30
import bzrlib.errors as errors
31
from bzrlib.osutils import pathjoin
1185.82.111 by Aaron Bentley
Remove progress indicator from cset command for now
32
from bzrlib.progress import DummyProgress
1185.82.78 by Aaron Bentley
Cleanups
33
from bzrlib.revision import NULL_REVISION
1185.82.7 by John Arbash Meinel
Adding patches.py into bzrlib, including the tests into the test suite.
34
from bzrlib.rio import RioWriter, read_stanzas
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
35
import bzrlib.ui
1185.82.121 by Aaron Bentley
Move calculation of Testament sha1s to Testament
36
from bzrlib.testament import StrictTestament
1185.82.96 by Aaron Bentley
Got first binary test passing
37
from bzrlib.textfile import text_file
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
38
1185.82.65 by Aaron Bentley
Factored out boolean text stuff
39
bool_text = {True: 'yes', False: 'no'}
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
40
1185.82.102 by Aaron Bentley
Start abstracting action line writing
41
42
class Action(object):
43
    """Represent an action"""
44
45
    def __init__(self, name, parameters=None, properties=None):
46
        self.name = name
47
        if parameters is None:
48
            self.parameters = []
49
        else:
50
            self.parameters = parameters
51
        if properties is None:
52
            self.properties = []
53
        else:
54
            self.properties = properties
55
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
56
    def add_property(self, name, value):
57
        """Add a property to the action"""
58
        self.properties.append((name, value))
59
60
    def add_bool_property(self, name, value):
61
        """Add a boolean property to the action"""
62
        self.add_property(name, bool_text[value])
63
1185.82.102 by Aaron Bentley
Start abstracting action line writing
64
    def write(self, to_file):
65
        """Write action as to a file"""
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
66
        p_texts = [' '.join([self.name]+self.parameters)]
67
        for prop in self.properties:
68
            if len(prop) == 1:
69
                p_texts.append(prop[0])
70
            else:
71
                try:
72
                    p_texts.append('%s:%s' % prop)
73
                except:
74
                    raise repr(prop)
1185.82.102 by Aaron Bentley
Start abstracting action line writing
75
        text = ['=== ']
76
        text.append(' // '.join(p_texts))
1185.82.106 by Aaron Bentley
Use elipsis to continue long meta lines
77
        text_line = ''.join(text).encode('utf-8')
78
        available = 79
79
        while len(text_line) > available:
80
            to_file.write(text_line[:available])
81
            text_line = text_line[available:]
82
            to_file.write('\n... ')
83
            available = 79 - len('... ')
84
        to_file.write(text_line+'\n')
1185.82.102 by Aaron Bentley
Start abstracting action line writing
85
86
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
87
class BundleSerializerV07(BundleSerializer):
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
88
    def read(self, f):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
89
        """Read the rest of the bundles from the supplied file.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
90
91
        :param f: The file to read from
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
92
        :return: A list of bundles
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
93
        """
1185.82.38 by Aaron Bentley
Changed changeset version number to 0.7
94
        assert self.version == '0.7'
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
95
        # The first line of the header should have been read
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
96
        raise NotImplementedError
97
1185.82.74 by Aaron Bentley
Allow custom base for any revision
98
    def write(self, source, revision_ids, forced_bases, f):
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
99
        """Write the bundless to the supplied files.
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
100
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
101
        :param source: A source for revision information
102
        :param revision_ids: The list of revision ids to serialize
1185.82.74 by Aaron Bentley
Allow custom base for any revision
103
        :param forced_bases: A dict of revision -> base that overrides default
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
104
        :param f: The file to output to
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
105
        """
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
106
        self.source = source
107
        self.revision_ids = revision_ids
1185.82.74 by Aaron Bentley
Allow custom base for any revision
108
        self.forced_bases = forced_bases
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
109
        self.to_file = f
110
        source.lock_read()
111
        try:
112
            self._write_main_header()
1185.82.111 by Aaron Bentley
Remove progress indicator from cset command for now
113
            pb = DummyProgress()
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
114
            try:
115
                self._write_revisions(pb)
116
            finally:
1185.82.111 by Aaron Bentley
Remove progress indicator from cset command for now
117
                pass
118
                #pb.finished()
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
119
        finally:
120
            source.unlock()
121
122
    def _write_main_header(self):
123
        """Write the header for the changes"""
124
        f = self.to_file
1185.82.130 by Aaron Bentley
Rename changesets to revision bundles
125
        f.write(BUNDLE_HEADER)
1185.82.38 by Aaron Bentley
Changed changeset version number to 0.7
126
        f.write('0.7\n')
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
127
        f.write('#\n')
128
129
    def _write(self, key, value, indent=1):
130
        """Write out meta information, with proper indenting, etc"""
131
        assert indent > 0, 'indentation must be greater than 0'
132
        f = self.to_file
133
        f.write('#' + (' ' * indent))
134
        f.write(key.encode('utf-8'))
135
        if not value:
136
            f.write(':\n')
1185.82.28 by Aaron Bentley
Got parent_id handling working
137
        elif isinstance(value, basestring):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
138
            f.write(': ')
139
            f.write(value.encode('utf-8'))
140
            f.write('\n')
141
        else:
142
            f.write(':\n')
1185.82.28 by Aaron Bentley
Got parent_id handling working
143
            for entry in value:
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
144
                f.write('#' + (' ' * (indent+2)))
1185.82.32 by Aaron Bentley
Got all tests passing using serializer
145
                f.write(entry.encode('utf-8'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
146
                f.write('\n')
147
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
148
    def _write_revisions(self, pb):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
149
        """Write the information for all of the revisions."""
150
151
        # Optimize for the case of revisions in order
152
        last_rev_id = None
153
        last_rev_tree = None
154
1185.82.92 by Aaron Bentley
Add progress bar for changeset generation
155
        i_max = len(self.revision_ids) 
156
        for i, rev_id in enumerate(self.revision_ids):
157
            pb.update("Generating revsion data", i, i_max)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
158
            rev = self.source.get_revision(rev_id)
1185.82.91 by Aaron Bentley
Updated the write_changeset optimization
159
            if rev_id == last_rev_id:
160
                rev_tree = last_rev_tree
161
            else:
162
                base_tree = self.source.revision_tree(rev_id)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
163
            rev_tree = self.source.revision_tree(rev_id)
1185.82.74 by Aaron Bentley
Allow custom base for any revision
164
            if rev_id in self.forced_bases:
165
                explicit_base = True
166
                base_id = self.forced_bases[rev_id]
167
                if base_id is None:
168
                    base_id = NULL_REVISION
1185.82.72 by Aaron Bentley
Always use leftmost base for changesets
169
            else:
1185.82.74 by Aaron Bentley
Allow custom base for any revision
170
                explicit_base = False
171
                if rev.parent_ids:
172
                    base_id = rev.parent_ids[-1]
173
                else:
174
                    base_id = NULL_REVISION
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
175
176
            if base_id == last_rev_id:
177
                base_tree = last_rev_tree
178
            else:
179
                base_tree = self.source.revision_tree(base_id)
180
1185.82.74 by Aaron Bentley
Allow custom base for any revision
181
            self._write_revision(rev, rev_tree, base_id, base_tree, 
182
                                 explicit_base)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
183
1185.82.91 by Aaron Bentley
Updated the write_changeset optimization
184
            last_rev_id = base_id
185
            last_rev_tree = base_tree
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
186
1185.82.74 by Aaron Bentley
Allow custom base for any revision
187
    def _write_revision(self, rev, rev_tree, base_rev, base_tree, 
188
                        explicit_base):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
189
        """Write out the information for a revision."""
190
        def w(key, value):
191
            self._write(key, value, indent=1)
192
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
193
        w('message', rev.message.split('\n'))
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
194
        w('committer', rev.committer)
195
        w('date', format_highres_date(rev.timestamp, rev.timezone))
196
        self.to_file.write('\n')
197
1185.82.74 by Aaron Bentley
Allow custom base for any revision
198
        self._write_delta(rev_tree, base_tree, rev.revision_id)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
199
1185.82.79 by Aaron Bentley
Move message to top, revision-id to footer
200
        w('revision id', rev.revision_id)
1185.82.121 by Aaron Bentley
Move calculation of Testament sha1s to Testament
201
        w('sha1', StrictTestament.from_revision(self.source, 
202
                                                rev.revision_id).as_sha1())
1185.82.29 by Aaron Bentley
Got merge test working
203
        w('inventory sha1', rev.inventory_sha1)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
204
        if rev.parent_ids:
1185.82.28 by Aaron Bentley
Got parent_id handling working
205
            w('parent ids', rev.parent_ids)
1185.82.74 by Aaron Bentley
Allow custom base for any revision
206
        if explicit_base:
207
            w('base id', base_rev)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
208
        if rev.properties:
209
            self._write('properties', None, indent=1)
210
            for name, value in rev.properties.items():
211
                self._write(name, value, indent=3)
212
        
213
        # Add an extra blank space at the end
214
        self.to_file.write('\n')
215
1185.82.101 by Aaron Bentley
Start using a standard action writer
216
    def _write_action(self, name, parameters, properties=None):
217
        if properties is None:
218
            properties = []
219
        p_texts = ['%s:%s' % v for v in properties]
220
        self.to_file.write('=== ')
221
        self.to_file.write(' '.join([name]+parameters).encode('utf-8'))
222
        self.to_file.write(' // '.join(p_texts).encode('utf-8'))
223
        self.to_file.write('\n')
224
1185.82.74 by Aaron Bentley
Allow custom base for any revision
225
    def _write_delta(self, new_tree, old_tree, default_revision_id):
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
226
        """Write out the changes between the trees."""
227
        DEVNULL = '/dev/null'
228
        old_label = ''
229
        new_label = ''
230
1185.82.105 by Aaron Bentley
Removed two-liner nested functions
231
        def do_diff(file_id, old_path, new_path, action):
1185.82.96 by Aaron Bentley
Got first binary test passing
232
            def tree_lines(tree, require_text=False):
233
                if file_id in tree:
234
                    tree_file = tree.get_file(file_id)
235
                    if require_text is True:
236
                        tree_file = text_file(tree_file)
237
                    return tree_file.readlines()
238
                else:
239
                    return []
240
241
            try:
242
                old_lines = tree_lines(old_tree, require_text=True)
243
                new_lines = tree_lines(new_tree, require_text=True)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
244
                action.write(self.to_file)
1185.82.96 by Aaron Bentley
Got first binary test passing
245
                internal_diff(old_path, old_lines, new_path, new_lines, 
246
                              self.to_file)
247
            except errors.BinaryFile:
248
                old_lines = tree_lines(old_tree, require_text=False)
249
                new_lines = tree_lines(new_tree, require_text=False)
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
250
                action.add_property('encoding', 'base64')
251
                action.write(self.to_file)
1185.82.96 by Aaron Bentley
Got first binary test passing
252
                binary_diff(old_path, old_lines, new_path, new_lines, 
253
                            self.to_file)
254
1185.82.104 by Aaron Bentley
Refactored action writing
255
        def finish_action(action, file_id, kind, meta_modified, text_modified,
256
                          old_path, new_path):
1185.82.105 by Aaron Bentley
Removed two-liner nested functions
257
            entry = new_tree.inventory[file_id]
258
            if entry.revision != default_revision_id:
259
                action.add_property('last-changed', entry.revision)
1185.82.104 by Aaron Bentley
Refactored action writing
260
            if meta_modified:
1185.82.105 by Aaron Bentley
Removed two-liner nested functions
261
                action.add_bool_property('executable', entry.executable)
1185.82.104 by Aaron Bentley
Refactored action writing
262
            if text_modified and kind == "symlink":
1185.82.105 by Aaron Bentley
Removed two-liner nested functions
263
                action.add_property('target', entry.symlink_target)
1185.82.104 by Aaron Bentley
Refactored action writing
264
            if text_modified and kind == "file":
1185.82.105 by Aaron Bentley
Removed two-liner nested functions
265
                do_diff(file_id, old_path, new_path, action)
1185.82.104 by Aaron Bentley
Refactored action writing
266
            else:
267
                action.write(self.to_file)
268
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
269
        delta = compare_trees(old_tree, new_tree, want_unchanged=True)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
270
        for path, file_id, kind in delta.removed:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
271
            action = Action('removed', [kind, path]).write(self.to_file)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
272
273
        for path, file_id, kind in delta.added:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
274
            action = Action('added', [kind, path], [('file-id', file_id)])
1185.82.119 by Aaron Bentley
Default execute bit to no for new files, directories, symlinks
275
            meta_modified = (kind=='file' and 
276
                             new_tree.is_executable(file_id))
277
            finish_action(action, file_id, kind, meta_modified, True,
1185.82.104 by Aaron Bentley
Refactored action writing
278
                          DEVNULL, path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
279
280
        for (old_path, new_path, file_id, kind,
281
             text_modified, meta_modified) in delta.renamed:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
282
            action = Action('renamed', [kind, old_path], [(new_path,)])
1185.82.104 by Aaron Bentley
Refactored action writing
283
            finish_action(action, file_id, kind, meta_modified, text_modified,
284
                          old_path, new_path)
1185.82.4 by John Arbash Meinel
Created output format, slightly simplified code
285
286
        for (path, file_id, kind,
287
             text_modified, meta_modified) in delta.modified:
1185.82.103 by Aaron Bentley
Serialize all actions through the action object
288
            action = Action('modified', [kind, path])
1185.82.104 by Aaron Bentley
Refactored action writing
289
            finish_action(action, file_id, kind, meta_modified, text_modified,
290
                          path, path)
1185.82.117 by Aaron Bentley
Handle last-modified changes on their own
291
292
        for path, file_id, kind in delta.unchanged:
293
            ie = new_tree.inventory[file_id]
294
            new_rev = getattr(ie, 'revision', None)
295
            if new_rev is None:
296
                continue
297
            old_rev = getattr(old_tree.inventory[ie.file_id], 'revision', None)
298
            if new_rev != old_rev:
299
                action = Action('modified', [ie.kind, 
300
                                             new_tree.id2path(ie.file_id)])
301
                action.add_property('last-changed', ie.revision)
302
                action.write(self.to_file)