/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.5.1 by John Arbash Meinel
Just an initial working step.
1
#!/usr/bin/env python
2
"""\
3
Just some work for generating a changeset.
4
"""
5
6
import bzrlib, bzrlib.errors
7
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
8
import common
9
10
from bzrlib.inventory import ROOT_ID
11
0.5.5 by John Arbash Meinel
Updated to now include the diffs
12
try:
13
    set
14
except NameError:
15
    from sets import Set as set
16
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
17
def _canonicalize_revision(branch, revno):
0.5.1 by John Arbash Meinel
Just an initial working step.
18
    """Turn some sort of revision information into a single
19
    set of from-to revision ids.
20
0.5.3 by John Arbash Meinel
Added a couple more bits
21
    A revision id can be None if there is no associated revison.
22
23
    :return: (old, new)
0.5.1 by John Arbash Meinel
Just an initial working step.
24
    """
25
    # This is a little clumsy because revision parsing may return
26
    # a single entry, or a list
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
27
    if revno is None:
28
        new = branch.last_patch()
29
    else:
30
        new = branch.lookup_revision(revno)
31
32
    if new is None:
33
        raise BzrCommandError('Cannot generate a changset with no commits in tree.')
34
35
    old = branch.get_revision(new).precursor
0.5.1 by John Arbash Meinel
Just an initial working step.
36
37
    return old, new
38
39
def _get_trees(branch, revisions):
40
    """Get the old and new trees based on revision.
41
    """
42
    if revisions[0] is None:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
43
        if hasattr(branch, 'get_root_id'): # Watch out for trees with labeled ROOT ids
44
            old_tree = EmptyTree(branch.get_root_id) 
45
        else:
46
            old_tree = EmptyTree()
0.5.1 by John Arbash Meinel
Just an initial working step.
47
    else:
48
        old_tree = branch.revision_tree(revisions[0])
49
50
    if revisions[1] is None:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
51
        # This is for the future, once we support rollup revisions
52
        # Or working tree revisions
0.5.1 by John Arbash Meinel
Just an initial working step.
53
        new_tree = branch.working_tree()
54
    else:
55
        new_tree = branch.revision_tree(revisions[1])
56
    return old_tree, new_tree
57
58
def _fake_working_revision(branch):
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
59
    """Fake a Revision object for the working tree.
60
    
61
    This is for the future, to support changesets against the working tree.
62
    """
0.5.1 by John Arbash Meinel
Just an initial working step.
63
    from bzrlib.revision import Revision
64
    import time
65
    from bzrlib.osutils import local_time_offset, \
66
            username
67
68
    precursor = branch.last_patch()
69
    precursor_sha1 = branch.get_revision_sha1(precursor)
70
71
    return Revision(timestamp=time.time(),
72
            timezone=local_time_offset(),
73
            committer=username(),
74
            precursor=precursor,
75
            precursor_sha1=precursor_sha1)
76
77
78
class MetaInfoHeader(object):
79
    """Maintain all of the header information about this
80
    changeset.
81
    """
82
0.5.5 by John Arbash Meinel
Updated to now include the diffs
83
    def __init__(self, branch, revisions, delta,
84
            full_remove=False, full_rename=False,
85
            external_diff_options = None,
86
            new_tree=None, old_tree=None,
87
            old_label = '', new_label = ''):
88
        """
89
        :param full_remove: Include the full-text for a delete
90
        :param full_rename: Include an add+delete patch for a rename
91
        """
0.5.1 by John Arbash Meinel
Just an initial working step.
92
        self.branch = branch
93
        self.delta = delta
0.5.5 by John Arbash Meinel
Updated to now include the diffs
94
        self.full_remove=full_remove
95
        self.full_rename=full_rename
96
        self.external_diff_options = external_diff_options
97
        self.old_label = old_label
98
        self.new_label = new_label
99
        self.old_tree = old_tree
100
        self.new_tree = new_tree
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
101
        self.to_file = None
102
        self.revno = None
103
        self.precursor_revno = None
104
105
        self._get_revision_list(revisions)
0.5.1 by John Arbash Meinel
Just an initial working step.
106
107
    def _get_revision_list(self, revisions):
108
        """This generates the list of all revisions from->to.
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
109
110
        This is for the future, when we support having a rollup changeset.
111
        For now, the list should only be one long.
0.5.1 by John Arbash Meinel
Just an initial working step.
112
        """
113
        old_revno = None
114
        new_revno = None
115
        rh = self.branch.revision_history()
116
        for revno, rev in enumerate(rh):
117
            if rev == revisions[0]:
118
                old_revno = revno
119
            if rev == revisions[1]:
120
                new_revno = revno
121
0.5.3 by John Arbash Meinel
Added a couple more bits
122
        self.revision_list = []
0.5.1 by John Arbash Meinel
Just an initial working step.
123
        if old_revno is None:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
124
            self.base_revision = None # Effectively the EmptyTree()
125
            old_revno = 0
0.5.3 by John Arbash Meinel
Added a couple more bits
126
        else:
