/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# TODO: build_working_dir can be built on something simpler than merge()
18
18
 
19
19
import os
 
20
import errno
20
21
 
21
22
import bzrlib
22
 
from bzrlib._changeset import generate_changeset
23
 
from bzrlib._changeset import Inventory, Diff3Merge
24
 
from bzrlib._merge import MergeConflictHandler
 
23
from bzrlib._changeset import generate_changeset, ExceptionConflictHandler
 
24
from bzrlib._changeset import Inventory, Diff3Merge, ReplaceContents
25
25
from bzrlib._merge_core import WeaveMerge
26
26
from bzrlib._merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
27
27
from bzrlib.branch import Branch
28
28
from bzrlib.delta import compare_trees
29
29
from bzrlib.errors import (BzrCommandError,
 
30
                           NotBranchError,
30
31
                           UnrelatedBranches,
31
32
                           NoCommonAncestor,
32
33
                           NoCommits,
34
35
                           NotVersionedError,
35
36
                           BzrError)
36
37
from bzrlib.fetch import greedy_fetch, fetch
37
 
from bzrlib.osutils import pathjoin
 
38
import bzrlib.osutils
 
39
from bzrlib.osutils import rename, pathjoin
38
40
from bzrlib.revision import common_ancestor, MultipleRevisionSources
39
41
from bzrlib.revision import is_ancestor, NULL_REVISION
40
 
from bzrlib.trace import mutter, note
41
 
 
 
42
from bzrlib.trace import mutter, warning, note
 
43
 
 
44
# TODO: Report back as changes are merged in
 
45
 
 
46
# comments from abentley on irc: merge happens in two stages, each
 
47
# of which generates a changeset object
 
48
 
 
49
# stage 1: generate OLD->OTHER,
 
50
# stage 2: use MINE and OLD->OTHER to generate MINE -> RESULT
 
51
 
 
52
class _MergeConflictHandler(ExceptionConflictHandler):
 
53
    """Handle conflicts encountered while merging.
 
54
 
 
55
    This subclasses ExceptionConflictHandler, so that any types of
 
56
    conflict that are not explicitly handled cause an exception and
 
57
    terminate the merge.
 
58
    """
 
59
    def __init__(self, this_tree, base_tree, other_tree, ignore_zero=False):
 
60
        ExceptionConflictHandler.__init__(self)
 
61
        self.conflicts = 0
 
62
        self.ignore_zero = ignore_zero
 
63
        self.this_tree = this_tree
 
64
        self.base_tree = base_tree
 
65
        self.other_tree = other_tree
 
66
 
 
67
    def copy(self, source, dest):
 
68
        """Copy the text and mode of a file
 
69
        :param source: The path of the file to copy
 
70
        :param dest: The distination file to create
 
71
        """
 
72
        s_file = file(source, "rb")
 
73
        d_file = file(dest, "wb")
 
74
        for line in s_file:
 
75
            d_file.write(line)
 
76
        os.chmod(dest, 0777 & os.stat(source).st_mode)
 
77
 
 
78
    def dump(self, lines, dest):
 
79
        """Copy the text and mode of a file
 
80
        :param source: The path of the file to copy
 
81
        :param dest: The distination file to create
 
82
        """
 
83
        d_file = file(dest, "wb")
 
84
        for line in lines:
 
85
            d_file.write(line)
 
86
 
 
87
    def add_suffix(self, name, suffix, last_new_name=None, fix_inventory=True):
 
88
        """Rename a file to append a suffix.  If the new name exists, the
 
89
        suffix is added repeatedly until a non-existant name is found
 
90
 
 
91
        :param name: The path of the file
 
92
        :param suffix: The suffix to append
 
93
        :param last_new_name: (used for recursive calls) the last name tried
 
94
        """
 
95
        if last_new_name is None:
 
96
            last_new_name = name
 
97
        new_name = last_new_name+suffix
 
98
        try:
 
99
            rename(name, new_name)
 
100
            if fix_inventory is True:
 
101
                try:
 
102
                    relpath = self.this_tree.relpath(name)
 
103
                except NotBranchError:
 
104
                    relpath = None
 
105
                if relpath is not None:
 
106
                    file_id = self.this_tree.path2id(relpath)
 
107
                    if file_id is not None:
 
108
                        new_path = self.this_tree.relpath(new_name)
 
109
                        rename(new_name, name)
 
110
                        self.this_tree.rename_one(relpath, new_path)
 
111
                        assert self.this_tree.id2path(file_id) == new_path
 
112
        except OSError, e:
 
113
            if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY:
 
114
                raise
 
115
            return self.add_suffix(name, suffix, last_new_name=new_name, 
 
116
                                   fix_inventory=fix_inventory)
 
117
        return new_name
 
118
 
 
119
    def conflict(self, text):
 
120
        warning(text)
 
121
        self.conflicts += 1
 
122
        
 
123
 
 
124
    def merge_conflict(self, new_file, this_path, base_lines, other_lines):
 
125
        """
 
126
        Handle diff3 conflicts by producing a .THIS, .BASE and .OTHER.  The
 
127
        main file will be a version with diff3 conflicts.
 
128
        :param new_file: Path to the output file with diff3 markers
 
129
        :param this_path: Path to the file text for the THIS tree
 
130
        :param base_path: Path to the file text for the BASE tree
 
131
        :param other_path: Path to the file text for the OTHER tree
 
132
        """
 
133
        self.add_suffix(this_path, ".THIS", fix_inventory=False)
 
134
        self.dump(base_lines, this_path+".BASE")
 
135
        self.dump(other_lines, this_path+".OTHER")
 
136
        rename(new_file, this_path)
 
137
        self.conflict("Diff3 conflict encountered in %s" % this_path)
 
138
 
 
139
    def weave_merge_conflict(self, filename, weave, other_i, out_file):
 
140
        """
 
141
        Handle weave conflicts by producing a .THIS, and .OTHER.  The
 
142
        main file will be a version with diff3-style conflicts.
 
143
        """
 
144
        self.add_suffix(filename, ".THIS", fix_inventory=False)
 
145
        out_file.commit()
 
146
        self.dump(weave.get_iter(other_i), filename+".OTHER")
 
147
        self.conflict("Text conflict encountered in %s" % filename)
 
148
 
 
149
    def new_contents_conflict(self, filename, other_contents):
 
150
        """Conflicting contents for newly added file."""
 
151
        other_contents(filename + ".OTHER", self, False)
 
152
        self.conflict("Conflict in newly added file %s" % filename)
 
153
    
 
154
 
 
155
    def target_exists(self, entry, target, old_path):
 
156
        """Handle the case when the target file or dir exists"""
 
157
        moved_path = self.add_suffix(target, ".moved")
 
158
        self.conflict("Moved existing %s to %s" % (target, moved_path))
 
159
 
 
160
    def rmdir_non_empty(self, filename):
 
161
        """Handle the case where the dir to be removed still has contents"""
 
162
        self.conflict("Directory %s not removed because it is not empty"\
 
163
            % filename)
 
164
        return "skip"
 
165
 
 
166
    def rem_contents_conflict(self, filename, this_contents, base_contents):
 
167
        base_contents(filename+".BASE", self)
 
168
        this_contents(filename+".THIS", self)
 
169
        self.conflict("Other branch deleted locally modified file %s" %
 
170
                      filename)
 
171
        return ReplaceContents(this_contents, None)
 
172
 
 
173
    def abs_this_path(self, file_id):
 
174
        """Return the absolute path for a file_id in the this tree."""
 
175
        return self.this_tree.id2abspath(file_id)
 
176
 
 
177
    def add_missing_parents(self, file_id, tree):
 
178
        """If some of the parents for file_id are missing, add them."""
 
179
        entry = tree.inventory[file_id]
 
180
        if entry.parent_id not in self.this_tree:
 
181
            return self.create_all_missing(entry.parent_id, tree)
 
182
        else:
 
183
            return self.abs_this_path(entry.parent_id)
 
184
 
 
185
    def create_all_missing(self, file_id, tree):
 
186
        """Add contents for a file_id and all its parents to a tree."""
 
187
        entry = tree.inventory[file_id]
 
188
        if entry.parent_id is not None and entry.parent_id not in self.this_tree:
 
189
            abspath = self.create_all_missing(entry.parent_id, tree)
 
190
        else:
 
191
            abspath = self.abs_this_path(entry.parent_id)
 
192
        entry_path = pathjoin(abspath, entry.name)
 
193
        if not os.path.isdir(entry_path):
 
194
            self.create(file_id, entry_path, tree)
 
195
        return entry_path
 
196
 
 
197
    def create(self, file_id, path, tree):
 
198
        """Uses tree data to create a filesystem object for the file_id"""
 
199
        from _changeset import get_contents
 
200
        get_contents(tree, file_id)(path, self)
 
201
 
 
202
    def missing_for_merge(self, file_id, other_path):
 
203
        """The file_id doesn't exist in THIS, but does in OTHER and BASE"""
 
204
        self.conflict("Other branch modified locally deleted file %s" %
 
205
                      other_path)
 
206
        parent_dir = self.add_missing_parents(file_id, self.other_tree)
 
207
        stem = pathjoin(parent_dir, os.path.basename(other_path))
 
208
        self.create(file_id, stem+".OTHER", self.other_tree)
 
209
        self.create(file_id, stem+".BASE", self.base_tree)
 
210
 
 
211
    def threeway_contents_conflict(filename, this_contents, base_contents,
 
212
                                   other_contents):
 
213
        self.conflict("Three-way conflict merging %s" % filename)
 
214
 
 
215
    def finalize(self):
 
216
        if self.conflicts == 0:
 
217
            if not self.ignore_zero:
 
218
                note("All changes applied successfully.")
 
219
        else:
 
220
            note("%d conflicts encountered." % self.conflicts)
42
221
 
43
222
def _get_tree(treespec, local_branch=None):
44
223
    location, revno = treespec
140
319
    merger.set_interesting_files(file_list)
141
320
    merger.show_base = show_base 
142
321
    merger.reprocess = reprocess
143
 
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, 
144
 
                                                   merger.base_tree, 
145
 
                                                   merger.other_tree,
146
 
                                                   ignore_zero=ignore_zero)
 
322
    merger.conflict_handler = _MergeConflictHandler(merger.this_tree, 
 
323
                                                    merger.base_tree, 
 
324
                                                    merger.other_tree,
 
325
                                                    ignore_zero=ignore_zero)
147
326
    conflicts = merger.do_merge()
148
327
    merger.set_pending()
149
328
    return conflicts
173
352
        merger._set_interesting_files(interesting_files)
174
353
    merger.show_base = show_base 
175
354
    merger.reprocess = reprocess
176
 
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, base_tree, 
177
 
                                                   other_tree,
178
 
                                                   ignore_zero=ignore_zero)
 
355
    merger.conflict_handler = _MergeConflictHandler(merger.this_tree, 
 
356
                                                    base_tree, other_tree,
 
357
                                                    ignore_zero=ignore_zero)
179
358
    merger.other_rev_id = other_rev_id
180
359
    merger.other_basis = other_rev_id
181
360
    return merger.do_merge()
197
376
        self.interesting_ids = None
198
377
        self.show_base = False
199
378
        self.reprocess = False
200
 
        self.conflict_handler = MergeConflictHandler(self.this_tree, base_tree, 
201
 
                                                     other_tree)
 
379
        self.conflict_handler = _MergeConflictHandler(self.this_tree, 
 
380
                                                      base_tree, other_tree)
202
381
 
203
382
    def revision_tree(self, revision_id):
204
383
        return self.this_branch.revision_tree(revision_id)