/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: John Arbash Meinel
  • Date: 2006-09-20 14:51:03 UTC
  • mfrom: (0.8.23 version_info)
  • mto: This revision was merged to the branch mainline in revision 2028.
  • Revision ID: john@arbash-meinel.com-20060920145103-02725c6d6c886040
[merge] version-info plugin, and cleanup for layout in bzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import os
19
19
import errno
 
20
from tempfile import mkdtemp
20
21
import warnings
21
22
 
22
 
from bzrlib import (
23
 
    osutils,
24
 
    registry,
25
 
    )
26
23
from bzrlib.branch import Branch
27
24
from bzrlib.conflicts import ConflictList, Conflict
28
25
from bzrlib.errors import (BzrCommandError,
39
36
                           BinaryFile,
40
37
                           )
41
38
from bzrlib.merge3 import Merge3
42
 
from bzrlib.osutils import rename, pathjoin
 
39
import bzrlib.osutils
 
40
from bzrlib.osutils import rename, pathjoin, rmtree
43
41
from progress import DummyProgress, ProgressPhase
44
42
from bzrlib.revision import common_ancestor, is_ancestor, NULL_REVISION
45
43
from bzrlib.textfile import check_text_lines
46
44
from bzrlib.trace import mutter, warning, note
47
45
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
48
 
                              FinalPaths, create_by_entry, unique_add,
49
 
                              ROOT_PARENT)
 
46
                              FinalPaths, create_by_entry, unique_add)
50
47
from bzrlib.versionedfile import WeaveMerge
51
48
from bzrlib import ui
52
49
 
60
57
        return tree.branch, tree
61
58
    branch = Branch.open_containing(location)[0]
62
59
    if revno == -1:
63
 
        revision_id = branch.last_revision()
 
60
        revision = branch.last_revision()
64
61
    else:
65
 
        revision_id = branch.get_rev_id(revno)
66
 
    if revision_id is None:
67
 
        revision_id = NULL_REVISION
68
 
    return branch, _get_revid_tree(branch, revision_id, local_branch)
69
 
 
70
 
 
71
 
def _get_revid_tree(branch, revision_id, local_branch):
72
 
    if revision_id is None:
 
62
        revision = branch.get_rev_id(revno)
 
63
        if revision is None:
 
64
            revision = NULL_REVISION
 
65
    return branch, _get_revid_tree(branch, revision, local_branch)
 
66
 
 
67
 
 
68
def _get_revid_tree(branch, revision, local_branch):
 
69
    if revision is None:
73
70
        base_tree = branch.bzrdir.open_workingtree()
74
71
    else:
75
72
        if local_branch is not None:
76
73
            if local_branch.base != branch.base:
77
 
                local_branch.fetch(branch, revision_id)
78
 
            base_tree = local_branch.repository.revision_tree(revision_id)
 
74
                local_branch.fetch(branch, revision)
 
75
            base_tree = local_branch.repository.revision_tree(revision)
79
76
        else:
80
 
            base_tree = branch.repository.revision_tree(revision_id)
 
77
            base_tree = branch.repository.revision_tree(revision)
81
78
    return base_tree
82
79
 
83
80
 
84
 
def _get_revid_tree_from_tree(tree, revision_id, local_branch):
85
 
    if revision_id is None:
86
 
        return tree
87
 
    if local_branch is not None:
88
 
        if local_branch.base != tree.branch.base:
89
 
            local_branch.fetch(tree.branch, revision_id)
90
 
        return local_branch.repository.revision_tree(revision_id)
91
 
    return tree.branch.repository.revision_tree(revision_id)
92
 
 
93
 
 
94
81
def transform_tree(from_tree, to_tree, interesting_ids=None):
95
82
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
96
83
                interesting_ids=interesting_ids, this_tree=from_tree)
97
84
 
98
85
 
99
86
class Merger(object):
100
 
    def __init__(self, this_branch, other_tree=None, base_tree=None,
101
 
                 this_tree=None, pb=DummyProgress(), change_reporter=None,
102
 
                 recurse='down'):
 
87
    def __init__(self, this_branch, other_tree=None, base_tree=None, 
 
88
                 this_tree=None, pb=DummyProgress()):
