/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
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
6
import bzrlib
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
7
import os
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
9
from bzrlib.inventory import ROOT_ID
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
10
from bzrlib.errors import BzrCommandError, NotAncestor
0.5.74 by John Arbash Meinel
Fixed handling when base does not exist in local tree, and workaround for older revisions without precursor sha hashes.
11
from bzrlib.trace import warning, mutter
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
12
from collections import deque
13
from bzrlib.revision import (common_ancestor, MultipleRevisionSources,
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
14
                             get_intervening_revisions, NULL_REVISION)
15
from bzrlib.diff import internal_diff, compare_trees
0.5.1 by John Arbash Meinel
Just an initial working step.
16
17
class MetaInfoHeader(object):
18
    """Maintain all of the header information about this
19
    changeset.
20
    """
21
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
22
    def __init__(self,
23
            base_branch, base_rev_id, base_tree,
24
            target_branch, target_rev_id, target_tree,
25
            delta,
26
            starting_rev_id=None,
27
            full_remove=False, full_rename=False,
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
28
            message=None,
0.5.82 by John Arbash Meinel
Lots of changes, changing separators, updating tests, updated ChangesetTree to include text_ids
29
            base_label = 'orig', target_label = 'mod'):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
30
        """
31
        :param full_remove: Include the full-text for a delete
32
        :param full_rename: Include an add+delete patch for a rename
0.5.25 by John Arbash Meinel
Added some work to allow rollup revisions, and handling multiple parents.
33
0.5.5 by John Arbash Meinel
Updated to now include the diffs
34
        """
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
35
        self.base_branch = base_branch
36
        self.base_rev_id = base_rev_id
37
        self.base_tree = base_tree
0.5.78 by John Arbash Meinel
Working on test cases, starting with the empty project issues.
38
        if self.base_rev_id is not None:
39
            self.base_revision = self.base_branch.get_revision(self.base_rev_id)
40
        else:
41
            self.base_revision = None
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
42
43
        self.target_branch = target_branch
44
        self.target_rev_id = target_rev_id
45
        self.target_tree = target_tree
46
0.5.1 by John Arbash Meinel
Just an initial working step.
47
        self.delta = delta
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
48
49
        self.starting_rev_id = starting_rev_id
50
0.5.5 by John Arbash Meinel
Updated to now include the diffs
51
        self.full_remove=full_remove
52
        self.full_rename=full_rename
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
53
54
        self.base_label = base_label
0.5.71 by John Arbash Meinel
Cleaning up code for latest bzr.
55
        self.target_label = target_label
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
56
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
57
        self.to_file = None
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
58
        #self.revno = None
59
        #self.parent_revno = None
60
61
        # These are entries in the header.
62
        # They will be repeated in the footer,
63
        # only if they have changed
64
        self.date = None
65
        self.committer = None
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
66
        self.message = message
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
67
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
68
        self._get_revision_list()
0.5.1 by John Arbash Meinel
Just an initial working step.
69
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
70
    def _get_revision_list(self):
0.5.1 by John Arbash Meinel
Just an initial working step.
71
        """This generates the list of all revisions from->to.
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
72
        It fills out the internal self.revision_list with Revision
73
        entries which should be in the changeset.
0.5.1 by John Arbash Meinel
Just an initial working step.
74
        """
0.5.113 by John Arbash Meinel
Small performance enhancement.
75
        # Performance, without locking here, a new lock is taken and
76
        # broken for every revision (6k+ total locks for the bzr.dev tree)
77
        self.target_branch.lock_read()
78
        self.base_branch.lock_read()
79
        try:
80
            source = MultipleRevisionSources(self.target_branch, self.base_branch)
81
            if self.starting_rev_id is None:
0.5.115 by John Arbash Meinel
Getting closer to being able to read back the changesets, still broken, though.
82
                if self.base_rev_id is None:
83
                    self.starting_rev_id = NULL_REVISION
84
                else:
