~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to gen_changeset.py

  • Committer: John Arbash Meinel
  • Date: 2005-07-10 06:40:38 UTC
  • mto: (0.5.85) (1185.82.1 bzr-w-changeset)
  • mto: This revision was merged to the branch mainline in revision 1738.
  • Revision ID: john@arbash-meinel.com-20050710064038-d59be0a0a1f2d5f4
(broken), starting to change the syntax of the command to allow cset to take a base and a target.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
except NameError:
16
16
    from sets import Set as set
17
17
 
18
 
def _get_trees(branch, revisions):
19
 
    """Get the old and new trees based on revision.
20
 
    """
21
 
    from bzrlib.tree import EmptyTree
22
 
    if revisions[0] is None:
23
 
        if hasattr(branch, 'get_root_id'): # Watch out for trees with labeled ROOT ids
24
 
            old_tree = EmptyTree(branch.get_root_id) 
25
 
        else:
26
 
            old_tree = EmptyTree()
27
 
    else:
28
 
        old_tree = branch.revision_tree(revisions[0])
29
 
 
30
 
    if revisions[1] is None:
31
 
        raise BzrCommandError('Cannot form a bzr changeset with no committed revisions')
32
 
    else:
33
 
        new_tree = branch.revision_tree(revisions[1])
34
 
    return old_tree, new_tree
35
 
 
36
18
def _fake_working_revision(branch):
37
19
    """Fake a Revision object for the working tree.
38
20
    
52
34
            precursor=precursor,
53
35
            precursor_sha1=precursor_sha1)
54
36
 
 
37
def _get_revision_set(branch):
 
38
    """Get the set of all revisions that are in the ancestry
 
39
    of this branch.
 
40
    """
 
41
    this_revs = set()
 
42
    to_search = [branch.last_patch()]
 
43
 
 
44
    while len(to_search) > 0:
 
45
        rev_id = to_search.pop(0)
 
46
        if rev_id in this_revs:
 
47
            continue
 
48
        this_revs.add(rev_id)
 
49
        rev = branch.get_revision(rev_id)
 
50
        for parent in rev.parents:
 
51
            if parent.revision_id not in this_revs:
 
52
                to_search.append(parent.revision_id)
 
53
    return this_revs
 
54
 
 
55
def _find_best_base(branch, other_rev_id, other_branch=None):
 
56
    """Find the best base revision based on ancestry.
 
57
    All revisions should already be pulled into the local tree.
 
58
    """
 
59
    this_revs = _get_revision_set(branch)
 
60
 
 
61
    # This does a breadth first search through history, looking for
 
62
    # something which matches
 
63
    checked = set()
 
64
    to_check = [other_rev_id]
 
65
    while len(to_check) > 0:
 
66
        # Removing the '0' would make this depth-first search
 
67
        rev_id = to_check.pop(0)
 
68
        if rev_id in checked:
 
69
            continue
 
70
        checked.add(rev_id)
 
71
        if rev_id in this_revs:
 
72
            return rev_id
 
73
 
 
74
        if rev_id in branch.revision_store:
 
75
            rev = branch.get_revision(rev_id)
 
76
        elif (other_branch is not None 
 
77
                and rev_id in other_branch.revision_store):
 
78
            rev = other_branch.get_revision(rev_id)
 
79
        else:
 
80
            # Should we just continue here?
 
81
            warning('Could not find revision for rev: {%s}'
 
82
                    % rev_id)
 
83
            continue
 
84
 
 
85
 
 
86
        for parent in rev.parents:
 
87
            if parent.revision_id not in checked:
 
88
                to_check.append(parent.revision_id)
 
89
 
 
90
    return None
 
91
 
 
92
def _create_ancestry_to_rev(branch, ancestor_rev_id, this_rev_id):
 
93
    """Return a listing of revisions, which traces back from this_rev_id
 
94
    all the way back to the ancestor_rev_id.
 