103
89
        object.__init__(self)
104
90
        assert this_tree is not None, "this_tree is required"
105
91
        self.this_branch = this_branch
109
95
        self.this_revision_tree = None
110
96
        self.this_basis_tree = None
111
97
        self.other_tree = other_tree
112
 
        self.other_branch = None
113
98
        self.base_tree = base_tree
114
99
        self.ignore_zero = False
115
100
        self.backup_files = False
116
101
        self.interesting_ids = None
117
102
        self.show_base = False
118
103
        self.reprocess = False
119
 
        self._pb = pb
 
104
        self._pb = pb 
120
105
        self.pp = None
121
 
        self.recurse = recurse
122
 
        self.change_reporter = change_reporter
 
106
 
123
107
 
124
108
    def revision_tree(self, revision_id):
125
109
        return self.this_branch.repository.revision_tree(revision_id)
155
139
 
156
140
    def check_basis(self, check_clean, require_commits=True):
157
141
        if self.this_basis is None and require_commits is True:
158
 
            raise BzrCommandError("This branch has no commits."
159
 
                                  " (perhaps you would prefer 'bzr pull')")
 
142
            raise BzrCommandError("This branch has no commits")
160
143
        if check_clean:
161
144
            self.compare_basis()
162
145
            if self.this_basis != self.this_rev_id:
183
166
        interesting_ids = set()
184
167
        for path in file_list:
185
168
            found_id = False
186
 
            # TODO: jam 20070226 The trees are not locked at this time,
187
 
            #       wouldn't it make merge faster if it locks everything in the
188
 
            #       beginning? It locks at do_merge time, but this happens
189
 
            #       before that.
190
169
            for tree in (self.this_tree, self.base_tree, self.other_tree):
191
 
                file_id = tree.path2id(path)
 
170
                file_id = tree.inventory.path2id(path)
192
171
                if file_id is not None:
193
172
                    interesting_ids.add(file_id)
194
173
                    found_id = True
213
192
 
214
193
        :param other_revision: The [path, revision] list to merge from.
215
194
        """
216
 
        self.other_branch, self.other_tree = _get_tree(other_revision,
 
195
        other_branch, self.other_tree = _get_tree(other_revision,
217
196
                                                  self.this_branch)
218
197
        if other_revision[1] == -1:
219
 
            self.other_rev_id = self.other_branch.last_revision()
 
198
            self.other_rev_id = other_branch.last_revision()
220
199
            if self.other_rev_id is None:
221
 
                raise NoCommits(self.other_branch)
 
200
                raise NoCommits(other_branch)
222
201
            self.other_basis = self.other_rev_id
223
202
        elif other_revision[1] is not None:
224
 
            self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
 
203
            self.other_rev_id = other_branch.get_rev_id(other_revision[1])
225
204
            self.other_basis = self.other_rev_id
226
205
        else:
227
206
            self.other_rev_id = None
228
 
            self.other_basis = self.other_branch.last_revision()
 
207
            self.other_basis = other_branch.last_revision()
229
208
            if self.other_basis is None:
230
 
                raise NoCommits(self.other_branch)
231
 
        if self.other_branch.base != self.this_branch.base:
232
 
            self.this_branch.fetch(self.other_branch,
233
 
                                   last_revision=self.other_basis)
234
 
 
235
 
    def set_other_revision(self, revision_id, other_branch):
236
 
        """Set 'other' based on a branch and revision id
237
 
 
238
 
        :param revision_id: The revision to use for a tree
239
 
        :param other_branch: The branch containing this tree