85
                    self.starting_rev_id = common_ancestor(self.target_rev_id, 
86
                        self.base_rev_id, source)
0.5.113 by John Arbash Meinel
Small performance enhancement.
87
88
            rev_id_list = get_intervening_revisions(self.starting_rev_id,
89
                self.target_rev_id, source, self.target_branch.revision_history())
90
91
            self.revision_list = [source.get_revision(rid) for rid in rev_id_list]
92
        finally:
93
            self.base_branch.unlock()
94
            self.target_branch.unlock()
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
95
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
96
    def _write(self, txt, key=None, encode=True, indent=1):
97
        from common import encode as _encode
98
        if encode:
99
            def write(txt):
100
                self.to_file.write(_encode(txt))
101
        else:
102
            def write(txt):
103
                self.to_file.write(txt)
104
        if indent > 0:
105
            write('#' + (' ' * indent))
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
106
        if key:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
107
            if txt:
108
                write('%s: %s\n' % (key, txt))
109
            else:
110
                write('%s:\n' % key)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
111
        else:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
112
            write('%s\n' % (txt,))
0.5.1 by John Arbash Meinel
Just an initial working step.
113
114
    def write_meta_info(self, to_file):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
115
        """Write out the meta-info portion to the supplied file.
116
117
        :param to_file: Write out the meta information to the supplied
118
                        file
119
        """
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
120
        self.to_file = to_file
121
122
        self._write_header()
123
        self._write_diffs()
124
        self._write_footer()
125
126
    def _write_header(self):
127
        """Write the stuff that comes before the patches."""
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
128
        from common import format_highres_date, get_header
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
129
        write = self._write
130
0.5.81 by John Arbash Meinel
Cleaning up from pychecker.
131
        for line in get_header():
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
132
            write(line)
133
0.5.27 by John Arbash Meinel
Now capable of generating rollup changesets.
134
        # Print out the basic information about the 'target' revision
135
        rev = self.revision_list[-1]
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
136
        write(rev.committer, key='committer')
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
137
        self.committer = rev.committer
138
        self.date = format_highres_date(rev.timestamp, offset=rev.timezone)
139
        write(self.date, key='date')
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
140
        if self.message is None:
141
            if rev.message is not None:
142
                self.message = rev.message
143
        if self.message:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
144
            write('', key='message')
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
145
            for line in self.message.split('\n'):
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
146
                write(txt=line, indent=4)
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
147
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
148
        write('') # line with just '#'
149
        write('', indent=0) # Empty line
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
150
151
    def _write_footer(self):
152
        """Write the stuff that comes after the patches.
153
154
        This is meant to be more meta-information, which people probably don't want
155
        to read, but which is required for proper bzr operation.
156
        """
157
        write = self._write
158
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
159
        # What should we print out for an Empty base revision?
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
160
        if len(self.revision_list[0].parent_ids) == 0:
0.5.59 by John Arbash Meinel
Several fixes for handling the case where you are doing a changeset against revno=0 (Null base)
161
            assumed_base = None
162
        else:
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
163
            assumed_base = self.revision_list[0].parent_ids[0]
164
0.5.59 by John Arbash Meinel
Several fixes for handling the case where you are doing a changeset against revno=0 (Null base)
165
        if (self.base_revision is not None 
166
                and self.base_revision.revision_id != assumed_base):
0.5.57 by John Arbash Meinel
Simplified the header, only output base if it is not the expected one.
167
            base = self.base_revision.revision_id
168
            write(base, key='base')
0.5.74 by John Arbash Meinel
Fixed handling when base does not exist in local tree, and workaround for older revisions without precursor sha hashes.
169
            write(self.base_branch.get_revision_sha1(base), key='base sha1')
0.5.17 by John Arbash Meinel
adding apply-changset, plus more meta information.
170
0.5.25 by John Arbash Meinel
Added some work to allow rollup revisions, and handling multiple parents.
171
        self._write_revisions()
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
172
173
    def _write_revisions(self):
