~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

Merge in bzrdir work to enable checkout improvements.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
import os
19
 
import shutil
20
19
import errno
21
20
 
22
 
import bzrlib.osutils
23
 
import bzrlib.revision
24
 
from bzrlib.merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
25
 
from bzrlib.merge_core import WeaveMerge
26
 
from bzrlib.changeset import generate_changeset, ExceptionConflictHandler
27
 
from bzrlib.changeset import Inventory, Diff3Merge, ReplaceContents
 
21
import bzrlib
 
22
from bzrlib._changeset import generate_changeset, ExceptionConflictHandler
 
23
from bzrlib._changeset import Inventory, Diff3Merge, ReplaceContents
 
24
from bzrlib._merge_core import WeaveMerge
 
25
from bzrlib._merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
28
26
from bzrlib.branch import Branch
 
27
from bzrlib.delta import compare_trees
29
28
from bzrlib.errors import (BzrCommandError,
30
 
                           UnrelatedBranches,
 
29
                           BzrError,
31
30
                           NoCommonAncestor,
32
31
                           NoCommits,
33
 
                           WorkingTreeNotRevision,
 
32
                           NoSuchRevision,
34
33
                           NotBranchError,
35
34
                           NotVersionedError,
36
 
                           BzrError)
37
 
from bzrlib.delta import compare_trees
38
 
from bzrlib.trace import mutter, warning, note
 
35
                           UnrelatedBranches,
 
36
                           WorkingTreeNotRevision,
 
37
                           )
39
38
from bzrlib.fetch import greedy_fetch, fetch
40
 
from bzrlib.revision import is_ancestor, NULL_REVISION
 
39
import bzrlib.osutils
41
40
from bzrlib.osutils import rename, pathjoin
42
 
from bzrlib.revision import common_ancestor, MultipleRevisionSources
43
 
from bzrlib.errors import NoSuchRevision
 
41
from bzrlib.revision import common_ancestor, is_ancestor, NULL_REVISION
 
42
from bzrlib.trace import mutter, warning, note
44
43
 
45
44
# TODO: Report back as changes are merged in
46
45
 
47
 
# TODO: build_working_dir can be built on something simpler than merge()
48
 
 
49
 
# FIXME: merge() parameters seem oriented towards the command line
50
 
# NOTABUG: merge is a helper for commandline functions.  merge_inner is the
51
 
#          the core functionality.
52
 
 
53
46
# comments from abentley on irc: merge happens in two stages, each
54
47
# of which generates a changeset object
55
48
 
56
49
# stage 1: generate OLD->OTHER,
57
50
# stage 2: use MINE and OLD->OTHER to generate MINE -> RESULT
58
51
 
59
 
class MergeConflictHandler(ExceptionConflictHandler):
 
52
class _MergeConflictHandler(ExceptionConflictHandler):
60
53
    """Handle conflicts encountered while merging.
61
54
 
62
55
    This subclasses ExceptionConflictHandler, so that any types of
203
196
 
204
197
    def create(self, file_id, path, tree):
205
198
        """Uses tree data to create a filesystem object for the file_id"""
206
 
        from changeset import get_contents
 
199
        from bzrlib._changeset import get_contents
207
200
        get_contents(tree, file_id)(path, self)
208
201
 
209
202
    def missing_for_merge(self, file_id, other_path):
225
218
                note("All changes applied successfully.")
226
219
        else:
227
220
            note("%d conflicts encountered." % self.conflicts)
228
 
            
229
 
def get_tree(treespec, local_branch=None):
 
221
 
 
222
def _get_tree(treespec, local_branch=None):
230
223
    location, revno = treespec
231
224
    branch = Branch.open_containing(location)[0]
232
225
    if revno is None:
237
230
        revision = branch.get_rev_id(revno)
238
231
        if revision is None:
239
232
            revision = NULL_REVISION
240
 
    return branch, get_revid_tree(branch, revision, local_branch)
241
 
 
242
 
def get_revid_tree(branch, revision, local_branch):
 
233
    return branch, _get_revid_tree(branch, revision, local_branch)
 
234
 
 
235
 
 
236
def _get_revid_tree(branch, revision, local_branch):
243
237
    if revision is None:
244
 
        base_tree = branch.working_tree()
 
238
        base_tree = branch.bzrdir.open_workingtree()
245
239
    else:
246
240
        if local_branch is not None:
247
 
            greedy_fetch(local_branch, branch, revision)
248
 
            base_tree = local_branch.revision_tree(revision)
 
241
            if local_branch.base != branch.base:
 
242
                greedy_fetch(local_branch, branch, revision)
 
243
            base_tree = local_branch.repository.revision_tree(revision)
249
244
        else:
250
 
            base_tree = branch.revision_tree(revision)
 
245
            base_tree = branch.repository.revision_tree(revision)
251
246
    return base_tree
252
247
 
253
248
 
254
 
def file_exists(tree, file_id):
255
 
    return tree.has_filename(tree.id2path(file_id))
256
 
    
257
 
 
258
 
def build_working_dir(to_dir):
259
 
    """Build a working directory in an empty directory.