127
            self.base_revision = self.branch.get_revision(rh[old_revno])
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
128
        if new_revno is None:
129
            # For the future, when we support working tree changesets.
0.5.3 by John Arbash Meinel
Added a couple more bits
130
            for rev_id in rh[old_revno+1:]:
0.5.1 by John Arbash Meinel
Just an initial working step.
131
                self.revision_list.append(self.branch.get_revision(rev_id))
132
            self.revision_list.append(_fake_working_revision(self.branch))
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
133
        else:
134
            for rev_id in rh[old_revno+1:new_revno+1]:
135
                self.revision_list.append(self.branch.get_revision(rev_id))
136
        self.precursor_revno = old_revno
137
        self.revno = new_revno
138
139
    def _write(self, txt, key=None):
140
        if key:
141
            self.to_file.write('# ' + key + ': ' + txt + '\n')
142
        else:
143
            self.to_file.write('# ' + txt + '\n')
0.5.1 by John Arbash Meinel
Just an initial working step.
144
145
    def write_meta_info(self, to_file):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
146
        """Write out the meta-info portion to the supplied file.
147
148
        :param to_file: Write out the meta information to the supplied
149
                        file
150
        """
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
151
        self.to_file = to_file
152
153
        self._write_header()
154
        self._write_diffs()
155
        self._write_footer()
156
157
    def _write_header(self):
158
        """Write the stuff that comes before the patches."""
159
        from bzrlib.osutils import username, format_date
160
        write = self._write
161
162
        for line in common.get_header():
163
            write(line)
164
165
        # This grabs the current username, what we really want is the
166
        # username from the actual patches.
167
        #write(username(), key='committer')
168
        assert len(self.revision_list) == 1
169
        rev = self.revision_list[0]
170
        write(rev.committer, key='committer')
171
        write(format_date(rev.timestamp, offset=rev.timezone), key='date')
172
        write(str(self.revno), key='revno')
173
        write(rev.revision_id, key='revision')
174
175
        if self.base_revision:
176
            write(self.base_revision.revision_id, key='precursor')
177
            write(str(self.precursor_revno), key='precursor revno')
178
179
0.5.4 by John Arbash Meinel
Added printout of file ids, need directory ids
180
        write('')
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
181
        self.to_file.write('\n')
182
183
    def _write_footer(self):
184
        """Write the stuff that comes after the patches.
185
186
        This is meant to be more meta-information, which people probably don't want
187
        to read, but which is required for proper bzr operation.
188
        """
189
        write = self._write
190
191
        write('BEGIN BZR FOOTER')
192
193
        assert len(self.revision_list) == 1 # We only handle single revision entries
194
        write(self.branch.get_revision_sha1(self.revision_list[0].revision_id), key='revision sha1')
0.5.3 by John Arbash Meinel
Added a couple more bits
195
        if self.base_revision:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
196
            write(self.branch.get_revision_sha1(self.base_revision.revision_id), 'precursor sha1')
197
198
        self._write_ids()
199
200
        write('END BZR FOOTER')
201
202
    def _write_revisions(self):
203
        """Not used. Used for writing multiple revisions."""
0.5.3 by John Arbash Meinel
Added a couple more bits
204
        first = True
205
        for rev in self.revision_list:
206
            if rev.revision_id is not None:
207
                if first:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
208
                    self._write('revisions:')
0.5.3 by John Arbash Meinel
Added a couple more bits
209
                    first = False
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
210
                self._write(' '*4 + rev.revision_id + '\t' + self.branch.get_revision_sha1(rev.revision_id))
211
212
213
    def _write_ids(self):
214
        if hasattr(self.branch, 'get_root_id'):
215
            root_id = self.branch.get_root_id()
216
        else:
217
            root_id = ROOT_ID
218
        seen_ids = set([root_id])
0.5.5 by John Arbash Meinel
Updated to now include the diffs
219
        need_ids = set()
220
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
221
        to_file = self.to_file
222
0.5.5 by John Arbash Meinel
Updated to now include the diffs
223
        def _write_entry(file_id, path=None):
224
            if file_id in self.new_tree.inventory:
225
                ie = self.new_tree.inventory[file_id]
226
            elif file_id in self.old_tree.inventory:
227
                ie = self.new_tree.inventory[file_id]
228
            else:
229
                ie = None
230
            if not path and ie:
231
                path = ie.name
232
            to_file.write(path.encode('utf-8'))
233
            to_file.write('\t')
234
            to_file.write(file_id.encode('utf-8'))
235
            if ie and ie.parent_id:
236
                to_file.write('\t')
237
                to_file.write(ie.parent_id.encode('utf-8'))
238
                if ie.parent_id not in seen_ids:
239
                    need_ids.add(ie.parent_id)
240
            seen_ids.add(ie.file_id)
241
            to_file.write('\n')
242
243
        class _write_kind(object):
244
            def __init__(self, kind):
245
                self.first = True
246
                self.kind = kind
