~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-09-25 18:42:17 UTC
  • mfrom: (2039.1.2 progress-cleanup)
  • Revision ID: pqm@pqm.ubuntu.com-20060925184217-fd144de117df49c3
cleanup progress properly when interrupted during fetch (#54000)

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
 
87
84
 
88
85
 
89
86
class Merger(object):
90
 
    def __init__(self, this_branch, other_tree=None, base_tree=None,
91
 
                 this_tree=None, pb=DummyProgress(), change_reporter=None):
 
87
    def __init__(self, this_branch, other_tree=None, base_tree=None, 
 
88
                 this_tree=None, pb=DummyProgress()):
92
89
        object.__init__(self)
93
90
        assert this_tree is not None, "this_tree is required"
94
91
        self.this_branch = this_branch
104
101
        self.interesting_ids = None
105
102
        self.show_base = False
106
103
        self.reprocess = False
107
 
        self._pb = pb
 
104
        self._pb = pb 
108
105
        self.pp = None
109
 
        self.change_reporter = change_reporter
 
106
 
110
107
 
111
108
    def revision_tree(self, revision_id):
112
109
        return self.this_branch.repository.revision_tree(revision_id)
142
139
 
143
140
    def check_basis(self, check_clean, require_commits=True):
144
141
        if self.this_basis is None and require_commits is True:
145
 
            raise BzrCommandError("This branch has no commits."
146
 
                                  " (perhaps you would prefer 'bzr pull')")
 
142
            raise BzrCommandError("This branch has no commits")
147
143
        if check_clean:
148
144
            self.compare_basis()
149
145
            if self.this_basis != self.this_rev_id:
170
166
        interesting_ids = set()
171
167
        for path in file_list:
172
168
            found_id = False
173
 
            # TODO: jam 20070226 The trees are not locked at this time,
174
 
            #       wouldn't it make merge faster if it locks everything in the
175
 
            #       beginning? It locks at do_merge time, but this happens
176
 
            #       before that.
177
169
            for tree in (self.this_tree, self.base_tree, self.other_tree):
178
 
                file_id = tree.path2id(path)
 
170
                file_id = tree.inventory.path2id(path)
179
171
                if file_id is not None:
180
172
                    interesting_ids.add(file_id)
181
173
                    found_id = True
229
221
        mutter("doing merge() with no base_revision specified")
230
222
        if base_revision == [None, None]:
231
223
            try:
232
 
                pb = ui.ui_factory.nested_progress_bar()
 
224
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
233
225
                try:
234
226
                    this_repo = self.this_branch.repository
235
227
                    self.base_rev_id = common_ancestor(self.this_basis, 
257
249
                                                self.this_branch)
258
250
 
259
251
    def do_merge(self):
260
 
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
261
 
                  'other_tree': self.other_tree,
 
252
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree, 
 
253
                  'other_tree': self.other_tree, 
262
254
                  'interesting_ids': self.interesting_ids,
263
255
                  'pp': self.pp}
264
256
        if self.merge_type.requires_base:
273
265
        elif self.show_base:
274
266
            raise BzrError("Showing base is not supported for this"
275
267
                                  " merge type. %s" % self.merge_type)
276
 
        self.this_tree.lock_tree_write()
277
 
        if self.base_tree is not None:
278
 
            self.base_tree.lock_read()
279
 
        if self.other_tree is not None:
280
 
            self.other_tree.lock_read()
281
 
        try:
282
 
            merge = self.merge_type(pb=self._pb,
283
 
                                    change_reporter=self.change_reporter,
284
 
                                    **kwargs)
285
 
        finally:
286
 
            if self.other_tree is not None:
287
 
                self.other_tree.unlock()
288
 
            if self.base_tree is not None:
289
 
                self.base_tree.unlock()
290
 
            self.this_tree.unlock()
 
268
        merge = self.merge_type(pb=self._pb, **kwargs)
291
269
        if len(merge.cooked_conflicts) == 0:
292
270
            if not self.ignore_zero:
293
271
                note("All changes applied successfully.")