260
 
 
261
 
    to_dir is a directory containing branch metadata but no working files,
262
 
    typically constructed by cloning an existing branch. 
263
 
 
264
 
    This is split out as a special idiomatic case of merge.  It could
265
 
    eventually be done by just building the tree directly calling into 
266
 
    lower-level code (e.g. constructing a changeset).
267
 
    """
268
 
    # RBC 20051019 is this not just 'export' ?
269
 
    # AB Well, export doesn't take care of inventory...
270
 
    this_branch = Branch.open_containing(to_dir)[0]
271
 
    transform_tree(this_branch.working_tree(), this_branch.basis_tree())
272
 
 
273
 
 
274
249
def transform_tree(from_tree, to_tree, interesting_ids=None):
275
250
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
276
251
                interesting_ids=interesting_ids)
277
252
 
278
253
 
279
 
def merge(other_revision, base_revision,
280
 
          check_clean=True, ignore_zero=False,
281
 
          this_dir=None, backup_files=False, merge_type=ApplyMerge3,
282
 
          file_list=None, show_base=False, reprocess=False):
283
 
    """Merge changes into a tree.
284
 
 
285
 
    base_revision
286
 
        list(path, revno) Base for three-way merge.  
287
 
        If [None, None] then a base will be automatically determined.
288
 
    other_revision
289
 
        list(path, revno) Other revision for three-way merge.
290
 
    this_dir
291
 
        Directory to merge changes into; '.' by default.
292
 
    check_clean
293
 
        If true, this_dir must have no uncommitted changes before the
294
 
        merge begins.
295
 
    ignore_zero - If true, suppress the "zero conflicts" message when 
296
 
        there are no conflicts; should be set when doing something we expect
297
 
        to complete perfectly.
298
 
    file_list - If supplied, merge only changes to selected files.
299
 
 
300
 
    All available ancestors of other_revision and base_revision are
301
 
    automatically pulled into the branch.
302
 
 
303
 
    The revno may be -1 to indicate the last revision on the branch, which is
304
 
    the typical case.
305
 
 
306
 
    This function is intended for use from the command line; programmatic
307
 
    clients might prefer to call merge_inner(), which has less magic behavior.
308
 
    """
309
 
    if this_dir is None:
310
 
        this_dir = u'.'
311
 
    this_branch = Branch.open_containing(this_dir)[0]
312
 
    if show_base and not merge_type is ApplyMerge3:
313
 
        raise BzrCommandError("Show-base is not supported for this merge"
314
 
                              " type. %s" % merge_type)
315
 
    if reprocess and not merge_type is ApplyMerge3:
316
 
        raise BzrCommandError("Reprocess is not supported for this merge"
317
 
                              " type. %s" % merge_type)
318
 
    if reprocess and show_base:
319
 
        raise BzrCommandError("Cannot reprocess and show base.")
320
 
    merger = Merger(this_branch)
321
 
    merger.check_basis(check_clean)
322
 
    merger.set_other(other_revision)
323
 
    merger.set_base(base_revision)
324
 
    if merger.base_rev_id == merger.other_rev_id:
325
 
        note('Nothing to do.')
326
 
        return 0
327
 
    merger.backup_files = backup_files
328
 
    merger.merge_type = merge_type 
329
 
    merger.set_interesting_files(file_list)
330
 
    merger.show_base = show_base 
331
 
    merger.reprocess = reprocess
332
 
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, 
333
 
                                                   merger.base_tree, 
334
 
                                                   merger.other_tree,
335
 
                                                   ignore_zero=ignore_zero)
336
 
    conflicts = merger.do_merge()
337
 
    merger.set_pending()
338
 
    return conflicts
339
 
 
340
254
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
341
255
                backup_files=False, 
342
256
                merge_type=ApplyMerge3, 
344
258
                show_base=False, 
345
259
                reprocess=False, 
346
260
                other_rev_id=None,
347
 
                interesting_files=None):
 
261
                interesting_files=None,
 
262
                this_tree=None):
348
263
    """Primary interface for merging. 