240
 
        """
241
 
        self.other_rev_id = revision_id
242
 
        self.other_branch = other_branch
243
 
        self.this_branch.fetch(other_branch, self.other_rev_id)
244
 
        self.other_tree = self.revision_tree(revision_id)
245
 
        self.other_basis = revision_id
 
209
                raise NoCommits(other_branch)
 
210
        if other_branch.base != self.this_branch.base:
 
211
            self.this_branch.fetch(other_branch, last_revision=self.other_basis)
246
212
 
247
213
    def find_base(self):
248
214
        self.set_base([None, None])
255
221
        mutter("doing merge() with no base_revision specified")
256
222
        if base_revision == [None, None]:
257
223
            try:
258
 
                pb = ui.ui_factory.nested_progress_bar()
 
224
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
259
225
                try:
260
226
                    this_repo = self.this_branch.repository
261
227
                    self.base_rev_id = common_ancestor(self.this_basis, 
265
231
                    pb.finished()
266
232
            except NoCommonAncestor:
267
233
                raise UnrelatedBranches()
268
 
            self.base_tree = _get_revid_tree_from_tree(self.this_tree,
269
 
                                                       self.base_rev_id,
270
 
                                                       None)
 
234
            self.base_tree = _get_revid_tree(self.this_branch, self.base_rev_id,
 
235
                                            None)
271
236
            self.base_is_ancestor = True
272
237
        else:
273
238
            base_branch, self.base_tree = _get_tree(base_revision)
284
249
                                                self.this_branch)
285
250
 
286
251
    def do_merge(self):
287
 
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
288
 
                  'other_tree': self.other_tree,
 
252
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree, 
 
253
                  'other_tree': self.other_tree, 
289
254
                  'interesting_ids': self.interesting_ids,
290
255
                  'pp': self.pp}
291
256
        if self.merge_type.requires_base:
300
265
        elif self.show_base:
301
266
            raise BzrError("Showing base is not supported for this"
302
267
                                  " merge type. %s" % self.merge_type)
303
 
        self.this_tree.lock_tree_write()
304
 
        if self.base_tree is not None:
305
 
            self.base_tree.lock_read()
306
 
        if self.other_tree is not None:
307
 
            self.other_tree.lock_read()
308
 
        try:
309
 
            merge = self.merge_type(pb=self._pb,
310
 
                                    change_reporter=self.change_reporter,
311
 
                                    **kwargs)
312
 
            if self.recurse == 'down':
313
 
                for path, file_id in self.this_tree.iter_references():
314
 
                    sub_tree = self.this_tree.get_nested_tree(file_id, path)
315
 
                    other_revision = self.other_tree.get_reference_revision(
316
 
                        file_id, path)
317
 
                    if  other_revision == sub_tree.last_revision():
318
 
                        continue
319
 
                    sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
320
 
                    sub_merge.merge_type = self.merge_type
321
 
                    relpath = self.this_tree.relpath(path)
322
 
                    other_branch = self.other_branch.reference_parent(file_id, relpath)
323
 
                    sub_merge.set_other_revision(other_revision, other_branch)
324
 
                    base_revision = self.base_tree.get_reference_revision(file_id)
325
 
                    sub_merge.base_tree = \
326
 
                        sub_tree.branch.repository.revision_tree(base_revision)
327
 
                    sub_merge.do_merge()
328
 
 
329
 
        finally:
330
 
            if self.other_tree is not None:
331
 
                self.other_tree.unlock()
332
 
            if self.base_tree is not None:
333
 
                self.base_tree.unlock()
334
 
            self.this_tree.unlock()
 
268
        merge = self.merge_type(pb=self._pb, **kwargs)
335
269
        if len(merge.cooked_conflicts) == 0:
336
270
            if not self.ignore_zero:
337
271
                note("All changes applied successfully.")
390
324
            else:
391
325
                parent = by_path[os.path.dirname(path)]
392
326
            abspath = pathjoin(self.this_tree.basedir, path)
393
 
            kind = osutils.file_kind(abspath)
 
327
            kind = bzrlib.osutils.file_kind(abspath)
394
328
            if file_id in self.base_tree.inventory:
395
329
                executable = getattr(self.base_tree.inventory[file_id], 'executable', False)
396
330
            else:
419
353
 
420
354
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
421
355
                 interesting_ids=None, reprocess=False, show_base=False,
422
 
                 pb=DummyProgress(), pp=None, change_reporter=None):
 
356
                 pb=DummyProgress(), pp=None):
423
357
        """Initialize the merger object and perform the merge."""
424
358
        object.__init__(self)
425
359
        self.this_tree = working_tree
426
 
        self.this_tree.lock_tree_write()
427
360
        self.base_tree = base_tree
428
 
        self.base_tree.lock_read()
429
361
        self.other_tree = other_tree
430
 
        self.other_tree.lock_read()
431
362
        self._raw_conflicts = []
432
363
        self.cooked_conflicts = []
433
364
        self.reprocess = reprocess
434
365
        self.show_base = show_base
435
366
        self.pb = pb
436
367
        self.pp = pp
437
 
        self.change_reporter = change_reporter
438
368
        if self.pp is None:
439
369
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
440
370
 
443
373
        else:
444
374
            all_ids = set(base_tree)
445
375
            all_ids.update(other_tree)
 
376
        working_tree.lock_write()
446
377
        self.tt = TreeTransform(working_tree, self.pb)
447
378
        try:
448
379
            self.pp.next_phase()
455
386
                    self.merge_executable(file_id, file_status)
456
387
            finally:
457
388
                child_pb.finished()
458
 
            self.fix_root()
 
389
                
459
390
            self.pp.next_phase()
460
391
            child_pb = ui.ui_factory.nested_progress_bar()
461
392
            try:
462
393
                fs_conflicts = resolve_conflicts(self.tt, child_pb)
463
394
            finally:
464
395
                child_pb.finished()
465
 
            if change_reporter is not None:
466
 
                from bzrlib import delta
467
 
                delta.report_changes(self.tt._iter_changes(), change_reporter)
468
396
            self.cook_conflicts(fs_conflicts)
469
397
            for conflict in self.cooked_conflicts:
470
398
                warning(conflict)
477
405
                pass
478
406
        finally:
479
407
            self.tt.finalize()
480
 
            self.other_tree.unlock()
481
 
            self.base_tree.unlock()
482
 
            self.this_tree.unlock()
 
408
            working_tree.unlock()
483
409
            self.pb.clear()
484
410
 
485
 
    def fix_root(self):
486
 
        try:
487
 
            self.tt.final_kind(self.tt.root)
488
 
        except NoSuchFile:
489
 
            self.tt.cancel_deletion(self.tt.root)
490
 
        if self.tt.final_file_id(self.tt.root) is None:
491
 
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
492
 
                                 self.tt.root)
493
 
        if self.other_tree.inventory.root is None:
494
 
            return
495
 
        other_root_file_id = self.other_tree.inventory.root.file_id
496
 
        other_root = self.tt.trans_id_file_id(other_root_file_id)
497
 
        if other_root == self.tt.root:
498
 
            return
499
 
        try:
500
 
            self.tt.final_kind(other_root)
501
 
        except NoSuchFile:
502
 
            return
503
 
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
504
 
        self.tt.cancel_creation(other_root)
505
 
        self.tt.cancel_versioning(other_root)
506
 
 
507
 
    def reparent_children(self, ie, target):
508
 
        for thing, child in ie.children.iteritems():
509
 
            trans_id = self.tt.trans_id_file_id(child.file_id)
510
 
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
511
 
 
512
411
    def write_modified(self, results):
513
412
        modified_hashes = {}
514
413
        for path in results.modified_paths:
619
518
                        "conflict": other_entry}
620
519
        trans_id = self.tt.trans_id_file_id(file_id)
621
520
        parent_id = winner_entry[parent_id_winner].parent_id
622
 
        if parent_id is not None:
623
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
624
 
            self.tt.adjust_path(winner_entry[name_winner].name, 
625
 
                                parent_trans_id, trans_id)
 
521
        parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
522
        self.tt.adjust_path(winner_entry[name_winner].name, parent_trans_id,
 
523
                            trans_id)
626
524
 
627
525
    def merge_contents(self, file_id):
628
526
        """Performa a merge on file_id contents."""
644
542
            parent_id = self.tt.final_parent(trans_id)
645
543
            if file_id in self.this_tree.inventory:
646
544
                self.tt.unversion_file(trans_id)
647
 
                if file_id in self.this_tree:
648
 
                    self.tt.delete_contents(trans_id)
 
545
                self.tt.delete_contents(trans_id)
649
546
            file_group = self._dump_conflicts(name, parent_id, file_id, 
650
547
                                              set_version=True)
651
548
            self._raw_conflicts.append(('contents conflict', file_group))
890
787
 
891
788
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
892
789
                 interesting_ids=None, pb=DummyProgress(), pp=None,
893
 
                 reprocess=False, change_reporter=None):
 
790
                 reprocess=False):
894
791
        self.this_revision_tree = self._get_revision_tree(this_tree)
895
792
        self.other_revision_tree = self._get_revision_tree(other_tree)
896
793
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
897
794
                                          base_tree, other_tree, 
898
795
                                          interesting_ids=interesting_ids, 
899
 
                                          pb=pb, pp=pp, reprocess=reprocess,
900
 
                                          change_reporter=change_reporter)
 
796
                                          pb=pb, pp=pp, reprocess=reprocess)
901
797
 
902
798
    def _get_revision_tree(self, tree):
903
799
        """Return a revision tree related to this tree.