174
        """Not used. Used for writing multiple revisions."""
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
175
        from common import format_highres_date, encode
176
177
        write = self._write
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
178
0.5.3 by John Arbash Meinel
Added a couple more bits
179
        for rev in self.revision_list:
0.5.25 by John Arbash Meinel
Added some work to allow rollup revisions, and handling multiple parents.
180
            rev_id = rev.revision_id
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
181
            write(rev_id, key='revision')
182
            write(self.target_branch.get_revision_sha1(rev_id),
183
                    key = 'sha1', indent=4)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
184
            if rev.committer != self.committer:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
185
                write(rev.committer, key='committer', indent=4)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
186
            date = format_highres_date(rev.timestamp, rev.timezone)
187
            if date != self.date:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
188
                write(date, key='date', indent=4)
189
            write(rev.inventory_sha1, key='inventory sha1', indent=4)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
190
            if len(rev.parent_ids) > 0:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
191
                write(txt='', key='parents', indent=4)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
192
                for p_id in rev.parent_ids:
193
                    p_sha1 = self.target_branch.get_revision_sha1(p_id)
0.5.117 by John Arbash Meinel
Almost there. Just need to track down a few remaining bugs.
194
                    if p_sha1 is not None:
195
                        write(p_id + '\t' + p_sha1, indent=7)
196
                    else:
0.5.74 by John Arbash Meinel
Fixed handling when base does not exist in local tree, and workaround for older revisions without precursor sha hashes.
197
                        warning('Rev id {%s} parent {%s} missing sha hash.'
198
                                % (rev_id, p_id))
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
199
                        write(p_id, indent=7)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
200
            if rev.message and rev.message != self.message:
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
201
                write('', key='message', indent=4)
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
202
                for line in rev.message.split('\n'):
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
203
                    write(line, indent=7)
0.5.25 by John Arbash Meinel
Added some work to allow rollup revisions, and handling multiple parents.
204
0.5.7 by John Arbash Meinel
Added a bunch more information about changesets. Can now read back in all of the meta information.
205
    def _write_diffs(self):
0.5.5 by John Arbash Meinel
Updated to now include the diffs
206
        """Write out the specific diffs"""
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
207
        def pjoin(*args):
208
            # Only forward slashes in changesets
209
            return os.path.join(*args).replace('\\', '/')
210
211
        def _maybe_diff(old_label, old_path, old_tree, file_id,
212
                        new_label, new_path, new_tree, text_modified,
213
                        kind, to_file, diff_file):
214
            if text_modified:
215
                new_entry = new_tree.inventory[file_id]
