/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

  • Committer: Michael Ellerman
  • Date: 2005-10-26 10:03:47 UTC
  • mfrom: (1185.16.116)
  • mto: (1185.16.126)
  • mto: This revision was merged to the branch mainline in revision 1488.
  • Revision ID: michael@ellerman.id.au-20051026100347-bb0b2bd42f7953f2
MergeĀ mainline.

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
from bzrlib.revision import common_ancestor, MultipleRevisionSources
37
37
from bzrlib.errors import NoSuchRevision
38
38
 
 
39
# TODO: Report back as changes are merged in
 
40
 
39
41
# TODO: build_working_dir can be built on something simpler than merge()
40
42
 
41
43
# FIXME: merge() parameters seem oriented towards the command line
124
126
 
125
127
    def new_contents_conflict(self, filename, other_contents):
126
128
        """Conflicting contents for newly added file."""
127
 
        other.contents.apply(filename + ".OTHER")
 
129
        other_contents(filename + ".OTHER", self, False)
128
130
        self.conflict("Conflict in newly added file %s" % filename)
129
131
    
130
132
 
234
236
    eventually be done by just building the tree directly calling into 
235
237
    lower-level code (e.g. constructing a changeset).
236
238
    """
 
239
    # RBC 20051019 is this not just 'export' ?
237
240
    merge((to_dir, -1), (to_dir, 0), this_dir=to_dir,
238
241
          check_clean=False, ignore_zero=True)
239
242
 
241
244
def merge(other_revision, base_revision,
242
245
          check_clean=True, ignore_zero=False,
243
246
          this_dir=None, backup_files=False, merge_type=ApplyMerge3,
244
 
          file_list=None):
 
247
          file_list=None, show_base=False):
245
248
    """Merge changes into a tree.
246
249
 
247
250
    base_revision
248
 
        tuple(path, revision) Base for three-way merge.
 
251
        tuple(path, revno) Base for three-way merge.  
 
252
        If (None, None) then a base will be automatically determined.
249
253
    other_revision
250
 
        tuple(path, revision) Other revision for three-way merge.
 
254
        tuple(path, revno) Other revision for three-way merge.
251
255
    this_dir
252
256
        Directory to merge changes into; '.' by default.
253
257
    check_clean
256
260
    ignore_zero - If true, suppress the "zero conflicts" message when 
257
261
        there are no conflicts; should be set when doing something we expect
258
262
        to complete perfectly.
 
263
    file_list - If true, merge only changes to selected files.
259
264
 
260
265
    All available ancestors of other_revision and base_revision are
261
266
    automatically pulled into the branch.
 
267
 
 
268
    The revno may be -1 to indicate the last revision on the branch, which is the 
 
269
    typical case.
 
270
 
 
271
    This function is intended for use from the command line; programmatic clients 
 
272
    might prefer to call merge_inner(), which has less magic behavior.
