14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib.trace import mutter
18
from bzrlib.errors import BzrError
17
19
from bzrlib.delta import compare_trees
18
from bzrlib.errors import BzrError
19
import bzrlib.errors as errors
20
from bzrlib.patiencediff import SequenceMatcher, unified_diff
21
from bzrlib.symbol_versioning import *
22
from bzrlib.textfile import check_text_lines
23
from bzrlib.trace import mutter
25
21
# TODO: Rather than building a changeset object, we should probably
26
22
# invoke callbacks on an object. That object can either accumulate a
27
23
# list, write them out directly, etc etc.
29
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
30
allow_binary=False, sequence_matcher=None):
25
def internal_diff(old_label, oldlines, new_label, newlines, to_file):
31
28
# FIXME: difflib is wrong if there is no trailing newline.
32
29
# The syntax used by patch seems to be "\ No newline at
33
30
# end of file" following the last diff line from that
43
40
# both sequences are empty.
44
41
if not oldlines and not newlines:
47
if allow_binary is False:
48
check_text_lines(oldlines)
49
check_text_lines(newlines)
51
if sequence_matcher is None:
52
sequence_matcher = SequenceMatcher
53
ud = unified_diff(oldlines, newlines,
54
fromfile=old_filename+'\t',
55
tofile=new_filename+'\t',
56
sequencematcher=sequence_matcher)
44
ud = difflib.unified_diff(oldlines, newlines,
45
fromfile=old_label, tofile=new_label)
59
48
# work-around for difflib being too smart for its own good
173
158
output = sys.stdout
175
160
if from_spec is None:
176
old_tree = b.bzrdir.open_workingtree()
178
old_tree = old_tree = old_tree.basis_tree()
161
old_tree = b.basis_tree()
180
old_tree = b.repository.revision_tree(from_spec.in_history(b).rev_id)
163
old_tree = b.revision_tree(from_spec.in_history(b).rev_id)
182
165
if revision2 is None:
184
new_tree = b.bzrdir.open_workingtree()
186
new_tree = b2.bzrdir.open_workingtree()
188
new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
190
return show_diff_trees(old_tree, new_tree, output, specific_files,
191
external_diff_options)
194
def diff_cmd_helper(tree, specific_files, external_diff_options,
195
old_revision_spec=None, new_revision_spec=None,
196
old_label='a/', new_label='b/'):
197
"""Helper for cmd_diff.
203
The specific files to compare, or None
205
external_diff_options
206
If non-None, run an external diff, and pass it these options
209
If None, use basis tree as old revision, otherwise use the tree for
210
the specified revision.
213
If None, use working tree as new revision, otherwise use the tree for
214
the specified revision.
216
The more general form is show_diff_trees(), where the caller
217
supplies any two trees.
222
revision_id = spec.in_store(tree.branch).rev_id
223
return tree.branch.repository.revision_tree(revision_id)
224
if old_revision_spec is None:
225
old_tree = tree.basis_tree()
227
old_tree = spec_tree(old_revision_spec)
229
if new_revision_spec is None:
232
new_tree = spec_tree(new_revision_spec)
234
return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
235
external_diff_options,
236
old_label=old_label, new_label=new_label)
166
new_tree = b.working_tree()
168
new_tree = b.revision_tree(revision2.in_history(b).rev_id)
170
show_diff_trees(old_tree, new_tree, output, specific_files,
171
external_diff_options)
239
175
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
240
external_diff_options=None,
241
old_label='a/', new_label='b/'):
176
external_diff_options=None):
242
177
"""Show in text form the changes from one tree to another.
247
182
external_diff_options
248
183
If set, use an external GNU diff and pass these options.
254
return _show_diff_trees(old_tree, new_tree, to_file,
255
specific_files, external_diff_options,
256
old_label=old_label, new_label=new_label)
263
def _show_diff_trees(old_tree, new_tree, to_file,
264
specific_files, external_diff_options,
265
old_label='a/', new_label='b/' ):
186
# TODO: Options to control putting on a prefix or suffix, perhaps as a format string
267
190
DEVNULL = '/dev/null'
268
191
# Windows users, don't panic about this filename -- it is a
283
204
diff_file = internal_diff
285
207
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
286
208
specific_files=specific_files)
289
210
for path, file_id, kind in delta.removed:
291
211
print >>to_file, '=== removed %s %r' % (kind, path)
292
212
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
293
213
DEVNULL, None, None, to_file)
294
214
for path, file_id, kind in delta.added:
296
215
print >>to_file, '=== added %s %r' % (kind, path)
297
216
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
298
217
DEVNULL, None, None, to_file,
300
219
for (old_path, new_path, file_id, kind,
301
220
text_modified, meta_modified) in delta.renamed:
303
221
prop_str = get_prop_change(meta_modified)
304
222
print >>to_file, '=== renamed %s %r => %r%s' % (
305
kind, old_path, new_path, prop_str)
223
kind, old_path, new_path, prop_str)
306
224
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
307
225
new_label, new_path, new_tree,
308
226
text_modified, kind, to_file, diff_file)
309
227
for path, file_id, kind, text_modified, meta_modified in delta.modified:
311
228
prop_str = get_prop_change(meta_modified)
312
229
print >>to_file, '=== modified %s %r%s' % (kind, path, prop_str)
313
230
if text_modified:
314
231
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
315
232
new_label, path, new_tree,
316
233
True, kind, to_file, diff_file)
321
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
322
"""Complain if paths are not versioned in either tree."""
323
if not specific_files:
325
old_unversioned = old_tree.filter_unversioned_files(specific_files)
326
new_unversioned = new_tree.filter_unversioned_files(specific_files)
327
unversioned = old_unversioned.intersection(new_unversioned)
329
raise errors.PathsNotVersionedError(sorted(unversioned))
332
def _raise_if_nonexistent(paths, old_tree, new_tree):
333
"""Complain if paths are not in either inventory or tree.
335
It's OK with the files exist in either tree's inventory, or
336
if they exist in the tree but are not versioned.
338
This can be used by operations such as bzr status that can accept
339
unknown or ignored files.
341
mutter("check paths: %r", paths)
344
s = old_tree.filter_unversioned_files(paths)
345
s = new_tree.filter_unversioned_files(s)
346
s = [path for path in s if not new_tree.has_filename(path)]
348
raise errors.PathsDoNotExist(sorted(s))
351
236
def get_prop_change(meta_modified):
352
237
if meta_modified: