3
Just some work for generating a changeset.
6
import bzrlib, bzrlib.errors
10
from bzrlib.inventory import ROOT_ID
15
from sets import Set as set
17
def _canonicalize_revision(branch, revno):
18
"""Turn some sort of revision information into a single
19
set of from-to revision ids.
21
A revision id can be None if there is no associated revison.
25
# This is a little clumsy because revision parsing may return
26
# a single entry, or a list
28
new = branch.last_patch()
30
new = branch.lookup_revision(revno)
33
raise BzrCommandError('Cannot generate a changset with no commits in tree.')
35
old = branch.get_revision(new).precursor
39
def _get_trees(branch, revisions):
40
"""Get the old and new trees based on revision.
42
if revisions[0] is None:
43
if hasattr(branch, 'get_root_id'): # Watch out for trees with labeled ROOT ids
44
old_tree = EmptyTree(branch.get_root_id)
46
old_tree = EmptyTree()
48
old_tree = branch.revision_tree(revisions[0])
50
if revisions[1] is None:
51
# This is for the future, once we support rollup revisions
52
# Or working tree revisions
53
new_tree = branch.working_tree()
55
new_tree = branch.revision_tree(revisions[1])
56
return old_tree, new_tree
58
def _fake_working_revision(branch):
59
"""Fake a Revision object for the working tree.
61
This is for the future, to support changesets against the working tree.
63
from bzrlib.revision import Revision
65
from bzrlib.osutils import local_time_offset, \
68
precursor = branch.last_patch()
69
precursor_sha1 = branch.get_revision_sha1(precursor)
71
return Revision(timestamp=time.time(),
72
timezone=local_time_offset(),
75
precursor_sha1=precursor_sha1)
78
class MetaInfoHeader(object):
79
"""Maintain all of the header information about this
83
def __init__(self, branch, revisions, delta,
84
full_remove=True, full_rename=False,
85
external_diff_options = None,
86
new_tree=None, old_tree=None,
87
old_label = '', new_label = ''):
89
:param full_remove: Include the full-text for a delete
90
:param full_rename: Include an add+delete patch for a rename
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
103
self.precursor_revno = None
105
self._get_revision_list(revisions)
107
def _get_revision_list(self, revisions):
108
"""This generates the list of all revisions from->to.
110
This is for the future, when we support having a rollup changeset.
111
For now, the list should only be one long.
115
rh = self.branch.revision_history()
116
for revno, rev in enumerate(rh):
117
if rev == revisions[0]:
119
if rev == revisions[1]:
122
self.revision_list = []
123
if old_revno is None:
124
self.base_revision = None # Effectively the EmptyTree()
127
self.base_revision = self.branch.get_revision(rh[old_revno])
128
if new_revno is None:
129
# For the future, when we support working tree changesets.
130
for rev_id in rh[old_revno+1:]:
131
self.revision_list.append(self.branch.get_revision(rev_id))
132
self.revision_list.append(_fake_working_revision(self.branch))
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
139
def _write(self, txt, key=None):
141
self.to_file.write('# %s: %s\n' % (key, txt))
143
self.to_file.write('# %s\n' % (txt,))
145
def write_meta_info(self, to_file):
146
"""Write out the meta-info portion to the supplied file.
148
:param to_file: Write out the meta information to the supplied
151
self.to_file = to_file
157
def _write_header(self):
158
"""Write the stuff that comes before the patches."""
159
from bzrlib.osutils import username, format_date
162
for line in common.get_header():
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')
174
self.to_file.write('# message:\n')
175
for line in rev.message.split('\n'):
176
self.to_file.write('# %s\n' % line)
177
write(rev.revision_id, key='revision')
179
if self.base_revision:
180
write(self.base_revision.revision_id, key='precursor')
181
write(str(self.precursor_revno), key='precursor revno')
185
self.to_file.write('\n')
187
def _write_footer(self):
188
"""Write the stuff that comes after the patches.
190
This is meant to be more meta-information, which people probably don't want
191
to read, but which is required for proper bzr operation.
195
write('BEGIN BZR FOOTER')
197
assert len(self.revision_list) == 1 # We only handle single revision entries
198
rev = self.revision_list[0]
199
write(self.branch.get_revision_sha1(rev.revision_id),
201
if self.base_revision:
202
rev_id = self.base_revision.revision_id
203
write(self.branch.get_revision_sha1(rev_id),
204
key='precursor sha1')
206
write('%.9f' % rev.timestamp, key='timestamp')
207
write(str(rev.timezone), key='timezone')
211
write('END BZR FOOTER')
213
def _write_revisions(self):
214
"""Not used. Used for writing multiple revisions."""
216
for rev in self.revision_list:
217
if rev.revision_id is not None:
219
self._write('revisions:')
221
self._write(' '*4 + rev.revision_id + '\t' + self.branch.get_revision_sha1(rev.revision_id))
224
def _write_ids(self):
225
if hasattr(self.branch, 'get_root_id'):
226
root_id = self.branch.get_root_id()
233
for path, file_id, kind in self.delta.removed:
235
for path, file_id, kind in self.delta.added:
237
for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
240
for path, file_id, kind in self.delta.modified:
243
self._write(root_id, key='tree root id')
245
def write_ids(tree, id_set, name):
247
self.to_file.write('# %s ids:\n' % name)
248
seen_ids = set([root_id])
249
while len(id_set) > 0:
250
file_id = id_set.pop()
251
if file_id in seen_ids:
253
seen_ids.add(file_id)
254
ie = tree.inventory[file_id]
255
if ie.parent_id not in seen_ids:
256
id_set.add(ie.parent_id)
257
path = tree.inventory.id2path(file_id)
258
self.to_file.write('# %s\t%s\t%s\n'
259
% (path.encode('utf8'), file_id.encode('utf8'),
260
ie.parent_id.encode('utf8')))
261
write_ids(self.new_tree, new_ids, 'file')
262
write_ids(self.old_tree, old_ids, 'old file')
264
def _write_diffs(self):
265
"""Write out the specific diffs"""
266
from bzrlib.diff import internal_diff, external_diff
267
DEVNULL = '/dev/null'
269
if self.external_diff_options:
270
assert isinstance(self.external_diff_options, basestring)
271
opts = self.external_diff_options.split()
272
def diff_file(olab, olines, nlab, nlines, to_file):
273
external_diff(olab, olines, nlab, nlines, to_file, opts)
275
diff_file = internal_diff
277
for path, file_id, kind in self.delta.removed:
278
print >>self.to_file, '*** removed %s %r' % (kind, path)
279
if kind == 'file' and self.full_remove:
280
diff_file(self.old_label + path,
281
self.old_tree.get_file(file_id).readlines(),
286
for path, file_id, kind in self.delta.added:
287
print >>self.to_file, '*** added %s %r' % (kind, path)
291
self.new_label + path,
292
self.new_tree.get_file(file_id).readlines(),
295
for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
296
print >>self.to_file, '*** renamed %s %r => %r' % (kind, old_path, new_path)
297
if self.full_rename and kind == 'file':
298
diff_file(self.old_label + old_path,
299
self.old_tree.get_file(file_id).readlines(),
305
self.new_label + new_path,
306
self.new_tree.get_file(file_id).readlines(),
309
diff_file(self.old_label + old_path,
310
self.old_tree.get_file(file_id).readlines(),
311
self.new_label + new_path,
312
self.new_tree.get_file(file_id).readlines(),
315
for path, file_id, kind in self.delta.modified:
316
print >>self.to_file, '*** modified %s %r' % (kind, path)
318
diff_file(self.old_label + path,
319
self.old_tree.get_file(file_id).readlines(),
320
self.new_label + path,
321
self.new_tree.get_file(file_id).readlines(),
324
def show_changeset(branch, revision=None, specific_files=None,
325
external_diff_options=None, to_file=None,
326
include_full_diff=False):
327
from bzrlib.diff import compare_trees
332
revisions = _canonicalize_revision(branch, revision)
334
old_tree, new_tree = _get_trees(branch, revisions)
336
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
337
specific_files=specific_files)
339
meta = MetaInfoHeader(branch, revisions, delta,
340
external_diff_options=external_diff_options,
341
old_tree=old_tree, new_tree=new_tree)
342
meta.write_meta_info(to_file)