262
273
    """
263
 
    tempdir = tempfile.mkdtemp(prefix="bzr-")
264
 
    try:
265
 
        if this_dir is None:
266
 
            this_dir = '.'
267
 
        this_branch = Branch.open_containing(this_dir)[0]
268
 
        this_rev_id = this_branch.last_revision()
269
 
        if this_rev_id is None:
270
 
            raise BzrCommandError("This branch has no commits")
271
 
        if check_clean:
272
 
            changes = compare_trees(this_branch.working_tree(), 
273
 
                                    this_branch.basis_tree(), False)
274
 
            if changes.has_changed():
275
 
                raise BzrCommandError("Working tree has uncommitted changes.")
276
 
        other_branch, other_tree = get_tree(other_revision, this_branch)
277
 
        if other_revision[1] == -1:
278
 
            other_rev_id = other_branch.last_revision()
279
 
            if other_rev_id is None:
280
 
                raise NoCommits(other_branch)
281
 
            other_basis = other_rev_id
282
 
        elif other_revision[1] is not None:
283
 
            other_rev_id = other_branch.get_rev_id(other_revision[1])
284
 
            other_basis = other_rev_id
285
 
        else:
286
 
            other_rev_id = None
287
 
            other_basis = other_branch.last_revision()
288
 
            if other_basis is None:
289
 
                raise NoCommits(other_branch)
290
 
        if base_revision == [None, None]:
291
 
            try:
292
 
                base_rev_id = common_ancestor(this_rev_id, other_basis, 
293
 
                                              this_branch)
294
 
            except NoCommonAncestor:
295
 
                raise UnrelatedBranches()
296
 
            base_tree = get_revid_tree(this_branch, base_rev_id, None)
297
 
            base_is_ancestor = True
298
 
        else:
299
 
            base_branch, base_tree = get_tree(base_revision)
300
 
            if base_revision[1] == -1:
301
 
                base_rev_id = base_branch.last_revision()
302
 
            elif base_revision[1] is None:
303
 
                base_rev_id = None
304
 
            else:
305
 
                base_rev_id = base_branch.get_rev_id(base_revision[1])
306
 
            fetch(from_branch=base_branch, to_branch=this_branch)
307
 
            base_is_ancestor = is_ancestor(this_rev_id, base_rev_id,
308
 
                                           this_branch)
309
 
        if file_list is None:
310
 
            interesting_ids = None
311
 
        else:
312
 
            interesting_ids = set()
313
 
            this_tree = this_branch.working_tree()
314
 
            for fname in file_list:
315
 
                path = this_tree.relpath(fname)
316
 
                found_id = False
317
 
                for tree in (this_tree, base_tree, other_tree):
318
 
                    file_id = tree.inventory.path2id(path)
319
 
                    if file_id is not None:
320
 
                        interesting_ids.add(file_id)
321
 
                        found_id = True
322
 
                if not found_id:
323
 
                    raise BzrCommandError("%s is not a source file in any"
324
 
                                          " tree." % fname)
325
 
        merge_inner(this_branch, other_tree, base_tree, tempdir, 
326
 
                    ignore_zero=ignore_zero, backup_files=backup_files, 
327
 
                    merge_type=merge_type, interesting_ids=interesting_ids)
328
 
        if base_is_ancestor and other_rev_id is not None\
329
 
            and other_rev_id not in this_branch.revision_history():
330
 
            this_branch.add_pending_merge(other_rev_id)
331
 
    finally:
332
 
        shutil.rmtree(tempdir)
 
274
    # TODO: please check this docstring is true and accurate - mbp 20051024
 
275
    if this_dir is None:
 
276
        this_dir = '.'
 
277
    this_branch = Branch.open_containing(this_dir)[0]
 
278
    this_rev_id = this_branch.last_revision()
 
279
    if this_rev_id is None:
 
280
        raise BzrCommandError("This branch has no commits")
 
281
    if check_clean:
 
282
        changes = compare_trees(this_branch.working_tree(), 
 
283
                                this_branch.basis_tree(), False)
 
284
        if changes.has_changed():
 
285
            raise BzrCommandError("Working tree has uncommitted changes.")
 
286
    other_branch, other_tree = get_tree(other_revision, this_branch)
 
287
    if other_revision[1] == -1:
 
288
        other_rev_id = other_branch.last_revision()
 
289
        if other_rev_id is None:
 
290
            raise NoCommits(other_branch)
 
291
        other_basis = other_rev_id
 
292
    elif other_revision[1] is not None:
 
293
        other_rev_id = other_branch.get_rev_id(other_revision[1])
 
294
        other_basis = other_rev_id
 
295
    else:
 
296
        other_rev_id = None
 
297
        other_basis = other_branch.last_revision()
 
298
        if other_basis is None:
 
299
            raise NoCommits(other_branch)
 
300
    fetch(from_branch=other_branch, to_branch=this_branch)
 
301
    if base_revision == [None, None]:
 
302
        mutter("doing merge() with no base_revision specified")
 
303
        try:
 
304
            base_rev_id = common_ancestor(this_rev_id, other_basis, 
 
305
                                          this_branch)
 
306
        except NoCommonAncestor:
 
307
            raise UnrelatedBranches()
 
308
        # fetch() is probably unnecessary in this case, because
 
309
        # get_revid_tree() does it anyway if base_rev_id is not None and the
 
310
        # local_branch is None -- but we do it above just to be sure -- mbp 20051024
 
311
        base_tree = get_revid_tree(this_branch, base_rev_id, None)
 
312
        base_is_ancestor = True
 
313
    else:
 
314
        mutter('doing merge() with base %r' % (base_revision,))
 
315
        base_branch, base_tree = get_tree(base_revision)
 
316
        if base_revision[1] == -1:
 
317
            base_rev_id = base_branch.last_revision()
 
318
        elif base_revision[1] is None:
 
319
            base_rev_id = None
 
320
        else:
 
321
            base_rev_id = base_branch.get_rev_id(base_revision[1])
 
322
        fetch(from_branch=base_branch, to_branch=this_branch)
 
323
        base_is_ancestor = is_ancestor(this_rev_id, base_rev_id,
 
324
                                       this_branch)
 
325
    if file_list is None:
 
326
        interesting_ids = None
 
327
    else:
 
328
        interesting_ids = set()
 
329
        this_tree = this_branch.working_tree()
 
330
        for fname in file_list:
 
331
            path = this_tree.relpath(fname)
 
332
            found_id = False
 
333
            for tree in (this_tree, base_tree, other_tree):
 
334
                file_id = tree.inventory.path2id(path)
 
335
                if file_id is not None:
 
336
                    interesting_ids.add(file_id)
 
337
                    found_id = True
 
338
            if not found_id:
 
339
                raise BzrCommandError("%s is not a source file in any"
 
340
                                      " tree." % fname)
 
341
    conflicts = merge_inner(this_branch, other_tree, base_tree, tempdir=None,
 
342
                            ignore_zero=ignore_zero,
 
343
                            backup_files=backup_files, 
 
344
                            merge_type=merge_type,
 
345
                            interesting_ids=interesting_ids,
 
346
                            show_base=show_base)
 
347
    if base_is_ancestor and other_rev_id is not None\
 
348
        and other_rev_id not in this_branch.revision_history():
 
349
        this_branch.add_pending_merge(other_rev_id)
 
350
    return conflicts
333
351
 
334
352
 
335
353
def set_interesting(inventory_a, inventory_b, interesting_ids):
340
358
             source_file.interesting = source_file.id in interesting_ids
341
359
 
342
360
 
343
 
def merge_inner(this_branch, other_tree, base_tree, tempdir, 
344
 
                ignore_zero=False, merge_type=ApplyMerge3, backup_files=False,
345
 
                interesting_ids=None):
346
 
 
 
361
def merge_inner(this_branch, other_tree, base_tree, tempdir=None, 
 
362
                ignore_zero=False, merge_type=ApplyMerge3, backup_files=False,
 
363
                interesting_ids=None, show_base=False):
 
364
    """Primary interface for merging. 
 