216
                old_tree.inventory[file_id].diff(diff_file,
217
                                                 pjoin(old_label, old_path), old_tree,
218
                                                 pjoin(new_label, new_path), new_entry, 
219
                                                 new_tree, to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
220
        DEVNULL = '/dev/null'
221
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
222
        diff_file = internal_diff
223
        # Get the target tree so that we can check for
224
        # Appropriate text ids.
0.5.73 by John Arbash Meinel
Some fixups for gen_changeset.
225
        rev_id = self.target_rev_id
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
226
227
        new_label = self.target_label
228
        new_tree = self.target_tree
229
230
        old_tree = self.base_tree
231
        old_label = self.base_label
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
232
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
233
        write = self._write
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
234
        to_file = self.to_file
235
236
237
        def get_rev_id_str(file_id, kind):
238
            last_changed_rev_id = new_tree.inventory[file_id].revision
239
240
            if rev_id != last_changed_rev_id:
241
                return ' // last-changed:' + last_changed_rev_id
0.5.55 by John Arbash Meinel
Lots of updates. Using a minimized annotations for changesets.
242
            else:
243
                return ''
244
0.5.5 by John Arbash Meinel
Updated to now include the diffs
245
        for path, file_id, kind in self.delta.removed:
0.5.100 by John Arbash Meinel
Switching from *** to ===
246
            write('=== removed %s %s' % (kind, path), indent=0)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
247
            if self.full_remove:
248
                old_tree.inventory[file_id].diff(diff_file, pjoin(old_label, path), old_tree,
249
                                                 DEVNULL, None, None, to_file)
0.5.5 by John Arbash Meinel
Updated to now include the diffs
250
        for path, file_id, kind in self.delta.added:
0.5.100 by John Arbash Meinel
Switching from *** to ===
251
            write('=== added %s %s // file-id:%s%s' % (kind,
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
252
                    path, file_id, get_rev_id_str(file_id, kind)),
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
253
                    indent=0)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
254
            new_tree.inventory[file_id].diff(diff_file, pjoin(new_label, path), new_tree,
255
                                             DEVNULL, None, None, to_file, 
256
                                             reverse=True)
257
        for (old_path, new_path, file_id, kind,
258
             text_modified, meta_modified) in self.delta.renamed:
259
            # TODO: Handle meta_modified
260
            #prop_str = get_prop_change(meta_modified)
0.5.100 by John Arbash Meinel
Switching from *** to ===
261
            write('=== renamed %s %s // %s%s' % (kind,
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
262
                    old_path, new_path,
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
263
                    get_rev_id_str(file_id, kind)),
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
264
                    indent=0)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
265
            if self.full_rename:
266
                # Looks like a delete + add
267
                old_tree.inventory[file_id].diff(diff_file, pjoin(old_label, path), old_tree,
268
                                                 DEVNULL, None, None, to_file)
269
                new_tree.inventory[file_id].diff(diff_file, pjoin(new_label, path), new_tree,
270
                                                 DEVNULL, None, None, to_file, 
271
                                                 reverse=True)
272
            else:
273
                _maybe_diff(old_label, old_path, old_tree, file_id,
274
                            new_label, new_path, new_tree,
275
                            text_modified, kind, to_file, diff_file)
276
277
        for (path, file_id, kind,
278
             text_modified, meta_modified) in self.delta.modified:
279
            # TODO: Handle meta_modified
280
            #prop_str = get_prop_change(meta_modified)
0.5.100 by John Arbash Meinel
Switching from *** to ===
281
            write('=== modified %s %s%s' % (kind,
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
282
                    path, get_rev_id_str(file_id, kind)),
0.5.87 by John Arbash Meinel
Handling international characters, added more test cases.
283
                    indent=0)
0.5.112 by John Arbash Meinel
Upgrading to v5 (ignoring compatibility)
284
            _maybe_diff(old_label, path, old_tree, file_id,
285
                        new_label, path, new_tree,
286
                        text_modified, kind, to_file, diff_file)
287
 
0.5.1 by John Arbash Meinel
Just an initial working step.
288
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
289
def show_changeset(base_branch, base_rev_id,
290
        target_branch, target_rev_id,
291
        starting_rev_id = None,
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
292
        to_file=None, include_full_diff=False,
293
        message=None):
0.5.1 by John Arbash Meinel
Just an initial working step.
294
295
    if to_file is None:
296
        import sys
297
        to_file = sys.stdout
0.5.71 by John Arbash Meinel
Cleaning up code for latest bzr.
298
    base_tree = base_branch.revision_tree(base_rev_id)
299
    target_tree = target_branch.revision_tree(target_rev_id)
0.5.68 by John Arbash Meinel
(broken), starting to change the syntax of the command to allow cset to take a base and a target.
300
301
    delta = compare_trees(base_tree, target_tree, want_unchanged=False)
302
303
    meta = MetaInfoHeader(base_branch, base_rev_id, base_tree,
304
            target_branch, target_rev_id, target_tree,
305
            delta,
306
            starting_rev_id=starting_rev_id,
0.5.103 by John Arbash Meinel
Updated to having a changeset specific message.
307
            full_rename=include_full_diff, full_remove=include_full_diff,
308
            message=message)
0.5.1 by John Arbash Meinel
Just an initial working step.
309
    meta.write_meta_info(to_file)
310