972
868
        will be dumped, and a will be conflict noted.
973
869
        """
974
870
        import bzrlib.patch
975
 
        temp_dir = osutils.mkdtemp(prefix="bzr-")
 
871
        temp_dir = mkdtemp(prefix="bzr-")
976
872
        try:
977
873
            new_file = pathjoin(temp_dir, "new")
978
874
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
990
886
                name = self.tt.final_name(trans_id)
991
887
                parent_id = self.tt.final_parent(trans_id)
992
888
                self._dump_conflicts(name, parent_id, file_id)
993
 
                self._raw_conflicts.append(('text conflict', trans_id))
 
889
            self._raw_conflicts.append(('text conflict', trans_id))
994
890
        finally:
995
 
            osutils.rmtree(temp_dir)
 
891
            rmtree(temp_dir)
996
892
 
997
893
 
998
894
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
999
 
                backup_files=False,
1000
 
                merge_type=Merge3Merger,
1001
 
                interesting_ids=None,
1002
 
                show_base=False,
1003
 
                reprocess=False,
 
895
                backup_files=False, 
 
896
                merge_type=Merge3Merger, 
 
897
                interesting_ids=None, 
 
898
                show_base=False, 
 
899
                reprocess=False, 
1004
900
                other_rev_id=None,
1005
901
                interesting_files=None,
1006
902
                this_tree=None,
1007
 
                pb=DummyProgress(),
1008
 
                change_reporter=None):
 
903
                pb=DummyProgress()):
1009
904
    """Primary interface for merging. 
