~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

MergeĀ fromĀ mainine.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
# TODO: build_working_dir can be built on something simpler than merge()
17
18
 
18
19
import os
19
 
import shutil
20
20
import errno
21
21
 
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
 
22
import bzrlib
 
23
from bzrlib._changeset import generate_changeset, ExceptionConflictHandler
 
24
from bzrlib._changeset import Inventory, Diff3Merge, ReplaceContents
 
25
from bzrlib._merge_core import WeaveMerge
 
26
from bzrlib._merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
28
27
from bzrlib.branch import Branch
 
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,
33
34
                           WorkingTreeNotRevision,
34
 
                           NotBranchError,
35
35
                           NotVersionedError,
36
36
                           BzrError)
37
 
from bzrlib.delta import compare_trees
38
 
from bzrlib.trace import mutter, warning, note
39
37
from bzrlib.fetch import greedy_fetch, fetch
40
 
from bzrlib.revision import is_ancestor, NULL_REVISION
 
38
import bzrlib.osutils
41
39
from bzrlib.osutils import rename, pathjoin
42
40
from bzrlib.revision import common_ancestor, MultipleRevisionSources
43
 
from bzrlib.errors import NoSuchRevision
 
41
from bzrlib.revision import 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
238
        base_tree = branch.working_tree()
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
249
def build_working_dir(to_dir):
259
250
    """Build a working directory in an empty directory.
260
251
 
276
267
                interesting_ids=interesting_ids)
277
268
 
278
269
 
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
270
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
341
271
                backup_files=False, 
342
272
                merge_type=ApplyMerge3, 
361
291
        merger._set_interesting_files(interesting_files)
362
292
    merger.show_base = show_base 
363
293
    merger.reprocess = reprocess
364
 
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, base_tree, 
365
 
                                                   other_tree,
366
 
                                                   ignore_zero=ignore_zero)
 
294
    merger.conflict_handler = _MergeConflictHandler(merger.this_tree, 
 
295
                                                    base_tree, other_tree,
 
296
                                                    ignore_zero=ignore_zero)
367
297
    merger.other_rev_id = other_rev_id
368
298
    merger.other_basis = other_rev_id
369
299
    return merger.do_merge()
385
315
        self.interesting_ids = None
386
316
        self.show_base = False
387
317
        self.reprocess = False
388
 
        self.conflict_handler = MergeConflictHandler(self.this_tree, base_tree, 
389
 
                                                     other_tree)
 
318
        self.conflict_handler = _MergeConflictHandler(self.this_tree, 
 
319
                                                      base_tree, other_tree)
390
320
 
391
321
    def revision_tree(self, revision_id):
392
 
        return self.this_branch.revision_tree(revision_id)
 
322
        return self.this_branch.repository.revision_tree(revision_id)
393
323
 
394
324
    def ensure_revision_trees(self):
395
325
        if self.this_revision_tree is None:
396
 
            self.this_basis_tree = self.this_branch.revision_tree(
 
326
            self.this_basis_tree = self.this_branch.repository.revision_tree(
397
327
                self.this_basis)
398
328
            if self.this_basis == self.this_rev_id:
399
329
                self.this_revision_tree = self.this_basis_tree
400
330
 
401
 
 
402
331
        if self.other_rev_id is None:
403
332
            other_basis_tree = self.revision_tree(self.other_basis)
404
333
            changes = compare_trees(self.other_tree, other_basis_tree)
407
336
            other_rev_id = other_basis
408
337
            self.other_tree = other_basis_tree
409
338
 
410
 
 
411
339
    def file_revisions(self, file_id):
412
340
        self.ensure_revision_trees()
413
341
        def get_id(tree, file_id):
421
349
 
422
350
        trees = (self.this_basis_tree, self.other_tree)
423
351
        return [get_id(tree, file_id) for tree in trees]
424
 
            
425
352
 
426
353
    def merge_factory(self, file_id, base, other):
427
354
        if self.merge_type.history_based:
472
399
            return
473
400
 
474
401
        interesting_ids = set()
475
 
        for fname in file_list:
476
 
            path = self.this_tree.relpath(fname)
 
402
        for path in file_list:
477
403
            found_id = False
478
404
            for tree in (self.this_tree, self.base_tree, self.other_tree):
479
405
                file_id = tree.inventory.path2id(path)
481
407
                    interesting_ids.add(file_id)
482
408
                    found_id = True
483
409
            if not found_id:
484
 
                raise NotVersionedError(path=fname)
 
410
                raise NotVersionedError(path=path)
485
411
        self.interesting_ids = interesting_ids
486
412
 
487
413
    def set_pending(self):
489
415
            return
490
416
        if self.other_rev_id is None:
491
417
            return
492
 
        if self.other_rev_id in self.this_branch.get_ancestry(self.this_basis):
 
418
        ancestry = self.this_branch.repository.get_ancestry(self.this_basis)
 
419
        if self.other_rev_id in ancestry:
493
420
            return
494
421
        self.this_branch.working_tree().add_pending_merge(self.other_rev_id)
495
422
 
496
423
    def set_other(self, other_revision):
497
 
        other_branch, self.other_tree = get_tree(other_revision, 
498
 
                                                 self.this_branch)
 
424
        other_branch, self.other_tree = _get_tree(other_revision, 
 
425
                                                  self.this_branch)
499
426
        if other_revision[1] == -1:
500
427
            self.other_rev_id = other_branch.last_revision()
501
428
            if self.other_rev_id is None:
509
436
            self.other_basis = other_branch.last_revision()
510
437
            if self.other_basis is None:
511
438
                raise NoCommits(other_branch)
512
 
        fetch(from_branch=other_branch, to_branch=self.this_branch, 
513
 
              last_revision=self.other_basis)
 
439
        if other_branch.base != self.this_branch.base:
 
440
            fetch(from_branch=other_branch, to_branch=self.this_branch, 
 
441
                  last_revision=self.other_basis)
514
442
 
515
443
    def set_base(self, base_revision):
516
444
        mutter("doing merge() with no base_revision specified")
518
446
            try:
519
447
                self.base_rev_id = common_ancestor(self.this_basis, 
520
448
                                                   self.other_basis, 
521
 
                                                   self.this_branch)
 
449
                                                   self.this_branch.repository)
522
450
            except NoCommonAncestor:
523
451
                raise UnrelatedBranches()
524
 
            self.base_tree = get_revid_tree(self.this_branch, self.base_rev_id,
 
452
            self.base_tree = _get_revid_tree(self.this_branch, self.base_rev_id,
525
453
                                            None)
526
454
            self.base_is_ancestor = True
527
455
        else:
528
 
            base_branch, self.base_tree = get_tree(base_revision)
 
456
            base_branch, self.base_tree = _get_tree(base_revision)
529
457
            if base_revision[1] == -1:
530
458
                self.base_rev_id = base_branch.last_revision()
531
459
            elif base_revision[1] is None:
623
551
        new_inventory_list.sort()
624
552
        return new_inventory_list
625
553
 
 
554
 
626
555
merge_types = {     "merge3": (ApplyMerge3, "Native diff3-style merge"), 
627
556
                     "diff3": (Diff3Merge,  "Merge using external diff3"),
628
557
                     'weave': (WeaveMerge, "Weave-based merge")
629
558
              }
630