95
    """
 
96
    # This is an optimization, when both target and base
 
97
    # exist in the revision history, we should already have
 
98
    # a valid listing of revision ancestry.
 
99
    rh = branch.revision_history()
 
100
    if ancestor_rev_id in rh and this_rev_id in rh:
 
101
        ancestor_idx = rh.index(ancestor_rev_id)
 
102
        this_rev_idx = rh.index(this_rev_id)
 
103
        if ancestor_idx > this_rev_idx:
 
104
            raise BzrCommandError('Revision {%s} is a child not an ancestor'
 
105
                    ' of {%s}' % (ancestor_rev_id, this_rev_id))
 
106
        rh_list = rh[ancestor_idx:this_rev_idx+1]
 
107
        rh_list.reverse()
 
108
        # return rh_list
 
109
 
 
110
    # I considered using depth-first search, as it is a little
 
111
    # bit less resource intensive, and it should favor generating
 
112
    # paths that are the same as revision_history
 
113
    # but since breadth-first-search is generally used
 
114
    # we will use that
 
115
    # 
 
116
    # WARNING: In the presence of merges, there are cases where
 
117
    # breadth first search will return a very different path
 
118
    # than revision_history or depth first search. Imaging the following:
 
119
    #
 
120
    # rh: A -> B -> C -> D -> E -> F
 
121
    #     |                        ^
 
122
    #     |                        |
 
123
    #     +--> Z ------------------+
 
124
    #
 
125
    # In this case, Starting with F, looking for A will return
 
126
    # A-F for a revision_history search, but breadth-first will
 
127
    # return A,Z,F since it is a much shorter path, and with
 
128
    # F merging Z, it looks like a shortcut.
 
129
    #
 
130
    # But since A-F seems to be the more "correct" history
 
131
    # for F, we might consider that revision_history should always
 
132
    # be consulted first, and if not found there, to use breadth
 
133
    # first search.
 
134
    checked_rev_ids = set()
 
135
 
 
136
    cur_trails = [[this_rev_id]]
 
137
    
 
138
    while len(cur_trails) > 0:
 
139
        cur_trail = cur_trails.pop(0)
 
140
        cur_rev_id = cur_trail[-1]
 
141
        if cur_rev_id in checked_rev_ids:
 
142
            continue
 
143
        checked_rev_ids.add(cur_rev_id)
 
144
 
 
145
        if cur_rev_id == ancestor_rev_id:
 
146
            return cur_trail
 
147
 
 
148
        if rev_id in branch.revision_store:
 
149
            rev = branch.get_revision(rev_id)
 
150
        else:
 
151
            # Should we just continue here?
 
152
            warning('Could not find revision for rev: {%s}, unable to'
 
153
                    ' trace ancestry.' % rev_id)
 
154
            continue
 
155
 
 
156
        for parent in rev.parents:
 
157
            if parent.revision_id not in checked:
 
158
                to_check.append(cur_trail + [parent.revision_id])
 
159
 
 
160
    raise BzrCommandError('Revision id {%s} not an ancestor of {%s}'
 
161
            % (ancestor_rev_id, this_rev_id))
55
162
 
56
163
class MetaInfoHeader(object):
57
164
    """Maintain all of the header information about this
58
165
    changeset.
59
166
    """
60
167
 
61
 
    def __init__(self, branch, revisions, delta,
62
 
            full_remove=True, full_rename=False,
63
 
            new_tree=None, old_tree=None,
64
 
            old_label = '', new_label = ''):
 
168
    def __init__(self,
 
169
            base_branch, base_rev_id, base_tree,
 
170
            target_branch, target_rev_id, target_tree,
 
171
            delta,
 
172
            starting_rev_id=None,
 
173
            full_remove=False, full_rename=False,
 
174
            base_label = 'BASE', target_label = 'TARGET'):
65
175
        """
66
176
        :param full_remove: Include the full-text for a delete
67
177
        :param full_rename: Include an add+delete patch for a rename
68
178
 
69
179
        """
70
 
        self.branch = branch
 
180
        self.base_branch = base_branch
 
181
        self.base_rev_id = base_rev_id
 
182
        self.base_tree = base_tree
 
183
 
 
184
        self.target_branch = target_branch
 
185
        self.target_rev_id = target_rev_id
 
186
        self.target_tree = target_tree
 
187
 
71
188
        self.delta = delta
 
189
 
 
190
        self.starting_rev_id = starting_rev_id
 
191
 
72
192
        self.full_remove=full_remove
73
193
        self.full_rename=full_rename
74
 
        self.old_label = old_label
 
194
 
 
195
        self.base_label = base_label
75
196
        self.new_label = new_label
76
 
        self.old_tree = old_tree
77
 
        self.new_tree = new_tree
 
197
 
78
198
        self.to_file = None
79
199
        #self.revno = None
80
200
        #self.parent_revno = None
86
206
        self.committer = None
87
207
        self.message = None
88
208
 
89
 
        self._get_revision_list(revisions)
 
209
        self._get_revision_list()
90
210
 
91
 
    def _get_revision_list(self, revisions):
 
211
    def _get_revision_list(self):
92
212
        """This generates the list of all revisions from->to.
93
 
 
94
 
        This is for having a rollup changeset.
 
213
        It fills out the internal self.revision_list with Revision
 
214
        entries which should be in the changeset.
95
215
        """
96
 
        old_revno = None
97
 
        new_revno = None
98
 
        rh = self.branch.revision_history()
99
216
        for revno, rev in enumerate(rh):
100
217
            if rev == revisions[0]:
101
 
                old_revno = revno
 
218
                base_revno = revno
102
219
            if rev == revisions[1]:
103
220
                new_revno = revno
104
221
 
105
222
        self.revision_list = []
106
 
        if old_revno is None:
 
223
        if base_revno is None:
107
224
            self.base_revision = None # Effectively the EmptyTree()
108
 
            old_revno = -1
 
225
            base_revno = -1
109
226
        else:
110
 
            self.base_revision = self.branch.get_revision(rh[old_revno])
 
227
            self.base_revision = self.branch.get_revision(rh[base_revno])
111
228
        if new_revno is None:
112
229
            # For the future, when we support working tree changesets.
113
 
            for rev_id in rh[old_revno+1:]:
 
230
            for rev_id in rh[base_revno+1:]:
114
231
                self.revision_list.append(self.branch.get_revision(rev_id))
115
232
            self.revision_list.append(_fake_working_revision(self.branch))
116
233
        else:
117
 
            for rev_id in rh[old_revno+1:new_revno+1]:
 
234
            for rev_id in rh[base_revno+1:new_revno+1]:
118
235
                self.revision_list.append(self.branch.get_revision(rev_id))
119
 
        #self.parent_revno = old_revno+1
 
236
        #self.parent_revno = base_revno+1
120
237
        #self.revno = new_revno+1
121
238
 
122
239
    def _write(self, txt, key=None):
182
299
 
183
300
        self._write_revisions()
184
301
 
185
 
        #self._write_ids()
186
 
        self._write_text_ids()
187
 
 
188
302
    def _write_revisions(self):
189
303
        """Not used. Used for writing multiple revisions."""
190
304
        from common import format_highres_date
213
327
                for line in rev.message.split('\n'):
214
328
                    self.to_file.write('#       %s\n' % line)
215
329
 
216
 
 
217
 
    def _write_ids(self):
218
 
        if hasattr(self.branch, 'get_root_id'):
219
 
            root_id = self.branch.get_root_id()
220
 
        else:
221
 
            root_id = ROOT_ID
222
 
 
223
 
        old_ids = set()
224
 
        new_ids = set()
225
 
 
226
 
        for path, file_id, kind in self.delta.removed:
227
 
            old_ids.add(file_id)
228
 
        for path, file_id, kind in self.delta.added:
229
 
            new_ids.add(file_id)
230
 
        for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
231
 
            old_ids.add(file_id)
232
 
            new_ids.add(file_id)
233
 
        for path, file_id, kind in self.delta.modified:
234
 
            new_ids.add(file_id)
235
 
 
236
 
        self._write(root_id, key='tree root id')
237
 
 
238
 
        def write_ids(tree, id_set, name):
239
 
            if len(id_set) > 0:
240
 
                self.to_file.write('# %s ids:\n' % name)
241
 
            seen_ids = set([root_id])
242
 
            while len(id_set) > 0:
243
 
                file_id = id_set.pop()
244
 
                if file_id in seen_ids:
245
 
                    continue
246
 
                seen_ids.add(file_id)
247
 
                ie = tree.inventory[file_id]
248
 
                if ie.parent_id not in seen_ids:
249
 
                    id_set.add(ie.parent_id)
250
 
                path = tree.inventory.id2path(file_id)
251
 
                self.to_file.write('#    %s\t%s\t%s\n'
252
 
                        % (path, file_id,
253
 
                            ie.parent_id))
254
 
        write_ids(self.new_tree, new_ids, 'file')
255
 
        write_ids(self.old_tree, old_ids, 'old file')
256
 
 
257
 
    def _write_text_ids(self):
258
 
        pass
259
 
 
260
330
    def _write_diffs(self):
261
331
        """Write out the specific diffs"""
262
332
        from bzrlib.diff import internal_diff, external_diff
290
360
                    encode(path))
291
361
            if kind == 'file' and self.full_remove:
292
362
                diff_file(self.old_label + path,
293
 
                          self.old_tree.get_file(file_id).readlines(),
 
363
                          self.base_tree.get_file(file_id).readlines(),
294
364
                          DEVNULL, 
295
365
                          [],
296
366
                          self.to_file)
304
374
                diff_file(DEVNULL,
305
375
                          [],
306
376
                          self.new_label + path,
307
 
                          self.new_tree.get_file(file_id).readlines(),
 
377
                          self.target_tree.get_file(file_id).readlines(),
308
378
                          self.to_file)
309
379
    
310
380
        for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
313
383
                    get_text_id_str(file_id, text_modified))
314
384
            if self.full_rename and kind == 'file':
315
385
                diff_file(self.old_label + old_path,
316
 
                          self.old_tree.get_file(file_id).readlines(),
 
386
                          self.base_tree.get_file(file_id).readlines(),
317
387
                          DEVNULL, 
318
388
                          [],
319
389
                          self.to_file)
320
390
                diff_file(DEVNULL,
321
391
                          [],
322
392
                          self.new_label + new_path,
323
 
                          self.new_tree.get_file(file_id).readlines(),
 
393
                          self.target_tree.get_file(file_id).readlines(),
324
394
                          self.to_file)
325
395
            elif text_modified:
326
396
                    diff_file(self.old_label + old_path,
327
 
                              self.old_tree.get_file(file_id).readlines(),
 
397
                              self.base_tree.get_file(file_id).readlines(),
328
398
                              self.new_label + new_path,
329
 
                              self.new_tree.get_file(file_id).readlines(),
 
399
                              self.target_tree.get_file(file_id).readlines(),
330
400
                              self.to_file)
331
401
    
332
402
        for path, file_id, kind in self.delta.modified:
334
404
                    encode(path), get_text_id_str(file_id))
335
405
            if kind == 'file':
336
406
                diff_file(self.old_label + path,
337
 
                          self.old_tree.get_file(file_id).readlines(),
 
407
                          self.base_tree.get_file(file_id).readlines(),
338
408
                          self.new_label + path,
339
 
                          self.new_tree.get_file(file_id).readlines(),
 
409
                          self.target_tree.get_file(file_id).readlines(),
340
410
                          self.to_file)
341
411
 
342
 
def show_changeset(branch, revisions=None, to_file=None, include_full_diff=False):
 
412
def show_changeset(base_branch, base_rev_id,
 
413
        target_branch, target_rev_id,
 
414
        starting_rev_id = None,
 
415
        to_file=None, include_full_diff=False):
343
416
    from bzrlib.diff import compare_trees
344
417
 
345
418
    if to_file is None:
346
419
        import sys
347
420
        to_file = sys.stdout
348
 
    revisions = common.canonicalize_revision(branch, revisions)
349
 
 
350
 
    old_tree, new_tree = _get_trees(branch, revisions)
351
 
 
352
 
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
353
 
 
354
 
    meta = MetaInfoHeader(branch, revisions, delta,
355
 
            old_tree=old_tree, new_tree=new_tree)
 
421
    base_tree = base_branch.get_revision_tree(base_rev_id)
 
422
    target_tree = target_branch.get_revision_tree(target_rev_id)
 
423
 
 
424
    delta = compare_trees(base_tree, target_tree, want_unchanged=False)
 
425
 
 
426
    meta = MetaInfoHeader(base_branch, base_rev_id, base_tree,
 
427
            target_branch, target_rev_id, target_tree,
 
428
            delta,
 
429
            starting_rev_id=starting_rev_id,
 
430
            full_rename=include_full_diff, full_remove=include_full_diff)
356
431
    meta.write_meta_info(to_file)
357
432