0.5.4 by John Arbash Meinel
Added printout of file ids, need directory ids
247
            def __call__(self, info):
248
                if self.first:
249
                    self.first = False
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
250
                    to_file.write('# %s ids:\n' % self.kind)
251
                to_file.write('#    ')
0.5.5 by John Arbash Meinel
Updated to now include the diffs
252
                _write_entry(info[1], info[0])
253
254
        def _write_all(kind):
255
            writer = _write_kind(kind)
256
            for info in self.delta.removed:
257
                if info[2] == kind:
258
                    writer(info)
259
            for info in self.delta.added:
260
                if info[2] == kind:
261
                    writer(info)
262
            for info in self.delta.renamed:
263
                if info[3] == kind:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
264
                    writer(info[1:3])
0.5.5 by John Arbash Meinel
Updated to now include the diffs
265
            for info in self.delta.modified:
266
                if info[2] == kind:
267
                    writer(info)
268
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
269
        self._write(root_id, key='tree root id')
270
0.5.5 by John Arbash Meinel
Updated to now include the diffs
271
        _write_all('file')
272
        _write_all('directory')
273
274
        first = True
275
        while len(need_ids) > 0:
276
            file_id = need_ids.pop()
277
            if file_id in seen_ids:
278
                continue
279
            seen_ids.add(file_id)
280
            if first:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
281
                self.to_file.write('# parent ids: ')
0.5.5 by John Arbash Meinel
Updated to now include the diffs
282
                first = False
283
            else:
284
                to_file.write('#             ')
285
            _write_entry(file_id)
286
287
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
288
    def _write_diffs(self):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
289
        """Write out the specific diffs"""
290
        from bzrlib.diff import internal_diff, external_diff
291
        DEVNULL = '/dev/null'
292
293
        if self.external_diff_options:
294
            assert isinstance(self.external_diff_options, basestring)
295
            opts = self.external_diff_options.split()
296
            def diff_file(olab, olines, nlab, nlines, to_file):
297
                external_diff(olab, olines, nlab, nlines, to_file, opts)
298
        else:
299
            diff_file = internal_diff
300
301
        for path, file_id, kind in self.delta.removed:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
302
            print >>self.to_file, '*** removed %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
303
            if kind == 'file' and self.full_remove:
304
                diff_file(self.old_label + path,
305
                          self.old_tree.get_file(file_id).readlines(),
306
                          DEVNULL, 
307
                          [],
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
308
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
309
    
310
        for path, file_id, kind in self.delta.added:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
311
            print >>self.to_file, '*** added %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
312
            if kind == 'file':
313
                diff_file(DEVNULL,
314
                          [],
315
                          self.new_label + path,
316
                          self.new_tree.get_file(file_id).readlines(),
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
317
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
318
    
319
        for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
320
            print >>self.to_file, '*** renamed %s %r => %r' % (kind, old_path, new_path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
321
            if self.full_rename and kind == 'file':
322
                diff_file(self.old_label + old_path,
323
                          self.old_tree.get_file(file_id).readlines(),
324
                          DEVNULL, 
325
                          [],
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
326
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
327
                diff_file(DEVNULL,
328
                          [],
329
                          self.new_label + new_path,
330
                          self.new_tree.get_file(file_id).readlines(),
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
331
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
332
            elif text_modified:
333
                    diff_file(self.old_label + old_path,
334
                              self.old_tree.get_file(file_id).readlines(),
335
                              self.new_label + new_path,
336
                              self.new_tree.get_file(file_id).readlines(),
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
337
                              self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
338
    
339
        for path, file_id, kind in self.delta.modified:
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
340
            print >>self.to_file, '*** modified %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
341
            if kind == 'file':
342
                diff_file(self.old_label + path,
343
                          self.old_tree.get_file(file_id).readlines(),
344
                          self.new_label + path,
345
                          self.new_tree.get_file(file_id).readlines(),
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
346
                          self.to_file)
0.5.1 by John Arbash Meinel
Just an initial working step.
347
348
def show_changeset(branch, revision=None, specific_files=None,
0.5.5 by John Arbash Meinel
Updated to now include the diffs
349
        external_diff_options=None, to_file=None,
350
        include_full_diff=False):
0.5.1 by John Arbash Meinel
Just an initial working step.
351
    from bzrlib.diff import compare_trees
352
353
    if to_file is None:
354
        import sys
355
        to_file = sys.stdout
356
    revisions = _canonicalize_revision(branch, revision)
357
358
    old_tree, new_tree = _get_trees(branch, revisions)
359
360
    delta = compare_trees(old_tree, new_tree, want_unchanged=False,
361
                          specific_files=specific_files)
362
0.5.5 by John Arbash Meinel
Updated to now include the diffs
363
    meta = MetaInfoHeader(branch, revisions, delta,
364
            external_diff_options=external_diff_options,
365
            old_tree=old_tree, new_tree=new_tree)
0.5.1 by John Arbash Meinel
Just an initial working step.
366
    meta.write_meta_info(to_file)
367
368