1010
905
 
1011
906
        typical use is probably 
1013
908
                     branch.get_revision_tree(base_revision))'
1014
909
        """
1015
910
    if this_tree is None:
1016
 
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1017
 
            "parameter as of bzrlib version 0.8.")
1018
 
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1019
 
                    pb=pb, change_reporter=change_reporter)
 
911
        warnings.warn("bzrlib.merge.merge_inner requires a this_tree parameter as of "
 
912
             "bzrlib version 0.8.",
 
913
             DeprecationWarning,
 
914
             stacklevel=2)
 
915
        this_tree = this_branch.bzrdir.open_workingtree()
 
916
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree, 
 
917
                    pb=pb)
1020
918
    merger.backup_files = backup_files
1021
919
    merger.merge_type = merge_type
1022
920
    merger.interesting_ids = interesting_ids
1031
929
    merger.other_basis = other_rev_id
1032
930
    return merger.do_merge()
1033
931
 
1034
 
def get_merge_type_registry():
1035
 
    """Merge type registry is in bzrlib.option to avoid circular imports.
1036
 
 
1037
 
    This method provides a sanctioned way to retrieve it.
1038
 
    """
1039
 
    from bzrlib import option
1040
 
    return option._merge_type_registry
 
932
 
 
933
merge_types = {     "merge3": (Merge3Merger, "Native diff3-style merge"), 
 
934
                     "diff3": (Diff3Merger,  "Merge using external diff3"),
 
935
                     'weave': (WeaveMerger, "Weave-based merge")
 
936
              }
 
937
 
 
938
 
 
939
def merge_type_help():
 
940
    templ = '%s%%7s: %%s' % (' '*12)
 
941
    lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
942
    return '\n'.join(lines)