349
264
 
350
265
        typical use is probably 
351
266
        'merge_inner(branch, branch.get_revision_tree(other_revision),
352
267
                     branch.get_revision_tree(base_revision))'
353
268
        """
354
 
    merger = Merger(this_branch, other_tree, base_tree)
 
269
    if this_tree is None:
 
270
        this_tree = this_branch.bzrdir.open_workingtree()
 
271
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree)
355
272
    merger.backup_files = backup_files
356
273
    merger.merge_type = merge_type
357
274
    merger.interesting_ids = interesting_ids
361
278
        merger._set_interesting_files(interesting_files)
362
279
    merger.show_base = show_base 
363
280
    merger.reprocess = reprocess
364
 
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, base_tree, 
365
 
                                                   other_tree,
366
 
                                                   ignore_zero=ignore_zero)
 
281
    merger.conflict_handler = _MergeConflictHandler(merger.this_tree, 
 
282
                                                    base_tree, other_tree,
 
283
                                                    ignore_zero=ignore_zero)
367
284
    merger.other_rev_id = other_rev_id
368
285
    merger.other_basis = other_rev_id
369
286
    return merger.do_merge()
370
287
 
371
288
 
372
289
class Merger(object):
373
 
    def __init__(self, this_branch, other_tree=None, base_tree=None):
 
290
    def __init__(self, this_branch, other_tree=None, base_tree=None, this_tree=None):
374
291
        object.__init__(self)
 
292
        assert this_tree is not None, "this_tree is required"
375
293
        self.this_branch = this_branch
376
294
        self.this_basis = this_branch.last_revision()
377
295
        self.this_rev_id = None
378
 
        self.this_tree = this_branch.working_tree()
 
296
        self.this_tree = this_tree
379
297
        self.this_revision_tree = None
380
298
        self.this_basis_tree = None
381
299
        self.other_tree = other_tree
385
303
        self.interesting_ids = None
386
304
        self.show_base = False
387
305
        self.reprocess = False
388
 
        self.conflict_handler = MergeConflictHandler(self.this_tree, base_tree, 
389
 
                                                     other_tree)
 
306
        self.conflict_handler = _MergeConflictHandler(self.this_tree, 
 
307
                                                      base_tree, other_tree)
390
308
 
391
309
    def revision_tree(self, revision_id):
392
 
        return self.this_branch.revision_tree(revision_id)
 
310
        return self.this_branch.repository.revision_tree(revision_id)
393
311
 
394
312
    def ensure_revision_trees(self):
395
313
        if self.this_revision_tree is None:
396
 
            self.this_basis_tree = self.this_branch.revision_tree(
 
314
            self.this_basis_tree = self.this_branch.repository.revision_tree(
397
315
                self.this_basis)
398
316
            if self.this_basis == self.this_rev_id:
399
317
                self.this_revision_tree = self.this_basis_tree
400
318
 
401
 
 
402
319
        if self.other_rev_id is None:
403
320
            other_basis_tree = self.revision_tree(self.other_basis)
404
321
            changes = compare_trees(self.other_tree, other_basis_tree)
407
324
            other_rev_id = other_basis
408
325
            self.other_tree = other_basis_tree
409
326
 
410
 
 
411
327
    def file_revisions(self, file_id):
412
328
        self.ensure_revision_trees()
413
329
        def get_id(tree, file_id):
421
337
 
422
338
        trees = (self.this_basis_tree, self.other_tree)
423
339
        return [get_id(tree, file_id) for tree in trees]
424
 
            
425
340
 
426
341
    def merge_factory(self, file_id, base, other):
427
342
        if self.merge_type.history_based:
453
368
                raise BzrCommandError("Working tree has uncommitted changes.")
454
369
 
455
370
    def compare_basis(self):
456
 
        changes = compare_trees(self.this_branch.working_tree(), 
457
 
                                self.this_branch.basis_tree(), False)
 
371
        changes = compare_trees(self.this_tree, 
 
372
                                self.this_tree.basis_tree(), False)
458
373
        if not changes.has_changed():
459
374
            self.this_rev_id = self.this_basis
460
375
 
472
387
            return
473
388
 
474
389
        interesting_ids = set()
475
 
        for fname in file_list:
476
 
            path = self.this_tree.relpath(fname)
 
390
        for path in file_list:
477
391
            found_id = False
478
392
            for tree in (self.this_tree, self.base_tree, self.other_tree):
479
393
                file_id = tree.inventory.path2id(path)
481
395
                    interesting_ids.add(file_id)
482
396
                    found_id = True
483
397
            if not found_id:
484
 
                raise NotVersionedError(path=fname)
 
398
                raise NotVersionedError(path=path)
485
399
        self.interesting_ids = interesting_ids
486
400
 
487
401
    def set_pending(self):
489
403
            return
490
404
        if self.other_rev_id is None:
491
405
            return
492
 
        if self.other_rev_id in self.this_branch.get_ancestry(self.this_basis):
 
406
        ancestry = self.this_branch.repository.get_ancestry(self.this_basis)
 
407
        if self.other_rev_id in ancestry:
493
408
            return
494
 
        self.this_branch.working_tree().add_pending_merge(self.other_rev_id)
 
409
        self.this_tree.add_pending_merge(self.other_rev_id)
495
410
 
496
411
    def set_other(self, other_revision):
497
 
        other_branch, self.other_tree = get_tree(other_revision, 
498
 
                                                 self.this_branch)
 
412
        other_branch, self.other_tree = _get_tree(other_revision, 
 
413
                                                  self.this_branch)
499
414
        if other_revision[1] == -1:
500
415
            self.other_rev_id = other_branch.last_revision()
501
416
            if self.other_rev_id is None:
509
424
            self.other_basis = other_branch.last_revision()
510
425
            if self.other_basis is None:
511
426
                raise NoCommits(other_branch)
512
 
        fetch(from_branch=other_branch, to_branch=self.this_branch, 
513
 
              last_revision=self.other_basis)
 
427
        if other_branch.base != self.this_branch.base:
 
428
            fetch(from_branch=other_branch, to_branch=self.this_branch, 
 
429
                  last_revision=self.other_basis)
514
430
 
515
431
    def set_base(self, base_revision):
516
432
        mutter("doing merge() with no base_revision specified")
518
434
            try:
519
435
                self.base_rev_id = common_ancestor(self.this_basis, 
520
436
                                                   self.other_basis, 
521
 
                                                   self.this_branch)
 
437
                                                   self.this_branch.repository)
522
438
            except NoCommonAncestor:
523
439
                raise UnrelatedBranches()
524
 
            self.base_tree = get_revid_tree(self.this_branch, self.base_rev_id,
 
440
            self.base_tree = _get_revid_tree(self.this_branch, self.base_rev_id,
525
441
                                            None)
526
442
            self.base_is_ancestor = True
527
443
        else:
528
 
            base_branch, self.base_tree = get_tree(base_revision)
 
444
            base_branch, self.base_tree = _get_tree(base_revision)
529
445
            if base_revision[1] == -1:
530
446
                self.base_rev_id = base_branch.last_revision()
531
447
            elif base_revision[1] is None:
558
474
                path = path[2:]
559
475
            adjust_ids.append((path, id))
560
476
        if len(adjust_ids) > 0:
561
 
            self.this_branch.working_tree().set_inventory(self.regen_inventory(adjust_ids))
 
477
            self.this_tree.set_inventory(self.regen_inventory(adjust_ids))
562
478
        conflicts = self.conflict_handler.conflicts
563
479
        self.conflict_handler.finalize()
564
480
        return conflicts
565
481
 
566
482
    def regen_inventory(self, new_entries):
567
 
        old_entries = self.this_branch.working_tree().read_working_inventory()
 
483
        old_entries = self.this_tree.read_working_inventory()
568
484
        new_inventory = {}
569
485
        by_path = {}
570
486
        new_entries_map = {} 
623
539
        new_inventory_list.sort()
624
540
        return new_inventory_list
625
541
 
 
542
 
626
543
merge_types = {     "merge3": (ApplyMerge3, "Native diff3-style merge"), 
627
544
                     "diff3": (Diff3Merge,  "Merge using external diff3"),
628
545
                     'weave': (WeaveMerge, "Weave-based merge")
629
546
              }
630