~abentley/bzrtools/bzrtools.dev

702 by Aaron Bentley
Get conflict-diff under test.
1
# Copyright (C) 2009 Aaron Bentley <aaron@aaronbentley.com>
2
#
3
#    This program is free software; you can redistribute it and/or modify
4
#    it under the terms of the GNU General Public License as published by
5
#    the Free Software Foundation; either version 2 of the License, or
6
#    (at your option) any later version.
7
#
8
#    This program is distributed in the hope that it will be useful,
9
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
10
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
#    GNU General Public License for more details.
12
#
13
#    You should have received a copy of the GNU General Public License
14
#    along with this program; if not, write to the Free Software
15
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
import errno
19
733 by Aaron Bentley
Conflict differ works with no file specified or many files specified.
20
from bzrlib.conflicts import TextConflict
702 by Aaron Bentley
Get conflict-diff under test.
21
from bzrlib.diff import internal_diff
22
from bzrlib.workingtree import WorkingTree
23
from bzrlib.plugins.bzrtools import errors
24
25
733 by Aaron Bentley
Conflict differ works with no file specified or many files specified.
26
class ConflictDiffer(object):
27
28
    def __init__(self):
29
        self._old_tree = None
30
        self._tree = None
31
32
    def run(self, output, files=None, direction='other'):
33
        if files is None:
34
            self._tree = WorkingTree.open_containing('.')[0]
35
            files = [self._tree.abspath(c.path) for c in self._tree.conflicts()
36
                     if isinstance(c, TextConflict)]
37
        for filename in files:
38
            self.conflict_diff(output, filename, direction)
39
40
    def conflict_diff(self, output, filename, direction):
41
        """Perform a diff for a file with conflicts."""
42
        old_path = filename + '.BASE'
43
        old_lines = self.get_old_lines(filename, old_path)
44
        new_path_extension = {
45
            'other': '.OTHER',
46
            'this': '.THIS'}[direction]
47
        new_path = filename + new_path_extension
48
        newlines = open(new_path).readlines()
49
        internal_diff(old_path, old_lines, new_path, newlines, output)
50
51
    def get_old_tree(self, tree, base_path):
52
        if self._old_tree is not None:
53
            return self._old_tree
54
        graph = tree.branch.repository.get_graph()
55
        parent_ids = tree.get_parent_ids()
56
        if len(parent_ids) < 2:
57
            raise errors.NoConflictFiles(base_path)
58
        lca = graph.find_unique_lca(*parent_ids)
59
        return tree.branch.repository.revision_tree(lca)
60
61
    def get_old_lines(self, filename, base_path):
62
        """"Return the lines from before the conflicting changes were made."""
63
        try:
64
            old_lines = open(base_path).readlines()
65
        except IOError, e:
66
            if e.errno != errno.ENOENT:
67
                raise
68
            return self.get_base_tree_lines(filename, base_path)
69
        return old_lines
70
71
    def get_base_tree_lines(self, filename, base_path):
72
        if self._tree is None:
73
            tree, path = WorkingTree.open_containing(filename)
74
        else:
75
            tree = self._tree
76
            path = tree.relpath(filename)
702 by Aaron Bentley
Get conflict-diff under test.
77
        tree.lock_read()
78
        try:
79
            file_id = tree.path2id(path)
733 by Aaron Bentley
Conflict differ works with no file specified or many files specified.
80
            old_tree = self.get_old_tree(tree, base_path)
81
            return old_tree.get_file_lines(file_id)
702 by Aaron Bentley
Get conflict-diff under test.
82
        finally:
83
            tree.unlock()