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
from bzrlib.tree import EmptyTree
43
if revisions[0] is None:
44
if hasattr(branch, 'get_root_id'): # Watch out for trees with labeled ROOT ids
45
old_tree = EmptyTree(branch.get_root_id)
47
old_tree = EmptyTree()
49
old_tree = branch.revision_tree(revisions[0])
51
if revisions[1] is None:
52
# This is for the future, once we support rollup revisions
53
# Or working tree revisions
54
new_tree = branch.working_tree()
56
new_tree = branch.revision_tree(revisions[1])
57
return old_tree, new_tree
59
def _fake_working_revision(branch):
60
"""Fake a Revision object for the working tree.
62
This is for the future, to support changesets against the working tree.
64
from bzrlib.revision import Revision
66
from bzrlib.osutils import local_time_offset, \
69
precursor = branch.last_patch()
70
precursor_sha1 = branch.get_revision_sha1(precursor)
72
return Revision(timestamp=time.time(),
73
timezone=local_time_offset(),
76
precursor_sha1=precursor_sha1)
79
class MetaInfoHeader(object):
80
"""Maintain all of the header information about this
84
def __init__(self, branch, revisions, delta,
85
full_remove=True, full_rename=False,
86
external_diff_options = None,
87
new_tree=None, old_tree=None,
88
old_label = '', new_label = ''):
90
:param full_remove: Include the full-text for a delete
91
:param full_rename: Include an add+delete patch for a rename
95
self.full_remove=full_remove
96
self.full_rename=full_rename
97
self.external_diff_options = external_diff_options
98
self.old_label = old_label
99
self.new_label = new_label
100
self.old_tree = old_tree
101
self.new_tree = new_tree
104
self.precursor_revno = None
106
self._get_revision_list(revisions)
108
def _get_revision_list(self, revisions):
109
"""This generates the list of all revisions from->to.
111
This is for the future, when we support having a rollup changeset.
112
For now, the list should only be one long.
116
rh = self.branch.revision_history()
117
for revno, rev in enumerate(rh):
118
if rev == revisions[0]:
120
if rev == revisions[1]:
123
self.revision_list = []
124
if old_revno is None:
125
self.base_revision = None # Effectively the EmptyTree()
128
self.base_revision = self.branch.get_revision(rh[old_revno])
129
if new_revno is None:
130
# For the future, when we support working tree changesets.
131
for rev_id in rh[old_revno+1:]:
132
self.revision_list.append(self.branch.get_revision(rev_id))
133
self.revision_list.append(_fake_working_revision(self.branch))
135
for rev_id in rh[old_revno+1:new_revno+1]:
136
self.revision_list.append(self.branch.get_revision(rev_id))
137
self.precursor_revno = old_revno
138
self.revno = new_revno
140
def _write(self, txt, key=None):
142
self.to_file.write('# %s: %s\n' % (key, txt))
144
self.to_file.write('# %s\n' % (txt,))
146
def write_meta_info(self, to_file):
147
"""Write out the meta-info portion to the supplied file.
149
:param to_file: Write out the meta information to the supplied
152
self.to_file = to_file
158
def _write_header(self):
159
"""Write the stuff that comes before the patches."""
160
from bzrlib.osutils import username, format_date
163
for line in common.get_header():
166
# This grabs the current username, what we really want is the
167
# username from the actual patches.
168
#write(username(), key='committer')
169
assert len(self.revision_list) == 1
170
rev = self.revision_list[0]
171
write(rev.committer, key='committer')
172
write(format_date(rev.timestamp, offset=rev.timezone), key='date')
173
write(str(self.revno), key='revno')
175
self.to_file.write('# message:\n')
176
for line in rev.message.split('\n'):
177
self.to_file.write('# %s\n' % line)
178
write(rev.revision_id, key='revision')
180
if self.base_revision:
181
write(self.base_revision.revision_id, key='precursor')
182
write(str(self.precursor_revno), key='precursor revno')
186
self.to_file.write('\n')
188
def _write_footer(self):
189
"""Write the stuff that comes after the patches.
191
This is meant to be more meta-information, which people probably don't want
192
to read, but which is required for proper bzr operation.
196
write('BEGIN BZR FOOTER')
198
assert len(self.revision_list) == 1 # We only handle single revision entries
199
rev = self.revision_list[0]
200
write(self.branch.get_revision_sha1(rev.revision_id),
202
if self.base_revision:
203
rev_id = self.base_revision.revision_id
204
write(self.branch.get_revision_sha1(rev_id),
205
key='precursor sha1')
207
write('%.9f' % rev.timestamp, key='timestamp')
208
write(str(rev.timezone), key='timezone')
212
write('END BZR FOOTER')
214
def _write_revisions(self):
215
"""Not used. Used for writing multiple revisions."""
217
for rev in self.revision_list:
218
if rev.revision_id is not None:
220
self._write('revisions:')
222
self._write(' '*4 + rev.revision_id + '\t' + self.branch.get_revision_sha1(rev.revision_id))
225
def _write_ids(self):
226
if hasattr(self.branch, 'get_root_id'):
227
root_id = self.branch.get_root_id()
234
for path, file_id, kind in self.delta.removed:
236
for path, file_id, kind in self.delta.added:
238
for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
241
for path, file_id, kind in self.delta.modified:
244
self._write(root_id, key='tree root id')
246
def write_ids(tree, id_set, name):
248
self.to_file.write('# %s ids:\n' % name)
249
seen_ids = set([root_id])
250
while len(id_set) > 0:
251
file_id = id_set.pop()
252
if file_id in seen_ids:
254
seen_ids.add(file_id)
255
ie = tree.inventory[file_id]
256
if ie.parent_id not in seen_ids:
257
id_set.add(ie.parent_id)
258
path = tree.inventory.id2path(file_id)
259
self.to_file.write('# %s\t%s\t%s\n'
260
% (path.encode('utf8'), file_id.encode('utf8'),
261
ie.parent_id.encode('utf8')))
262
write_ids(self.new_tree, new_ids, 'file')
263
write_ids(self.old_tree, old_ids, 'old file')
265
def _write_diffs(self):
266
"""Write out the specific diffs"""
267
from bzrlib.diff import internal_diff, external_diff
268
DEVNULL = '/dev/null'
270
if self.external_diff_options:
271
assert isinstance(self.external_diff_options, basestring)
272
opts = self.external_diff_options.split()
273
def diff_file(olab, olines, nlab, nlines, to_file):
274
external_diff(olab, olines, nlab, nlines, to_file, opts)
276
diff_file = internal_diff
278
for path, file_id, kind in self.delta.removed:
279
print >>self.to_file, '*** removed %s %r' % (kind, path)
280
if kind == 'file' and self.full_remove:
281
diff_file(self.old_label + path,
282
self.old_tree.get_file(file_id).readlines(),
287
for path, file_id, kind in self.delta.added:
288
print >>self.to_file, '*** added %s %r' % (kind, path)
292
self.new_label + path,
293
self.new_tree.get_file(file_id).readlines(),
296
for old_path, new_path, file_id, kind, text_modified in self.delta.renamed:
297
print >>self.to_file, '*** renamed %s %r => %r' % (kind, old_path, new_path)
298
if self.full_rename and kind == 'file':
299
diff_file(self.old_label + old_path,
300
self.old_tree.get_file(file_id).readlines(),
306
self.new_label + new_path,
307
self.new_tree.get_file(file_id).readlines(),
310
diff_file(self.old_label + old_path,
311
self.old_tree.get_file(file_id).readlines(),
312
self.new_label + new_path,
313
self.new_tree.get_file(file_id).readlines(),
316
for path, file_id, kind in self.delta.modified:
317
print >>self.to_file, '*** modified %s %r' % (kind, path)
319
diff_file(self.old_label + path,
320
self.old_tree.get_file(file_id).readlines(),
321
self.new_label + path,
322
self.new_tree.get_file(file_id).readlines(),
325
def show_changeset(branch, revision=None, specific_files=None,
326
external_diff_options=None, to_file=None,
327
include_full_diff=False):
328
from bzrlib.diff import compare_trees
333
revisions = _canonicalize_revision(branch, revision)
335
old_tree, new_tree = _get_trees(branch, revisions)
337
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
338
specific_files=specific_files)
340
meta = MetaInfoHeader(branch, revisions, delta,
341
external_diff_options=external_diff_options,
342
old_tree=old_tree, new_tree=new_tree)
343
meta.write_meta_info(to_file)