365
 
 
366
    typical use is probably 
 
367
    'merge_inner(branch, branch.get_revision_tree(other_revision),
 
368
                 branch.get_revision_tree(base_revision))'
 
369
    """
 
370
    if tempdir is None:
 
371
        _tempdir = tempfile.mkdtemp(prefix="bzr-")
 
372
    else:
 
373
        _tempdir = tempdir
 
374
    try:
 
375
        return _merge_inner(this_branch, other_tree, base_tree, _tempdir,
 
376
                            ignore_zero, merge_type, backup_files,
 
377
                            interesting_ids,
 
378
                            show_base=show_base)
 
379
    finally:
 
380
        if tempdir is None:
 
381
            shutil.rmtree(_tempdir)
 
382
 
 
383
 
 
384
def _merge_inner(this_branch, other_tree, base_tree, user_tempdir, 
 
385
                ignore_zero=False, merge_type=ApplyMerge3, backup_files=False,
 
386
                interesting_ids=None, show_base=False):
347
387
    def merge_factory(file_id, base, other):
348
 
        contents_change = merge_type(file_id, base, other)
 
388
        if show_base is True:
 
389
            contents_change = merge_type(file_id, base, other, show_base=True)
 
390
        else:
 
391
            contents_change = merge_type(file_id, base, other)
349
392
        if backup_files:
350
393
            contents_change = BackupBeforeChange(contents_change)
351
394
        return contents_change
355
398
    def get_inventory(tree):
356
399
        return tree.inventory
357
400
 
 
401
    conflict_handler = MergeConflictHandler(this_tree, base_tree, other_tree,
 
402
                                            ignore_zero=ignore_zero)
358
403
    inv_changes = merge_flex(this_tree, base_tree, other_tree,
359
404
                             generate_changeset, get_inventory,
360
 
                             MergeConflictHandler(this_tree, base_tree,
361
 
                             other_tree, ignore_zero=ignore_zero),
 
405
                             conflict_handler,
362
406
                             merge_factory=merge_factory, 
363
407
                             interesting_ids=interesting_ids)
364
408
 
375
419
        this_branch.set_inventory(regen_inventory(this_branch, 
376
420
                                                  this_tree.basedir,
377
421
                                                  adjust_ids))
 
422
    conflicts = conflict_handler.conflicts
 
423
    conflict_handler.finalize()
 
424
    return conflicts
378
425
 
379
426
 
380
427
def regen_inventory(this_branch, root, new_entries):