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
25
# compatability - plugins import compare_trees from diff!!!
26
# deprecated as of 0.10
27
17
from bzrlib.delta import compare_trees
28
18
from bzrlib.errors import BzrError
29
19
import bzrlib.errors as errors
31
20
from bzrlib.patiencediff import unified_diff
32
21
import bzrlib.patiencediff
33
from bzrlib.symbol_versioning import (deprecated_function,
22
from bzrlib.symbol_versioning import *
35
23
from bzrlib.textfile import check_text_lines
36
from bzrlib.trace import mutter, warning
24
from bzrlib.trace import mutter
39
27
# TODO: Rather than building a changeset object, we should probably
148
140
diffcmd.extend(diff_opts)
151
pipe = subprocess.Popen(diffcmd,
152
stdin=subprocess.PIPE,
153
stdout=subprocess.PIPE)
155
if e.errno == errno.ENOENT:
156
raise errors.NoDiff(str(e))
160
first_line = pipe.stdout.readline()
161
to_file.write(first_line)
162
bzrlib.osutils.pumpfile(pipe.stdout, to_file)
142
rc = os.spawnvp(os.P_WAIT, 'diff', diffcmd)
166
# 'diff' gives retcode == 2 for all sorts of errors
167
# one of those is 'Binary files differ'.
168
# Bad options could also be the problem.
169
# 'Binary files' is not a real error, so we suppress that error
170
m = re.match('^binary files.*differ$', first_line, re.I)
172
raise BzrError('external diff failed with exit code 2;'
173
' command: %r' % (diffcmd,))
174
elif rc not in (0, 1):
144
if rc != 0 and rc != 1:
175
145
# returns 1 if files differ; that's OK
177
147
msg = 'signal %d' % (-rc)
179
149
msg = 'exit code %d' % rc
181
raise BzrError('external diff failed with %s; command: %r'
184
# internal_diff() adds a trailing newline, add one here for consistency
151
raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd))
188
153
oldtmpf.close() # and delete
190
# Clean up. Warn in case the files couldn't be deleted
191
# (in case windows still holds the file open, but not
192
# if the files have already been deleted)
194
os.remove(old_abspath)
196
if e.errno not in (errno.ENOENT,):
197
warning('Failed to delete temporary file: %s %s',
200
os.remove(new_abspath)
202
if e.errno not in (errno.ENOENT,):
203
warning('Failed to delete temporary file: %s %s',
207
157
@deprecated_function(zero_eight)
344
285
diff_file = internal_diff
346
delta = new_tree.changes_from(old_tree,
347
specific_files=specific_files,
348
extra_trees=extra_trees, require_versioned=True)
287
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
288
specific_files=specific_files)
351
291
for path, file_id, kind in delta.removed:
353
print >>to_file, '=== removed %s %r' % (kind, path.encode('utf8'))
354
old_name = '%s%s\t%s' % (old_label, path,
355
_patch_header_date(old_tree, file_id, path))
356
new_name = '%s%s\t%s' % (new_label, path, EPOCH_DATE)
357
old_tree.inventory[file_id].diff(diff_file, old_name, old_tree,
358
new_name, None, None, to_file)
293
print >>to_file, '=== removed %s %r' % (kind, path)
294
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
295
DEVNULL, None, None, to_file)
359
296
for path, file_id, kind in delta.added:
361
print >>to_file, '=== added %s %r' % (kind, path.encode('utf8'))
362
old_name = '%s%s\t%s' % (old_label, path, EPOCH_DATE)
363
new_name = '%s%s\t%s' % (new_label, path,
364
_patch_header_date(new_tree, file_id, path))
365
new_tree.inventory[file_id].diff(diff_file, new_name, new_tree,
366
old_name, None, None, to_file,
298
print >>to_file, '=== added %s %r' % (kind, path)
299
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
300
DEVNULL, None, None, to_file,
368
302
for (old_path, new_path, file_id, kind,
369
303
text_modified, meta_modified) in delta.renamed:
371
305
prop_str = get_prop_change(meta_modified)
372
306
print >>to_file, '=== renamed %s %r => %r%s' % (
373
kind, old_path.encode('utf8'),
374
new_path.encode('utf8'), prop_str)
375
old_name = '%s%s\t%s' % (old_label, old_path,
376
_patch_header_date(old_tree, file_id,
378
new_name = '%s%s\t%s' % (new_label, new_path,
379
_patch_header_date(new_tree, file_id,
381
_maybe_diff_file_or_symlink(old_name, old_tree, file_id,
307
kind, old_path, new_path, prop_str)
308
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
309
new_label, new_path, new_tree,
383
310
text_modified, kind, to_file, diff_file)
384
311
for path, file_id, kind, text_modified, meta_modified in delta.modified:
386
313
prop_str = get_prop_change(meta_modified)
387
print >>to_file, '=== modified %s %r%s' % (kind, path.encode('utf8'), prop_str)
388
old_name = '%s%s\t%s' % (old_label, path,
389
_patch_header_date(old_tree, file_id, path))
390
new_name = '%s%s\t%s' % (new_label, path,
391
_patch_header_date(new_tree, file_id, path))
314
print >>to_file, '=== modified %s %r%s' % (kind, path, prop_str)
392
315
if text_modified:
393
_maybe_diff_file_or_symlink(old_name, old_tree, file_id,
316
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
317
new_label, path, new_tree,
395
318
True, kind, to_file, diff_file)
397
320
return has_changes
400
def _patch_header_date(tree, file_id, path):
401
"""Returns a timestamp suitable for use in a patch header."""
402
tm = time.gmtime(tree.get_file_mtime(file_id, path))
403
return time.strftime('%Y-%m-%d %H:%M:%S +0000', tm)
323
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
324
"""Complain if paths are not versioned in either tree."""
325
if not specific_files:
327
old_unversioned = old_tree.filter_unversioned_files(specific_files)
328
new_unversioned = new_tree.filter_unversioned_files(specific_files)
329
unversioned = old_unversioned.intersection(new_unversioned)
331
raise errors.PathsNotVersionedError(sorted(unversioned))
406
334
def _raise_if_nonexistent(paths, old_tree, new_tree):
407
335
"""Complain if paths are not in either inventory or tree.