/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
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
274
        # All files must be traceable back to the ROOT_ID
275
        # Since we may not have modified some of the parent
276
        # directories, add a new set of fields for "parent ids"
0.5.5 by John Arbash Meinel
Updated to now include the diffs
277
        first = True
278
        while len(need_ids) > 0:
279
            file_id = need_ids.pop()
280
            if file_id in seen_ids:
281
                continue
282
            seen_ids.add(file_id)
283
            if first:
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
284
                self.to_file.write('# parent ids:\n')
0.5.5 by John Arbash Meinel
Updated to now include the diffs
285
                first = False
0.5.9 by John Arbash Meinel
Now adding the patch information to the ChangesetInfo
286
            to_file.write('#    ')
0.5.5 by John Arbash Meinel
Updated to now include the diffs
287
            _write_entry(file_id)
288
289
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
290
    def _write_diffs(self):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
291
        """Write out the specific diffs"""
292
        from bzrlib.diff import internal_diff, external_diff
293
        DEVNULL = '/dev/null'
294
295
        if self.external_diff_options:
296
            assert isinstance(self.external_diff_options, basestring)
297
            opts = self.external_diff_options.split()
298
            def diff_file(olab, olines, nlab, nlines, to_file):
299
                external_diff(olab, olines, nlab, nlines, to_file, opts)
300
        else:
301
            diff_file = internal_diff
302
303
        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.
304
            print >>self.to_file, '*** removed %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
305
            if kind == 'file' and self.full_remove:
306
                diff_file(self.old_label + path,
307
                          self.old_tree.get_file(file_id).readlines(),
308
                          DEVNULL, 
309
                          [],
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
310
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
311
    
312
        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.
313
            print >>self.to_file, '*** added %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
314
            if kind == 'file':
315
                diff_file(DEVNULL,
316
                          [],
317
                          self.new_label + path,
318
                          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.
319
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
320
    
321
        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.
322
            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
323
            if self.full_rename and kind == 'file':
324
                diff_file(self.old_label + old_path,
325
                          self.old_tree.get_file(file_id).readlines(),
326
                          DEVNULL, 
327
                          [],
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
328
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
329
                diff_file(DEVNULL,
330
                          [],
331
                          self.new_label + new_path,
332
                          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.
333
                          self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
334
            elif text_modified:
335
                    diff_file(self.old_label + old_path,
336
                              self.old_tree.get_file(file_id).readlines(),
337
                              self.new_label + new_path,
338
                              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.
339
                              self.to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
340
    
341
        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.
342
            print >>self.to_file, '*** modified %s %r' % (kind, path)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
343
            if kind == 'file':
344
                diff_file(self.old_label + path,
345
                          self.old_tree.get_file(file_id).readlines(),
346
                          self.new_label + path,
347
                          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.
348
                          self.to_file)
0.5.1 by John Arbash Meinel
Just an initial working step.
349
350
def show_changeset(branch, revision=None, specific_files=None,
0.5.5 by John Arbash Meinel
Updated to now include the diffs
351
        external_diff_options=None, to_file=None,
352
        include_full_diff=False):
0.5.1 by John Arbash Meinel
Just an initial working step.
353
    from bzrlib.diff import compare_trees
354
355
    if to_file is None:
356
        import sys
357
        to_file = sys.stdout
358
    revisions = _canonicalize_revision(branch, revision)
359
360
    old_tree, new_tree = _get_trees(branch, revisions)
361
362
    delta = compare_trees(old_tree, new_tree, want_unchanged=False,
363
                          specific_files=specific_files)
364
0.5.5 by John Arbash Meinel
Updated to now include the diffs
365
    meta = MetaInfoHeader(branch, revisions, delta,
366
            external_diff_options=external_diff_options,
367
            old_tree=old_tree, new_tree=new_tree)
0.5.1 by John Arbash Meinel
Just an initial working step.
368
    meta.write_meta_info(to_file)
369
370