346
324
            else:
347
325
                parent = by_path[os.path.dirname(path)]
348
326
            abspath = pathjoin(self.this_tree.basedir, path)
349
 
            kind = osutils.file_kind(abspath)
 
327
            kind = bzrlib.osutils.file_kind(abspath)
350
328
            if file_id in self.base_tree.inventory:
351
329
                executable = getattr(self.base_tree.inventory[file_id], 'executable', False)
352
330
            else:
375
353
 
376
354
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
377
355
                 interesting_ids=None, reprocess=False, show_base=False,
378
 
                 pb=DummyProgress(), pp=None, change_reporter=None):
 
356
                 pb=DummyProgress(), pp=None):
379
357
        """Initialize the merger object and perform the merge."""
380
358
        object.__init__(self)
381
359
        self.this_tree = working_tree
382
 
        self.this_tree.lock_tree_write()
383
360
        self.base_tree = base_tree
384
 
        self.base_tree.lock_read()
385
361
        self.other_tree = other_tree
386
 
        self.other_tree.lock_read()
387
362
        self._raw_conflicts = []
388
363
        self.cooked_conflicts = []
389
364
        self.reprocess = reprocess
390
365
        self.show_base = show_base
391
366
        self.pb = pb
392
367
        self.pp = pp
393
 
        self.change_reporter = change_reporter
394
368
        if self.pp is None:
395
369
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
396
370
 
399
373
        else:
400
374
            all_ids = set(base_tree)
401
375
            all_ids.update(other_tree)
 
376
        working_tree.lock_write()
402
377
        self.tt = TreeTransform(working_tree, self.pb)
403
378
        try:
404
379
            self.pp.next_phase()
411
386
                    self.merge_executable(file_id, file_status)
412
387
            finally:
413
388
                child_pb.finished()
414
 
            self.fix_root()
 
389
                
415
390
            self.pp.next_phase()
416
391
            child_pb = ui.ui_factory.nested_progress_bar()
417
392
            try:
418
393
                fs_conflicts = resolve_conflicts(self.tt, child_pb)
419
394
            finally:
420
395
                child_pb.finished()
421
 
            if change_reporter is not None:
422
 
                from bzrlib import delta
423
 
                delta.report_changes(self.tt._iter_changes(), change_reporter)
424
396
            self.cook_conflicts(fs_conflicts)
425
397
            for conflict in self.cooked_conflicts:
426
398
                warning(conflict)
433
405
                pass
434
406
        finally:
435
407
            self.tt.finalize()
436
 
            self.other_tree.unlock()
437
 
            self.base_tree.unlock()
438
 
            self.this_tree.unlock()
 
408
            working_tree.unlock()
439
409
            self.pb.clear()
440
410
 
441
 
    def fix_root(self):
442
 
        try:
443
 
            self.tt.final_kind(self.tt.root)
444
 
        except NoSuchFile:
445
 
            self.tt.cancel_deletion(self.tt.root)
446
 
        if self.tt.final_file_id(self.tt.root) is None:
447
 
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
448
 
                                 self.tt.root)
449
 
        if self.other_tree.inventory.root is None:
450
 
            return
451
 
        other_root_file_id = self.other_tree.inventory.root.file_id
452
 
        other_root = self.tt.trans_id_file_id(other_root_file_id)
453
 
        if other_root == self.tt.root:
454
 
            return
455
 
        try:
456
 
            self.tt.final_kind(other_root)
457
 
        except NoSuchFile:
458
 
            return
459
 
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
460
 
        self.tt.cancel_creation(other_root)
461
 
        self.tt.cancel_versioning(other_root)
462
 
 
463
 
    def reparent_children(self, ie, target):
464
 
        for thing, child in ie.children.iteritems():
465
 
            trans_id = self.tt.trans_id_file_id(child.file_id)
466
 
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
467
 
 
468
411
    def write_modified(self, results):
469
412
        modified_hashes = {}
470
413
        for path in results.modified_paths:
575
518
                        "conflict": other_entry}
576
519
        trans_id = self.tt.trans_id_file_id(file_id)
