87
73
# In the meantime we at least make sure the patch isn't
77
# Special workaround for Python2.3, where difflib fails if
78
# both sequences are empty.
79
if not oldlines and not newlines:
90
82
if allow_binary is False:
91
83
textfile.check_text_lines(oldlines)
92
84
textfile.check_text_lines(newlines)
94
86
if sequence_matcher is None:
95
87
sequence_matcher = patiencediff.PatienceSequenceMatcher
96
ud = patiencediff.unified_diff_bytes(oldlines, newlines,
97
fromfile=old_label.encode(path_encoding, 'replace'),
98
tofile=new_label.encode(path_encoding, 'replace'),
99
n=context_lines, sequencematcher=sequence_matcher)
88
ud = patiencediff.unified_diff(oldlines, newlines,
89
fromfile=old_filename.encode(path_encoding),
90
tofile=new_filename.encode(path_encoding),
91
sequencematcher=sequence_matcher)
102
94
if len(ud) == 0: # Identical contents, nothing to do
104
96
# work-around for difflib being too smart for its own good
105
97
# if /dev/null is "1,0", patch won't recognize it as /dev/null
107
ud[2] = ud[2].replace(b'-1,0', b'-0,0')
99
ud[2] = ud[2].replace('-1,0', '-0,0')
108
100
elif not newlines:
109
ud[2] = ud[2].replace(b'+1,0', b'+0,0')
101
ud[2] = ud[2].replace('+1,0', '+0,0')
112
104
to_file.write(line)
113
if not line.endswith(b'\n'):
114
to_file.write(b"\n\\ No newline at end of file\n")
105
if not line.endswith('\n'):
106
to_file.write("\n\\ No newline at end of file\n")
118
110
def _spawn_external_diff(diffcmd, capture_errors=True):
119
"""Spawn the external diff process, and return the child handle.
111
"""Spawn the externall diff process, and return the child handle.
121
113
:param diffcmd: The command list to spawn
122
114
:param capture_errors: Capture stderr as well as setting LANG=C
144
136
stdout=subprocess.PIPE,
148
140
if e.errno == errno.ENOENT:
149
141
raise errors.NoDiff(str(e))
154
# diff style options as of GNU diff v3.2
155
style_option_list = ['-c', '-C', '--context',
157
'-f', '--forward-ed',
161
'-u', '-U', '--unified',
162
'-y', '--side-by-side',
165
def default_style_unified(diff_opts):
166
"""Default to unified diff style if alternative not specified in diff_opts.
168
diff only allows one style to be specified; they don't override.
169
Note that some of these take optargs, and the optargs can be
170
directly appended to the options.
171
This is only an approximate parser; it doesn't properly understand
174
:param diff_opts: List of options for external (GNU) diff.
175
:return: List of options with default style=='unified'.
177
for s in style_option_list:
185
diff_opts.append('-u')
189
def external_diff(old_label, oldlines, new_label, newlines, to_file,
147
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
191
149
"""Display a diff by calling out to the external diff program."""
192
150
# make sure our own output is properly ordered before the diff
195
oldtmp_fd, old_abspath = tempfile.mkstemp(prefix='brz-diff-old-')
196
newtmp_fd, new_abspath = tempfile.mkstemp(prefix='brz-diff-new-')
153
oldtmp_fd, old_abspath = tempfile.mkstemp(prefix='bzr-diff-old-')
154
newtmp_fd, new_abspath = tempfile.mkstemp(prefix='bzr-diff-new-')
197
155
oldtmpf = os.fdopen(oldtmp_fd, 'wb')
198
156
newtmpf = os.fdopen(newtmp_fd, 'wb')
214
172
if not diff_opts:
216
if sys.platform == 'win32':
217
# Popen doesn't do the proper encoding for external commands
218
# Since we are dealing with an ANSI api, use mbcs encoding
219
old_label = old_label.encode('mbcs')
220
new_label = new_label.encode('mbcs')
221
174
diffcmd = ['diff',
222
'--label', old_label,
175
'--label', old_filename,
224
'--label', new_label,
177
'--label', new_filename,
229
diff_opts = default_style_unified(diff_opts)
182
# diff only allows one style to be specified; they don't override.
183
# note that some of these take optargs, and the optargs can be
184
# directly appended to the options.
185
# this is only an approximate parser; it doesn't properly understand
187
for s in ['-c', '-u', '-C', '-U',
192
'-y', '--side-by-side',
232
204
diffcmd.extend(diff_opts)
234
206
pipe = _spawn_external_diff(diffcmd, capture_errors=True)
235
out, err = pipe.communicate()
207
out,err = pipe.communicate()
236
208
rc = pipe.returncode
238
210
# internal_diff() adds a trailing newline, add one here for consistency
241
213
# 'diff' gives retcode == 2 for all sorts of errors
242
214
# one of those is 'Binary files differ'.
249
221
out, err = pipe.communicate()
251
223
# Write out the new i18n diff response
252
to_file.write(out+b'\n')
224
to_file.write(out+'\n')
253
225
if pipe.returncode != 2:
254
226
raise errors.BzrError(
255
227
'external diff failed with exit code 2'
256
228
' when run with LANG=C and LC_ALL=C,'
257
229
' but not when run natively: %r' % (diffcmd,))
259
first_line = lang_c_out.split(b'\n', 1)[0]
231
first_line = lang_c_out.split('\n', 1)[0]
260
232
# Starting with diffutils 2.8.4 the word "binary" was dropped.
261
m = re.match(b'^(binary )?files.*differ$', first_line, re.I)
233
m = re.match('^(binary )?files.*differ$', first_line, re.I)
263
235
raise errors.BzrError('external diff failed with exit code 2;'
264
236
' command: %r' % (diffcmd,))
277
249
msg = 'exit code %d' % rc
279
251
raise errors.BzrError('external diff failed with %s; command: %r'
284
256
oldtmpf.close() # and delete
288
# Warn in case the file couldn't be deleted (in case windows still
289
# holds the file open, but not if the files have already been
294
if e.errno not in (errno.ENOENT,):
295
warning('Failed to delete temporary file: %s %s', path, e)
301
def get_trees_and_branches_to_diff_locked(
302
path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
258
# Clean up. Warn in case the files couldn't be deleted
259
# (in case windows still holds the file open, but not
260
# if the files have already been deleted)
262
os.remove(old_abspath)
264
if e.errno not in (errno.ENOENT,):
265
warning('Failed to delete temporary file: %s %s',
268
os.remove(new_abspath)
270
if e.errno not in (errno.ENOENT,):
271
warning('Failed to delete temporary file: %s %s',
275
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
303
277
"""Get the trees and specific files to diff given a list of paths.
305
279
This method works out the trees to be diff'ed and the files of
317
291
The url of the new branch or tree. If None, the tree to use is
318
292
taken from the first path, if any, or the current working tree.
320
a callable like Command.add_cleanup. get_trees_and_branches_to_diff
321
will register cleanups that must be run to unlock the trees, etc.
322
293
:param apply_view:
323
294
if True and a view is set, apply the view or check that the paths
326
a tuple of (old_tree, new_tree, old_branch, new_branch,
327
specific_files, extra_trees) where extra_trees is a sequence of
328
additional trees to search in for file-ids. The trees and branches
329
will be read-locked until the cleanups registered via the add_cleanup
297
a tuple of (old_tree, new_tree, specific_files, extra_trees) where
298
extra_trees is a sequence of additional trees to search in for
332
301
# Get the old and new revision specs
333
302
old_revision_spec = None
356
325
default_location = path_list[0]
357
326
other_paths = path_list[1:]
359
def lock_tree_or_branch(wt, br):
362
add_cleanup(wt.unlock)
365
add_cleanup(br.unlock)
367
328
# Get the old location
368
329
specific_files = []
369
330
if old_url is None:
370
331
old_url = default_location
371
332
working_tree, branch, relpath = \
372
controldir.ControlDir.open_containing_tree_or_branch(old_url)
373
lock_tree_or_branch(working_tree, branch)
333
bzrdir.BzrDir.open_containing_tree_or_branch(old_url)
374
334
if consider_relpath and relpath != '':
375
335
if working_tree is not None and apply_view:
376
336
views.check_path_in_view(working_tree, relpath)
377
337
specific_files.append(relpath)
378
338
old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
381
340
# Get the new location
382
341
if new_url is None:
383
342
new_url = default_location
384
343
if new_url != old_url:
385
344
working_tree, branch, relpath = \
386
controldir.ControlDir.open_containing_tree_or_branch(new_url)
387
lock_tree_or_branch(working_tree, branch)
345
bzrdir.BzrDir.open_containing_tree_or_branch(new_url)
388
346
if consider_relpath and relpath != '':
389
347
if working_tree is not None and apply_view:
390
348
views.check_path_in_view(working_tree, relpath)
391
349
specific_files.append(relpath)
392
350
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
393
351
basis_is_default=working_tree is None)
396
353
# Get the specific files (all files is None, no files is [])
397
354
if make_paths_wt_relative and working_tree is not None:
398
other_paths = working_tree.safe_relpath_files(
356
from bzrlib.builtins import safe_relpath_files
357
other_paths = safe_relpath_files(working_tree, other_paths,
400
358
apply_view=apply_view)
359
except errors.FileInWrongBranch:
360
raise errors.BzrCommandError("Files are in different branches")
401
361
specific_files.extend(other_paths)
402
362
if len(specific_files) == 0:
403
363
specific_files = None
408
368
specific_files = view_files
409
369
view_str = views.view_display_str(view_files)
410
note(gettext("*** Ignoring files outside view. View is %s") % view_str)
370
note("*** Ignoring files outside view. View is %s" % view_str)
412
372
# Get extra trees that ought to be searched for file-ids
413
373
extra_trees = None
414
374
if working_tree is not None and working_tree not in (old_tree, new_tree):
415
375
extra_trees = (working_tree,)
416
return (old_tree, new_tree, old_branch, new_branch,
417
specific_files, extra_trees)
376
return old_tree, new_tree, specific_files, extra_trees
420
378
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
421
379
if branch is None and tree is not None:
436
394
old_label='a/', new_label='b/',
437
395
extra_trees=None,
438
396
path_encoding='utf8',
441
context=DEFAULT_CONTEXT_AMOUNT):
442
398
"""Show in text form the changes from one tree to another.
444
:param to_file: The output stream.
445
:param specific_files: Include only changes to these files - None for all
447
:param external_diff_options: If set, use an external GNU diff and pass
449
:param extra_trees: If set, more Trees to use for looking up file ids
450
:param path_encoding: If set, the path will be encoded as specified,
451
otherwise is supposed to be utf8
452
:param format_cls: Formatter class (DiffTree subclass)
404
Include only changes to these files - None for all changes.
406
external_diff_options
407
If set, use an external GNU diff and pass these options.
410
If set, more Trees to use for looking up file ids
413
If set, the path will be encoded as specified, otherwise is supposed
455
context = DEFAULT_CONTEXT_AMOUNT
456
if format_cls is None:
457
format_cls = DiffTree
458
with old_tree.lock_read():
459
418
if extra_trees is not None:
460
419
for tree in extra_trees:
462
421
new_tree.lock_read()
464
differ = format_cls.from_trees_options(old_tree, new_tree, to_file,
466
external_diff_options,
467
old_label, new_label, using,
468
context_lines=context)
423
differ = DiffTree.from_trees_options(old_tree, new_tree, to_file,
425
external_diff_options,
426
old_label, new_label, using)
469
427
return differ.show_diff(specific_files, extra_trees)
471
429
new_tree.unlock()
472
430
if extra_trees is not None:
473
431
for tree in extra_trees:
477
437
def _patch_header_date(tree, file_id, path):
478
438
"""Returns a timestamp suitable for use in a patch header."""
480
mtime = tree.get_file_mtime(path, file_id)
481
except FileTimestampUnavailable:
439
mtime = tree.get_file_mtime(file_id, path)
483
440
return timestamp.format_patch_date(mtime)
486
443
def get_executable_change(old_is_x, new_is_x):
487
descr = { True:b"+x", False:b"-x", None:b"??" }
444
descr = { True:"+x", False:"-x", None:"??" }
488
445
if old_is_x != new_is_x:
489
return [b"%s to %s" % (descr[old_is_x], descr[new_is_x],)]
446
return ["%s to %s" % (descr[old_is_x], descr[new_is_x],)]
613
570
def diff_symlink(self, old_target, new_target):
614
571
if old_target is None:
615
self.to_file.write(b'=== target is \'%s\'\n' %
616
new_target.encode(self.path_encoding, 'replace'))
572
self.to_file.write('=== target is %r\n' % new_target)
617
573
elif new_target is None:
618
self.to_file.write(b'=== target was \'%s\'\n' %
619
old_target.encode(self.path_encoding, 'replace'))
574
self.to_file.write('=== target was %r\n' % old_target)
621
self.to_file.write(b'=== target changed \'%s\' => \'%s\'\n' %
622
(old_target.encode(self.path_encoding, 'replace'),
623
new_target.encode(self.path_encoding, 'replace')))
576
self.to_file.write('=== target changed %r => %r\n' %
577
(old_target, new_target))
624
578
return self.CHANGED
630
584
# or removed in a diff.
631
585
EPOCH_DATE = '1970-01-01 00:00:00 +0000'
633
def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
634
old_label='', new_label='', text_differ=internal_diff,
635
context_lines=DEFAULT_CONTEXT_AMOUNT):
587
def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
588
old_label='', new_label='', text_differ=internal_diff):
636
589
DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
637
590
self.text_differ = text_differ
638
591
self.old_label = old_label
639
592
self.new_label = new_label
640
593
self.path_encoding = path_encoding
641
self.context_lines = context_lines
643
595
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
644
596
"""Compare two files in unified diff format
666
618
to_file_id = None
668
620
return self.CANNOT_DIFF
669
from_label = '%s%s\t%s' % (self.old_label, old_path,
671
to_label = '%s%s\t%s' % (self.new_label, new_path,
673
return self.diff_text(old_path, new_path, from_label, to_label,
674
from_file_id, to_file_id)
621
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
622
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
623
return self.diff_text(from_file_id, to_file_id, from_label, to_label,
676
def diff_text(self, from_path, to_path, from_label, to_label,
677
from_file_id=None, to_file_id=None):
626
def diff_text(self, from_file_id, to_file_id, from_label, to_label,
627
from_path=None, to_path=None):
678
628
"""Diff the content of given files in two trees
680
:param from_path: The path in the from tree. If None,
630
:param from_file_id: The id of the file in the from tree. If None,
681
631
the file is not present in the from tree.
682
:param to_path: The path in the to tree. This may refer
683
to a different file from from_path. If None,
632
:param to_file_id: The id of the file in the to tree. This may refer
633
to a different file from from_file_id. If None,
684
634
the file is not present in the to tree.
685
:param from_file_id: The id of the file in the from tree or None if
687
:param to_file_id: The id of the file in the to tree or None if
635
:param from_path: The path in the from tree or None if unknown.
636
:param to_path: The path in the to tree or None if unknown.
690
638
def _get_text(tree, file_id, path):
639
if file_id is not None:
640
return tree.get_file(file_id, path).readlines()
693
return tree.get_file_lines(path, file_id)
695
644
from_text = _get_text(self.old_tree, from_file_id, from_path)
696
645
to_text = _get_text(self.new_tree, to_file_id, to_path)
697
646
self.text_differ(from_label, from_text, to_label, to_text,
698
self.to_file, path_encoding=self.path_encoding,
699
context_lines=self.context_lines)
700
648
except errors.BinaryFile:
701
649
self.to_file.write(
702
650
("Binary files %s and %s differ\n" %
703
(from_label, to_label)).encode(self.path_encoding, 'replace'))
651
(from_label, to_label)).encode(self.path_encoding))
704
652
return self.CHANGED
710
658
path_encoding='utf-8'):
711
659
DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
712
660
self.command_template = command_template
713
self._root = osutils.mkdtemp(prefix='brz-diff-')
661
self._root = osutils.mkdtemp(prefix='bzr-diff-')
716
664
def from_string(klass, command_string, old_tree, new_tree, to_file,
717
665
path_encoding='utf-8'):
718
command_template = cmdline.split(command_string)
719
if '@' not in command_string:
720
command_template.extend(['@old_path', '@new_path'])
666
command_template = commands.shlex_split_unicode(command_string)
667
command_template.extend(['%(old_path)s', '%(new_path)s'])
721
668
return klass(command_template, old_tree, new_tree, to_file,
725
def make_from_diff_tree(klass, command_string, external_diff_options=None):
672
def make_from_diff_tree(klass, command_string):
726
673
def from_diff_tree(diff_tree):
727
full_command_string = [command_string]
728
if external_diff_options is not None:
729
full_command_string += ' ' + external_diff_options
730
return klass.from_string(full_command_string, diff_tree.old_tree,
674
return klass.from_string(command_string, diff_tree.old_tree,
731
675
diff_tree.new_tree, diff_tree.to_file)
732
676
return from_diff_tree
734
678
def _get_command(self, old_path, new_path):
735
679
my_map = {'old_path': old_path, 'new_path': new_path}
736
command = [AtTemplate(t).substitute(my_map) for t in
737
self.command_template]
738
if sys.platform == 'win32': # Popen doesn't accept unicode on win32
741
if isinstance(c, text_type):
742
command_encoded.append(c.encode('mbcs'))
744
command_encoded.append(c)
745
return command_encoded
680
return [t % my_map for t in self.command_template]
749
682
def _execute(self, old_path, new_path):
750
683
command = self._get_command(old_path, new_path)
752
685
proc = subprocess.Popen(command, stdout=subprocess.PIPE,
755
688
if e.errno == errno.ENOENT:
756
689
raise errors.ExecutableMissing(command[0])
759
692
self.to_file.write(proc.stdout.read())
761
693
return proc.wait()
763
695
def _try_symlink_root(self, tree, prefix):
768
700
os.symlink(tree.abspath(''), osutils.pathjoin(self._root, prefix))
770
702
if e.errno != errno.EEXIST:
776
"""Returns safe encoding for passing file path to diff tool"""
777
if sys.platform == 'win32':
780
# Don't fallback to 'utf-8' because subprocess may not be able to
781
# handle utf-8 correctly when locale is not utf-8.
782
return sys.getfilesystemencoding() or 'ascii'
784
def _is_safepath(self, path):
785
"""Return true if `path` may be able to pass to subprocess."""
788
return path == path.encode(fenc).decode(fenc)
792
def _safe_filename(self, prefix, relpath):
793
"""Replace unsafe character in `relpath` then join `self._root`,
794
`prefix` and `relpath`."""
796
# encoded_str.replace('?', '_') may break multibyte char.
797
# So we should encode, decode, then replace(u'?', u'_')
798
relpath_tmp = relpath.encode(fenc, 'replace').decode(fenc, 'replace')
799
relpath_tmp = relpath_tmp.replace(u'?', u'_')
800
return osutils.pathjoin(self._root, prefix, relpath_tmp)
802
def _write_file(self, relpath, tree, prefix, force_temp=False,
803
allow_write=False, file_id=None):
804
if not force_temp and isinstance(tree, WorkingTree):
805
full_path = tree.abspath(relpath)
806
if self._is_safepath(full_path):
809
full_path = self._safe_filename(prefix, relpath)
810
if not force_temp and self._try_symlink_root(tree, prefix):
706
def _write_file(self, file_id, tree, prefix, relpath):
707
full_path = osutils.pathjoin(self._root, prefix, relpath)
708
if self._try_symlink_root(tree, prefix):
812
710
parent_dir = osutils.dirname(full_path)
814
712
os.makedirs(parent_dir)
816
714
if e.errno != errno.EEXIST:
818
source = tree.get_file(relpath, file_id)
716
source = tree.get_file(file_id, relpath)
820
with open(full_path, 'wb') as target:
718
target = open(full_path, 'wb')
821
720
osutils.pumpfile(source, target)
825
mtime = tree.get_file_mtime(relpath, file_id)
826
except FileTimestampUnavailable:
829
os.utime(full_path, (mtime, mtime))
831
osutils.make_readonly(full_path)
725
osutils.make_readonly(full_path)
726
mtime = tree.get_file_mtime(file_id)
727
os.utime(full_path, (mtime, mtime))
834
def _prepare_files(self, old_path, new_path, force_temp=False,
835
allow_write_new=False, file_id=None):
836
old_disk_path = self._write_file(old_path, self.old_tree, 'old',
837
force_temp, file_id=file_id)
838
new_disk_path = self._write_file(new_path, self.new_tree, 'new',
839
force_temp, file_id=file_id,
840
allow_write=allow_write_new)
730
def _prepare_files(self, file_id, old_path, new_path):
731
old_disk_path = self._write_file(file_id, self.old_tree, 'old',
733
new_disk_path = self._write_file(file_id, self.new_tree, 'new',
841
735
return old_disk_path, new_disk_path
843
737
def finish(self):
845
739
osutils.rmtree(self._root)
847
741
if e.errno != errno.ENOENT:
848
742
mutter("The temporary directory \"%s\" was not "
849
743
"cleanly removed: %s." % (self._root, e))
851
745
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
852
746
if (old_kind, new_kind) != ('file', 'file'):
853
747
return DiffPath.CANNOT_DIFF
854
(old_disk_path, new_disk_path) = self._prepare_files(
855
old_path, new_path, file_id=file_id)
856
self._execute(old_disk_path, new_disk_path)
858
def edit_file(self, old_path, new_path, file_id=None):
859
"""Use this tool to edit a file.
861
A temporary copy will be edited, and the new contents will be
864
:param file_id: The id of the file to edit.
865
:return: The new contents of the file.
867
old_abs_path, new_abs_path = self._prepare_files(
868
old_path, new_path, allow_write_new=True, force_temp=True,
870
command = self._get_command(old_abs_path, new_abs_path)
871
subprocess.call(command, cwd=self._root)
872
with open(new_abs_path, 'rb') as new_file:
873
return new_file.read()
748
self._prepare_files(file_id, old_path, new_path)
749
self._execute(osutils.pathjoin('old', old_path),
750
osutils.pathjoin('new', new_path))
876
753
class DiffTree(object):
934
810
:param using: Commandline to use to invoke an external diff tool
936
812
if using is not None:
937
extra_factories = [DiffFromTool.make_from_diff_tree(using, external_diff_options)]
813
extra_factories = [DiffFromTool.make_from_diff_tree(using)]
939
815
extra_factories = []
940
816
if external_diff_options:
941
817
opts = external_diff_options.split()
942
def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None, context_lines=None):
943
""":param path_encoding: not used but required
944
to match the signature of internal_diff.
818
def diff_file(olab, olines, nlab, nlines, to_file):
946
819
external_diff(olab, olines, nlab, nlines, to_file, opts)
948
821
diff_file = internal_diff
949
822
diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
950
old_label, new_label, diff_file, context_lines=context_lines)
823
old_label, new_label, diff_file)
951
824
return klass(old_tree, new_tree, to_file, path_encoding, diff_text,
954
827
def show_diff(self, specific_files, extra_trees=None):
955
828
"""Write tree diff to self.to_file
957
:param specific_files: the specific files to compare (recursive)
830
:param sepecific_files: the specific files to compare (recursive)
958
831
:param extra_trees: extra trees to use for mapping paths to file_ids
997
870
properties_changed.extend(get_executable_change(executable[0], executable[1]))
999
872
if properties_changed:
1000
prop_str = b" (properties changed: %s)" % (
1001
b", ".join(properties_changed),)
873
prop_str = " (properties changed: %s)" % (", ".join(properties_changed),)
1005
877
if (old_present, new_present) == (True, False):
1006
self.to_file.write(b"=== removed %s '%s'\n" %
1007
(kind[0].encode('ascii'), oldpath_encoded))
878
self.to_file.write("=== removed %s '%s'\n" %
879
(kind[0], oldpath_encoded))
1008
880
newpath = oldpath
1009
881
elif (old_present, new_present) == (False, True):
1010
self.to_file.write(b"=== added %s '%s'\n" %
1011
(kind[1].encode('ascii'), newpath_encoded))
882
self.to_file.write("=== added %s '%s'\n" %
883
(kind[1], newpath_encoded))
1012
884
oldpath = newpath
1014
self.to_file.write(b"=== renamed %s '%s' => '%s'%s\n" %
1015
(kind[0].encode('ascii'), oldpath_encoded, newpath_encoded, prop_str))
886
self.to_file.write("=== renamed %s '%s' => '%s'%s\n" %
887
(kind[0], oldpath_encoded, newpath_encoded, prop_str))
1017
889
# if it was produced by iter_changes, it must be
1018
890
# modified *somehow*, either content or execute bit.
1019
self.to_file.write(b"=== modified %s '%s'%s\n" % (kind[0].encode('ascii'),
891
self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
1020
892
newpath_encoded, prop_str))
1021
893
if changed_content:
1022
self._diff(oldpath, newpath, kind[0], kind[1], file_id=file_id)
894
self._diff(file_id, oldpath, newpath, kind[0], kind[1])
1032
904
:param old_path: The path of the file in the old tree
1033
905
:param new_path: The path of the file in the new tree
1035
if old_path is None:
908
old_kind = self.old_tree.kind(file_id)
909
except (errors.NoSuchId, errors.NoSuchFile):
1038
old_kind = self.old_tree.kind(old_path, file_id)
1039
if new_path is None:
912
new_kind = self.new_tree.kind(file_id)
913
except (errors.NoSuchId, errors.NoSuchFile):
1042
new_kind = self.new_tree.kind(new_path, file_id)
1043
self._diff(old_path, new_path, old_kind, new_kind, file_id=file_id)
1045
def _diff(self, old_path, new_path, old_kind, new_kind, file_id):
915
self._diff(file_id, old_path, new_path, old_kind, new_kind)
918
def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
1046
919
result = DiffPath._diff_many(self.differs, file_id, old_path,
1047
new_path, old_kind, new_kind)
920
new_path, old_kind, new_kind)
1048
921
if result is DiffPath.CANNOT_DIFF:
1049
922
error_path = new_path
1050
923
if error_path is None:
1051
924
error_path = old_path
1052
925
raise errors.NoDiffFound(error_path)
1055
format_registry = Registry()
1056
format_registry.register('default', DiffTree)