~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

Restore shlex_split_unicode()

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
 
1
# Copyright (C) 2005-2010 Canonical Ltd.
2
2
#
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
18
18
import os
19
19
import re
20
20
import shutil
 
21
import string
21
22
import sys
22
23
 
23
24
from bzrlib.lazy_import import lazy_import
30
31
from bzrlib import (
31
32
    branch as _mod_branch,
32
33
    bzrdir,
33
 
    commands,
 
34
    cmdline,
34
35
    errors,
35
36
    osutils,
36
37
    patiencediff,
38
39
    timestamp,
39
40
    views,
40
41
    )
 
42
 
 
43
from bzrlib.workingtree import WorkingTree
41
44
""")
42
45
 
43
46
from bzrlib.symbol_versioning import (
46
49
from bzrlib.trace import mutter, note, warning
47
50
 
48
51
 
 
52
class AtTemplate(string.Template):
 
53
    """Templating class that uses @ instead of $."""
 
54
 
 
55
    delimiter = '@'
 
56
 
 
57
 
49
58
# TODO: Rather than building a changeset object, we should probably
50
59
# invoke callbacks on an object.  That object can either accumulate a
51
60
# list, write them out directly, etc etc.
277
286
                        new_abspath, e)
278
287
 
279
288
 
280
 
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
281
 
    apply_view=True):
 
289
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
 
290
                                   apply_view=True):
282
291
    """Get the trees and specific files to diff given a list of paths.
283
292
 
284
293
    This method works out the trees to be diff'ed and the files of
299
308
        if True and a view is set, apply the view or check that the paths
300
309
        are within it
301
310
    :returns:
302
 
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
303
 
        extra_trees is a sequence of additional trees to search in for
304
 
        file-ids.
 
311
        a tuple of (old_tree, new_tree, old_branch, new_branch,
 
312
        specific_files, extra_trees) where extra_trees is a sequence of
 
313
        additional trees to search in for file-ids.
305
314
    """
306
315
    # Get the old and new revision specs
307
316
    old_revision_spec = None
341
350
            views.check_path_in_view(working_tree, relpath)
342
351
        specific_files.append(relpath)
343
352
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
 
353
    old_branch = branch
344
354
 
345
355
    # Get the new location
346
356
    if new_url is None:
354
364
            specific_files.append(relpath)
355
365
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
356
366
        basis_is_default=working_tree is None)
 
367
    new_branch = branch
357
368
 
358
369
    # Get the specific files (all files is None, no files is [])
359
370
    if make_paths_wt_relative and working_tree is not None:
378
389
    extra_trees = None
379
390
    if working_tree is not None and working_tree not in (old_tree, new_tree):
380
391
        extra_trees = (working_tree,)
381
 
    return old_tree, new_tree, specific_files, extra_trees
 
392
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
 
393
 
382
394
 
383
395
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
384
396
    if branch is None and tree is not None:
441
453
 
442
454
def _patch_header_date(tree, file_id, path):
443
455
    """Returns a timestamp suitable for use in a patch header."""
444
 
    mtime = tree.get_file_mtime(file_id, path)
 
456
    try:
 
457
        mtime = tree.get_file_mtime(file_id, path)
 
458
    except errors.FileTimestampUnavailable:
 
459
        mtime = 0
445
460
    return timestamp.format_patch_date(mtime)
446
461
 
447
462
 
668
683
    @classmethod
669
684
    def from_string(klass, command_string, old_tree, new_tree, to_file,
670
685
                    path_encoding='utf-8'):
671
 
        command_template = commands.shlex_split_unicode(command_string)
672
 
        command_template.extend(['%(old_path)s', '%(new_path)s'])
 
686
        command_template = cmdline.split(command_string)
 
687
        if '@' not in command_string:
 
688
            command_template.extend(['@old_path', '@new_path'])
673
689
        return klass(command_template, old_tree, new_tree, to_file,
674
690
                     path_encoding)
675
691
 
682
698
 
683
699
    def _get_command(self, old_path, new_path):
684
700
        my_map = {'old_path': old_path, 'new_path': new_path}
685
 
        return [t % my_map for t in self.command_template]
 
701
        return [AtTemplate(t).substitute(my_map) for t in
 
702
                self.command_template]
686
703
 
687
704
    def _execute(self, old_path, new_path):
688
705
        command = self._get_command(old_path, new_path)
708
725
                raise
709
726
        return True
710
727
 
711
 
    def _write_file(self, file_id, tree, prefix, relpath):
 
728
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
 
729
                    allow_write=False):
 
730
        if not force_temp and isinstance(tree, WorkingTree):
 
731
            return tree.abspath(tree.id2path(file_id))
 
732
        
712
733
        full_path = osutils.pathjoin(self._root, prefix, relpath)
713
 
        if self._try_symlink_root(tree, prefix):
 
734
        if not force_temp and self._try_symlink_root(tree, prefix):
714
735
            return full_path
715
736
        parent_dir = osutils.dirname(full_path)
716
737
        try:
727
748
                target.close()
728
749
        finally:
729
750
            source.close()
730
 
        osutils.make_readonly(full_path)
731
 
        mtime = tree.get_file_mtime(file_id)
 
751
        if not allow_write:
 
752
            osutils.make_readonly(full_path)
 
753
        try:
 
754
            mtime = tree.get_file_mtime(file_id)
 
755
        except errors.FileTimestampUnavailable:
 
756
            mtime = 0
732
757
        os.utime(full_path, (mtime, mtime))
733
758
        return full_path
734
759
 
735
 
    def _prepare_files(self, file_id, old_path, new_path):
 
760
    def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
 
761
                       allow_write_new=False):
736
762
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
737
 
                                         old_path)
 
763
                                         old_path, force_temp)
738
764
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
739
 
                                         new_path)
 
765
                                         new_path, force_temp,
 
766
                                         allow_write=allow_write_new)
740
767
        return old_disk_path, new_disk_path
741
768
 
742
769
    def finish(self):
750
777
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
751
778
        if (old_kind, new_kind) != ('file', 'file'):
752
779
            return DiffPath.CANNOT_DIFF
753
 
        self._prepare_files(file_id, old_path, new_path)
754
 
        self._execute(osutils.pathjoin('old', old_path),
755
 
                      osutils.pathjoin('new', new_path))
 
780
        (old_disk_path, new_disk_path) = self._prepare_files(
 
781
                                                file_id, old_path, new_path)
 
782
        self._execute(old_disk_path, new_disk_path)
 
783
 
 
784
    def edit_file(self, file_id):
 
785
        """Use this tool to edit a file.
 
786
 
 
787
        A temporary copy will be edited, and the new contents will be
 
788
        returned.
 
789
 
 
790
        :param file_id: The id of the file to edit.
 
791
        :return: The new contents of the file.
 
792
        """
 
793
        old_path = self.old_tree.id2path(file_id)
 
794
        new_path = self.new_tree.id2path(file_id)
 
795
        new_abs_path = self._prepare_files(file_id, old_path, new_path,
 
796
                                           allow_write_new=True,
 
797
                                           force_temp=True)[1]
 
798
        command = self._get_command(osutils.pathjoin('old', old_path),
 
799
                                    osutils.pathjoin('new', new_path))
 
800
        subprocess.call(command, cwd=self._root)
 
801
        new_file = open(new_abs_path, 'r')
 
802
        try:
 
803
            return new_file.read()
 
804
        finally:
 
805
            new_file.close()
756
806
 
757
807
 
758
808
class DiffTree(object):