577
520
        parent_id = winner_entry[parent_id_winner].parent_id
578
 
        if parent_id is not None:
579
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
580
 
            self.tt.adjust_path(winner_entry[name_winner].name, 
581
 
                                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)
582
524
 
583
525
    def merge_contents(self, file_id):
584
526
        """Performa a merge on file_id contents."""
600
542
            parent_id = self.tt.final_parent(trans_id)
601
543
            if file_id in self.this_tree.inventory:
602
544
                self.tt.unversion_file(trans_id)
603
 
                if file_id in self.this_tree:
604
 
                    self.tt.delete_contents(trans_id)
 
545
                self.tt.delete_contents(trans_id)
605
546
            file_group = self._dump_conflicts(name, parent_id, file_id, 
606
547
                                              set_version=True)
607
548
            self._raw_conflicts.append(('contents conflict', file_group))
846
787
 
847
788
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
848
789
                 interesting_ids=None, pb=DummyProgress(), pp=None,
849
 
                 reprocess=False, change_reporter=None):
 
790
                 reprocess=False):
850
791
        self.this_revision_tree = self._get_revision_tree(this_tree)
851
792
        self.other_revision_tree = self._get_revision_tree(other_tree)
852
793
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
853
794
                                          base_tree, other_tree, 
854
795
                                          interesting_ids=interesting_ids, 
855
 
                                          pb=pb, pp=pp, reprocess=reprocess,
856
 
                                          change_reporter=change_reporter)
 
796
                                          pb=pb, pp=pp, reprocess=reprocess)
857
797
 
858
798
    def _get_revision_tree(self, tree):
859
799
        """Return a revision tree related to this tree.
928
868
        will be dumped, and a will be conflict noted.
929
869
        """
930
870
        import bzrlib.patch
931
 
        temp_dir = osutils.mkdtemp(prefix="bzr-")
 
871
        temp_dir = mkdtemp(prefix="bzr-")
932
872
        try:
933
873
            new_file = pathjoin(temp_dir, "new")
934
874
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
946
886
                name = self.tt.final_name(trans_id)
947
887
                parent_id = self.tt.final_parent(trans_id)
948
888
                self._dump_conflicts(name, parent_id, file_id)
949
 
                self._raw_conflicts.append(('text conflict', trans_id))
 
889
            self._raw_conflicts.append(('text conflict', trans_id))
950
890
        finally:
951
 
            osutils.rmtree(temp_dir)
 
891
            rmtree(temp_dir)
952
892
 
953
893
 
954
894
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
955
 
                backup_files=False,
956
 
                merge_type=Merge3Merger,
957
 
                interesting_ids=None,
958
 
                show_base=False,
959
 
                reprocess=False,
 
895
                backup_files=False, 
 
896
                merge_type=Merge3Merger, 
 
897
                interesting_ids=None, 
 
898
                show_base=False, 
 
899
                reprocess=False, 
960
900
                other_rev_id=None,
961
901
                interesting_files=None,
962
902
                this_tree=None,
963
 
                pb=DummyProgress(),
964
 
                change_reporter=None):
 
903
                pb=DummyProgress()):
965
904
    """Primary interface for merging. 
966
905
 
967
906
        typical use is probably 
974
913
             DeprecationWarning,
975
914
             stacklevel=2)
976
915
        this_tree = this_branch.bzrdir.open_workingtree()
977
 
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
978
 
                    pb=pb, change_reporter=change_reporter)
 
916
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree, 
 
917
                    pb=pb)
979
918
    merger.backup_files = backup_files
980
919
    merger.merge_type = merge_type
981
920
    merger.interesting_ids = interesting_ids
990
929
    merger.other_basis = other_rev_id
991
930
    return merger.do_merge()
992
931
 
993
 
def get_merge_type_registry():
994
 
    """Merge type registry is in bzrlib.option to avoid circular imports.
995
 
 
996
 
    This method provides a sanctioned way to retrieve it.
997
 
    """
998
 
    from bzrlib import option
999
 
    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)