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.delta import compare_trees
18
from bzrlib.errors import BzrError
19
import bzrlib.errors as errors
20
from bzrlib.symbol_versioning import *
21
from bzrlib.textfile import check_text_lines
17
22
from bzrlib.trace import mutter
18
from bzrlib.errors import BzrError
19
from bzrlib.delta import compare_trees
21
24
# TODO: Rather than building a changeset object, we should probably
22
25
# invoke callbacks on an object. That object can either accumulate a
23
26
# list, write them out directly, etc etc.
25
def internal_diff(old_label, oldlines, new_label, newlines, to_file):
28
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
28
32
# FIXME: difflib is wrong if there is no trailing newline.
158
171
output = sys.stdout
160
173
if from_spec is None:
161
old_tree = b.basis_tree()
174
old_tree = b.bzrdir.open_workingtree()
176
old_tree = old_tree = old_tree.basis_tree()
163
old_tree = b.revision_tree(from_spec.in_history(b).rev_id)
178
old_tree = b.repository.revision_tree(from_spec.in_history(b).rev_id)
165
180
if revision2 is None:
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)
182
new_tree = b.bzrdir.open_workingtree()
184
new_tree = b2.bzrdir.open_workingtree()
186
new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
188
return show_diff_trees(old_tree, new_tree, output, specific_files,
189
external_diff_options)
192
def diff_cmd_helper(tree, specific_files, external_diff_options,
193
old_revision_spec=None, new_revision_spec=None,
194
old_label='a/', new_label='b/'):
195
"""Helper for cmd_diff.
201
The specific files to compare, or None
203
external_diff_options
204
If non-None, run an external diff, and pass it these options
207
If None, use basis tree as old revision, otherwise use the tree for
208
the specified revision.
211
If None, use working tree as new revision, otherwise use the tree for
212
the specified revision.
214
The more general form is show_diff_trees(), where the caller
215
supplies any two trees.
220
revision_id = spec.in_store(tree.branch).rev_id
221
return tree.branch.repository.revision_tree(revision_id)
222
if old_revision_spec is None:
223
old_tree = tree.basis_tree()
225
old_tree = spec_tree(old_revision_spec)
227
if new_revision_spec is None:
230
new_tree = spec_tree(new_revision_spec)
232
return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
233
external_diff_options,
234
old_label=old_label, new_label=new_label)
175
237
def show_diff_trees(old_tree, new_tree, to_file, specific_files=None,
176
external_diff_options=None):
238
external_diff_options=None,
239
old_label='a/', new_label='b/'):
177
240
"""Show in text form the changes from one tree to another.
182
245
external_diff_options
183
246
If set, use an external GNU diff and pass these options.
186
# TODO: Options to control putting on a prefix or suffix, perhaps as a format string
252
return _show_diff_trees(old_tree, new_tree, to_file,
253
specific_files, external_diff_options,
254
old_label=old_label, new_label=new_label)
261
def _show_diff_trees(old_tree, new_tree, to_file,
262
specific_files, external_diff_options,
263
old_label='a/', new_label='b/' ):
190
265
DEVNULL = '/dev/null'
191
266
# Windows users, don't panic about this filename -- it is a
204
281
diff_file = internal_diff
207
283
delta = compare_trees(old_tree, new_tree, want_unchanged=False,
208
284
specific_files=specific_files)
210
287
for path, file_id, kind in delta.removed:
211
289
print >>to_file, '=== removed %s %r' % (kind, path)
212
290
old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
213
291
DEVNULL, None, None, to_file)
214
292
for path, file_id, kind in delta.added:
215
294
print >>to_file, '=== added %s %r' % (kind, path)
216
295
new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
217
296
DEVNULL, None, None, to_file,
219
298
for (old_path, new_path, file_id, kind,
220
299
text_modified, meta_modified) in delta.renamed:
221
301
prop_str = get_prop_change(meta_modified)
222
302
print >>to_file, '=== renamed %s %r => %r%s' % (
223
kind, old_path, new_path, prop_str)
303
kind, old_path, new_path, prop_str)
224
304
_maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
225
305
new_label, new_path, new_tree,
226
306
text_modified, kind, to_file, diff_file)
227
307
for path, file_id, kind, text_modified, meta_modified in delta.modified:
228
309
prop_str = get_prop_change(meta_modified)
229
310
print >>to_file, '=== modified %s %r%s' % (kind, path, prop_str)
230
311
if text_modified:
231
312
_maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
232
313
new_label, path, new_tree,
233
314
True, kind, to_file, diff_file)
319
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
320
"""Complain if paths are not versioned in either tree."""
321
if not specific_files:
323
old_unversioned = old_tree.filter_unversioned_files(specific_files)
324
new_unversioned = new_tree.filter_unversioned_files(specific_files)
325
unversioned = old_unversioned.intersection(new_unversioned)
327
raise errors.PathsNotVersionedError(sorted(unversioned))
330
def _raise_if_nonexistent(paths, old_tree, new_tree):
331
"""Complain if paths are not in either inventory or tree.
333
It's OK with the files exist in either tree's inventory, or
334
if they exist in the tree but are not versioned.
336
This can be used by operations such as bzr status that can accept
337
unknown or ignored files.
339
mutter("check paths: %r", paths)
342
s = old_tree.filter_unversioned_files(paths)
343
s = new_tree.filter_unversioned_files(s)
344
s = [path for path in s if not new_tree.has_filename(path)]
346
raise errors.PathsDoNotExist(sorted(s))
236
349
def get_prop_change(meta_modified):
237
350
if meta_modified: