1
1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
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
24
from bzrlib.delta import compare_trees
18
25
from bzrlib.errors import BzrError
19
26
import bzrlib.errors as errors
20
from bzrlib.symbol_versioning import *
21
from bzrlib.trace import mutter
28
from bzrlib.patiencediff import unified_diff
29
import bzrlib.patiencediff
30
from bzrlib.symbol_versioning import (deprecated_function,
32
from bzrlib.textfile import check_text_lines
33
from bzrlib.trace import mutter, warning
23
36
# TODO: Rather than building a changeset object, we should probably
24
37
# invoke callbacks on an object. That object can either accumulate a
25
38
# list, write them out directly, etc etc.
27
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file):
40
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
41
allow_binary=False, sequence_matcher=None,
42
path_encoding='utf8'):
30
43
# FIXME: difflib is wrong if there is no trailing newline.
31
44
# The syntax used by patch seems to be "\ No newline at
32
45
# end of file" following the last diff line from that
275
328
diff_file = internal_diff
277
330
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
278
specific_files=specific_files)
331
specific_files=specific_files,
332
extra_trees=extra_trees, require_versioned=True)
281
335
for path, file_id, kind in delta.removed:
283
print >>to_file, '=== removed %s %r' % (kind, old_label + path)
284
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
285
DEVNULL, None, None, to_file)
337
print >>to_file, '=== removed %s %r' % (kind, path.encode('utf8'))
338
old_name = '%s%s\t%s' % (old_label, path,
339
_patch_header_date(old_tree, file_id, path))
340
new_name = '%s%s\t%s' % (new_label, path, EPOCH_DATE)
341
old_tree.inventory[file_id].diff(diff_file, old_name, old_tree,
342
new_name, None, None, to_file)
286
343
for path, file_id, kind in delta.added:
288
print >>to_file, '=== added %s %r' % (kind, new_label + path)
289
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
290
DEVNULL, None, None, to_file,
345
print >>to_file, '=== added %s %r' % (kind, path.encode('utf8'))
346
old_name = '%s%s\t%s' % (old_label, path, EPOCH_DATE)
347
new_name = '%s%s\t%s' % (new_label, path,
348
_patch_header_date(new_tree, file_id, path))
349
new_tree.inventory[file_id].diff(diff_file, new_name, new_tree,
350
old_name, None, None, to_file,
292
352
for (old_path, new_path, file_id, kind,
293
353
text_modified, meta_modified) in delta.renamed:
295
355
prop_str = get_prop_change(meta_modified)
296
356
print >>to_file, '=== renamed %s %r => %r%s' % (
297
kind, old_label + old_path, new_label + new_path, prop_str)
298
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
299
new_label, new_path, new_tree,
357
kind, old_path.encode('utf8'),
358
new_path.encode('utf8'), prop_str)
359
old_name = '%s%s\t%s' % (old_label, old_path,
360
_patch_header_date(old_tree, file_id,
362
new_name = '%s%s\t%s' % (new_label, new_path,
363
_patch_header_date(new_tree, file_id,
365
_maybe_diff_file_or_symlink(old_name, old_tree, file_id,
300
367
text_modified, kind, to_file, diff_file)
301
368
for path, file_id, kind, text_modified, meta_modified in delta.modified:
303
370
prop_str = get_prop_change(meta_modified)
304
print >>to_file, '=== modified %s %r%s' % (kind, old_label + path,
371
print >>to_file, '=== modified %s %r%s' % (kind, path.encode('utf8'), prop_str)
372
old_name = '%s%s\t%s' % (old_label, path,
373
_patch_header_date(old_tree, file_id, path))
374
new_name = '%s%s\t%s' % (new_label, path,
375
_patch_header_date(new_tree, file_id, path))
306
376
if text_modified:
307
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
308
new_label, path, new_tree,
377
_maybe_diff_file_or_symlink(old_name, old_tree, file_id,
309
379
True, kind, to_file, diff_file)
311
381
return has_changes
314
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
315
"""Complain if paths are not versioned in either tree."""
316
if not specific_files:
384
def _patch_header_date(tree, file_id, path):
385
"""Returns a timestamp suitable for use in a patch header."""
386
tm = time.gmtime(tree.get_file_mtime(file_id, path))
387
return time.strftime('%Y-%m-%d %H:%M:%S +0000', tm)
390
def _raise_if_nonexistent(paths, old_tree, new_tree):
391
"""Complain if paths are not in either inventory or tree.
393
It's OK with the files exist in either tree's inventory, or
394
if they exist in the tree but are not versioned.
396
This can be used by operations such as bzr status that can accept
397
unknown or ignored files.
399
mutter("check paths: %r", paths)
318
old_unversioned = old_tree.filter_unversioned_files(specific_files)
319
new_unversioned = new_tree.filter_unversioned_files(specific_files)
320
unversioned = old_unversioned.intersection(new_unversioned)
322
raise errors.PathsNotVersionedError(sorted(unversioned))
402
s = old_tree.filter_unversioned_files(paths)
403
s = new_tree.filter_unversioned_files(s)
404
s = [path for path in s if not new_tree.has_filename(path)]
406
raise errors.PathsDoNotExist(sorted(s))
325
409
def get_prop_change(meta_modified):
326
410
if meta_modified: