~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Robert Collins
  • Date: 2005-09-29 02:01:49 UTC
  • Revision ID: robertc@robertcollins.net-20050929020149-1ff16722c6a01b2c
reenable remotebranch tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import bzrlib.revision
25
25
from bzrlib.merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
26
26
from bzrlib.changeset import generate_changeset, ExceptionConflictHandler
27
 
from bzrlib.changeset import Inventory, Diff3Merge, ReplaceContents
 
27
from bzrlib.changeset import Inventory, Diff3Merge
28
28
from bzrlib.branch import Branch
29
29
from bzrlib.errors import BzrCommandError, UnrelatedBranches, NoCommonAncestor
30
30
from bzrlib.errors import NoCommits
36
36
from bzrlib.revision import common_ancestor, MultipleRevisionSources
37
37
from bzrlib.errors import NoSuchRevision
38
38
 
39
 
# TODO: build_working_dir can be built on something simpler than merge()
40
 
 
41
 
# FIXME: merge() parameters seem oriented towards the command line
42
39
 
43
40
# comments from abentley on irc: merge happens in two stages, each
44
41
# of which generates a changeset object
53
50
    conflict that are not explicitly handled cause an exception and
54
51
    terminate the merge.
55
52
    """
56
 
    def __init__(self, this_tree, base_tree, other_tree, ignore_zero=False):
 
53
    def __init__(self, ignore_zero=False):
57
54
        ExceptionConflictHandler.__init__(self)
58
55
        self.conflicts = 0
59
56
        self.ignore_zero = ignore_zero
60
 
        self.this_tree = this_tree
61
 
        self.base_tree = base_tree
62
 
        self.other_tree = other_tree
63
57
 
64
58
    def copy(self, source, dest):
65
59
        """Copy the text and mode of a file
137
131
            % filename)
138
132
        return "skip"
139
133
 
140
 
    def rem_contents_conflict(self, filename, this_contents, base_contents):
141
 
        base_contents(filename+".BASE", self, False)
142
 
        this_contents(filename+".THIS", self, False)
143
 
        self.conflict("Other branch deleted locally modified file %s" %
144
 
                      filename)
145
 
        return ReplaceContents(this_contents, None)
146
 
 
147
 
    def abs_this_path(self, file_id):
148
 
        """Return the absolute path for a file_id in the this tree."""
149
 
        relpath = self.this_tree.id2path(file_id)
150
 
        return self.this_tree.tree.abspath(relpath)
151
 
 
152
 
    def add_missing_parents(self, file_id, tree):
153
 
        """If some of the parents for file_id are missing, add them."""
154
 
        entry = tree.tree.inventory[file_id]
155
 
        if entry.parent_id not in self.this_tree:
156
 
            return self.create_all_missing(entry.parent_id, tree)
157
 
        else:
158
 
            return self.abs_this_path(entry.parent_id)
159
 
 
160
 
    def create_all_missing(self, file_id, tree):
161
 
        """Add contents for a file_id and all its parents to a tree."""
162
 
        entry = tree.tree.inventory[file_id]
163
 
        if entry.parent_id is not None and entry.parent_id not in self.this_tree:
164
 
            abspath = self.create_all_missing(entry.parent_id, tree)
165
 
        else:
166
 
            abspath = self.abs_this_path(entry.parent_id)
167
 
        entry_path = os.path.join(abspath, entry.name)
168
 
        if not os.path.isdir(entry_path):
169
 
            self.create(file_id, entry_path, tree)
170
 
        return entry_path
171
 
 
172
 
    def create(self, file_id, path, tree, reverse=False):
173
 
        """Uses tree data to create a filesystem object for the file_id"""
174
 
        from merge_core import get_id_contents
175
 
        get_id_contents(file_id, tree)(path, self, reverse)
176
 
 
177
 
    def missing_for_merge(self, file_id, other_path):
178
 
        """The file_id doesn't exist in THIS, but does in OTHER and BASE"""
179
 
        self.conflict("Other branch modified locally deleted file %s" %
180
 
                      other_path)
181
 
        parent_dir = self.add_missing_parents(file_id, self.other_tree)
182
 
        stem = os.path.join(parent_dir, os.path.basename(other_path))
183
 
        self.create(file_id, stem+".OTHER", self.other_tree)
184
 
        self.create(file_id, stem+".BASE", self.base_tree)
185
 
 
186
134
    def finalize(self):
187
135
        if not self.ignore_zero:
188
136
            print "%d conflicts encountered.\n" % self.conflicts
227
175
        self.tree = tree
228
176
        self.tempdir = tempdir
229
177
        os.mkdir(os.path.join(self.tempdir, "texts"))
230
 
        os.mkdir(os.path.join(self.tempdir, "symlinks"))
231
178
        self.cached = {}
232
179
 
233
180
    def __iter__(self):
242
189
    def get_file_sha1(self, id):
243
190
        return self.tree.get_file_sha1(id)
244
191
 
245
 
    def is_executable(self, id):
246
 
        return self.tree.is_executable(id)
247
 
 
248
192
    def id2path(self, file_id):
249
193
        return self.tree.id2path(file_id)
250
194
 
267
211
        if self.root is not None:
268
212
            return self.tree.abspath(self.tree.id2path(id))
269
213
        else:
270
 
            kind = self.tree.inventory[id].kind
271
 
            if kind in ("directory", "root_directory"):
 
214
            if self.tree.inventory[id].kind in ("directory", "root_directory"):
272
215
                return self.tempdir
273
216
            if not self.cached.has_key(id):
274
 
                if kind == "file":
275
 
                    path = os.path.join(self.tempdir, "texts", id)
276
 
                    outfile = file(path, "wb")
277
 
                    outfile.write(self.tree.get_file(id).read())
278
 
                    assert(bzrlib.osutils.lexists(path))
279
 
                    if self.tree.is_executable(id):
280
 
                        os.chmod(path, 0755)
281
 
                else:
282
 
                    assert kind == "symlink"
283
 
                    path = os.path.join(self.tempdir, "symlinks", id)
284
 
                    target = self.tree.get_symlink_target(id)
285
 
                    os.symlink(target, path)
 
217
                path = os.path.join(self.tempdir, "texts", id)
 
218
                outfile = file(path, "wb")
 
219
                outfile.write(self.tree.get_file(id).read())
 
220
                assert(os.path.exists(path))
286
221
                self.cached[id] = path
287
222
            return self.cached[id]
288
223
 
289
224
 
290
 
def build_working_dir(to_dir):
291
 
    """Build a working directory in an empty directory.
292
 
 
293
 
    to_dir is a directory containing branch metadata but no working files,
294
 
    typically constructed by cloning an existing branch. 
295
 
 
296
 
    This is split out as a special idiomatic case of merge.  It could
297
 
    eventually be done by just building the tree directly calling into 
298
 
    lower-level code (e.g. constructing a changeset).
299
 
    """
300
 
    merge((to_dir, -1), (to_dir, 0), this_dir=to_dir,
301
 
          check_clean=False, ignore_zero=True)
302
 
 
303
225
 
304
226
def merge(other_revision, base_revision,
305
227
          check_clean=True, ignore_zero=False,
316
238
    check_clean
317
239
        If true, this_dir must have no uncommitted changes before the
318
240
        merge begins.
319
 
    ignore_zero - If true, suppress the "zero conflicts" message when 
320
 
        there are no conflicts; should be set when doing something we expect
321
 
        to complete perfectly.
322
241
 
323
242
    All available ancestors of other_revision and base_revision are
324
243
    automatically pulled into the branch.
432
351
 
433
352
    inv_changes = merge_flex(this_tree, base_tree, other_tree,
434
353
                             generate_cset_optimized, get_inventory,
435
 
                             MergeConflictHandler(this_tree, base_tree,
436
 
                             other_tree, ignore_zero=ignore_zero),
 
354
                             MergeConflictHandler(ignore_zero=ignore_zero),
437
355
                             merge_factory=merge_factory, 
438
356
                             interesting_ids=interesting_ids)
439
357