~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-07 16:05:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060607160527-2b3649154d0e2e84
more code cleanup

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
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
17
17
 
18
18
import os
19
19
import errno
20
 
import warnings
 
20
from tempfile import mkdtemp
21
21
 
22
 
from bzrlib import (
23
 
    osutils,
24
 
    registry,
25
 
    )
 
22
import bzrlib
26
23
from bzrlib.branch import Branch
27
24
from bzrlib.conflicts import ConflictList, Conflict
 
25
from bzrlib.delta import compare_trees
28
26
from bzrlib.errors import (BzrCommandError,
29
27
                           BzrError,
30
28
                           NoCommonAncestor,
39
37
                           BinaryFile,
40
38
                           )
41
39
from bzrlib.merge3 import Merge3
42
 
from bzrlib.osutils import rename, pathjoin
 
40
import bzrlib.osutils
 
41
from bzrlib.osutils import rename, pathjoin, rmtree
43
42
from progress import DummyProgress, ProgressPhase
44
 
from bzrlib.revision import (is_ancestor, NULL_REVISION, ensure_null)
 
43
from bzrlib.revision import common_ancestor, is_ancestor, NULL_REVISION
 
44
from bzrlib.symbol_versioning import *
45
45
from bzrlib.textfile import check_text_lines
46
46
from bzrlib.trace import mutter, warning, note
47
47
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
48
 
                              FinalPaths, create_by_entry, unique_add,
49
 
                              ROOT_PARENT)
 
48
                              FinalPaths, create_by_entry, unique_add)
50
49
from bzrlib.versionedfile import WeaveMerge
51
 
from bzrlib import ui
 
50
import bzrlib.ui
52
51
 
53
52
# TODO: Report back as changes are merged in
54
53
 
55
54
def _get_tree(treespec, local_branch=None):
56
 
    from bzrlib import workingtree
57
55
    location, revno = treespec
 
56
    branch = Branch.open_containing(location)[0]
58
57
    if revno is None:
59
 
        tree = workingtree.WorkingTree.open_containing(location)[0]
60
 
        return tree.branch, tree
61
 
    branch = Branch.open_containing(location)[0]
62
 
    if revno == -1:
63
 
        revision_id = branch.last_revision()
 
58
        revision = None
 
59
    elif revno == -1:
 
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)
133
117
 
134
118
        if self.other_rev_id is None:
135
119
            other_basis_tree = self.revision_tree(self.other_basis)
136
 
            changes = other_basis_tree.changes_from(self.other_tree)
 
120
            changes = compare_trees(self.other_tree, other_basis_tree)
137
121
            if changes.has_changed():
138
122
                raise WorkingTreeNotRevision(self.this_tree)
139
 
            other_rev_id = self.other_basis
 
123
            other_rev_id = other_basis
140
124
            self.other_tree = other_basis_tree
141
125
 
142
126
    def file_revisions(self, file_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:
163
146
                raise BzrCommandError("Working tree has uncommitted changes.")
164
147
 
165
148
    def compare_basis(self):
166
 
        changes = self.this_tree.changes_from(self.this_tree.basis_tree())
 
149
        changes = compare_trees(self.this_tree, 
 
150
                                self.this_tree.basis_tree(), False)
167
151
        if not changes.has_changed():
168
152
            self.this_rev_id = self.this_basis
169
153
 
183
167
        interesting_ids = set()
184
168
        for path in file_list:
185
169
            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
170
            for tree in (self.this_tree, self.base_tree, self.other_tree):
191
 
                file_id = tree.path2id(path)
 
171
                file_id = tree.inventory.path2id(path)
192
172
                if file_id is not None:
193
173
                    interesting_ids.add(file_id)
194
174
                    found_id = True
201
181
            return
202
182
        if self.other_rev_id is None:
203
183
            return
204
 
        ancestry = set(self.this_branch.repository.get_ancestry(
205
 
            self.this_basis, topo_sorted=False))
 
184
        ancestry = self.this_branch.repository.get_ancestry(self.this_basis)
206
185
        if self.other_rev_id in ancestry:
207
186
            return
208
 
        self.this_tree.add_parent_tree((self.other_rev_id, self.other_tree))
 
187
        self.this_tree.add_pending_merge(self.other_rev_id)
209
188
 
210
189
    def set_other(self, other_revision):
211
 
        """Set the revision and tree to merge from.
212
 
 
213
 
        This sets the other_tree, other_rev_id, other_basis attributes.
214
 
 
215
 
        :param other_revision: The [path, revision] list to merge from.
216
 
        """
217
 
        self.other_branch, self.other_tree = _get_tree(other_revision,
 
190
        other_branch, self.other_tree = _get_tree(other_revision, 
218
191
                                                  self.this_branch)
219
192
        if other_revision[1] == -1:
220
 
            self.other_rev_id = self.other_branch.last_revision()
 
193
            self.other_rev_id = other_branch.last_revision()
221
194
            if self.other_rev_id is None:
222
 
                raise NoCommits(self.other_branch)
 
195
                raise NoCommits(other_branch)
223
196
            self.other_basis = self.other_rev_id
224
197
        elif other_revision[1] is not None:
225
 
            self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
 
198
            self.other_rev_id = other_branch.get_rev_id(other_revision[1])
226
199
            self.other_basis = self.other_rev_id
227
200
        else:
228
201
            self.other_rev_id = None
229
 
            self.other_basis = self.other_branch.last_revision()
 
202
            self.other_basis = other_branch.last_revision()
230
203
            if self.other_basis is None:
231
 
                raise NoCommits(self.other_branch)
232
 
        if self.other_branch.base != self.this_branch.base:
233
 
            self.this_branch.fetch(self.other_branch,
234
 
                                   last_revision=self.other_basis)
235
 
 
236
 
    def set_other_revision(self, revision_id, other_branch):
237
 
        """Set 'other' based on a branch and revision id
238
 
 
239
 
        :param revision_id: The revision to use for a tree
240
 
        :param other_branch: The branch containing this tree
241
 
        """
242
 
        self.other_rev_id = revision_id
243
 
        self.other_branch = other_branch
244
 
        self.this_branch.fetch(other_branch, self.other_rev_id)
245
 
        self.other_tree = self.revision_tree(revision_id)
246
 
        self.other_basis = revision_id
 
204
                raise NoCommits(other_branch)
 
205
        if other_branch.base != self.this_branch.base:
 
206
            self.this_branch.fetch(other_branch, last_revision=self.other_basis)
247
207
 
248
208
    def find_base(self):
249
209
        self.set_base([None, None])
250
210
 
251
211
    def set_base(self, base_revision):
252
 
        """Set the base revision to use for the merge.
253
 
 
254
 
        :param base_revision: A 2-list containing a path and revision number.
255
 
        """
256
212
        mutter("doing merge() with no base_revision specified")
257
213
        if base_revision == [None, None]:
258
214
            try:
259
 
                pb = ui.ui_factory.nested_progress_bar()
 
215
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
260
216
                try:
261
217
                    this_repo = self.this_branch.repository
262
 
                    graph = this_repo.get_graph()
263
 
                    revisions = [ensure_null(self.this_basis),
264
 
                                 ensure_null(self.other_basis)]
265
 
                    if NULL_REVISION in revisions:
266
 
                        self.base_rev_id = NULL_REVISION
267
 
                    else:
268
 
                        self.base_rev_id = graph.find_unique_lca(*revisions)
269
 
                        if self.base_rev_id == NULL_REVISION:
270
 
                            raise UnrelatedBranches()
 
218
                    self.base_rev_id = common_ancestor(self.this_basis, 
 
219
                                                       self.other_basis, 
 
220
                                                       this_repo, pb)
271
221
                finally:
272
222
                    pb.finished()
273
223
            except NoCommonAncestor:
274
224
                raise UnrelatedBranches()
275
 
            self.base_tree = _get_revid_tree_from_tree(self.this_tree,
276
 
                                                       self.base_rev_id,
277
 
                                                       None)
 
225
            self.base_tree = _get_revid_tree(self.this_branch, self.base_rev_id,
 
226
                                            None)
278
227
            self.base_is_ancestor = True
279
228
        else:
280
229
            base_branch, self.base_tree = _get_tree(base_revision)
291
240
                                                self.this_branch)
292
241
 
293
242
    def do_merge(self):
294
 
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
295
 
                  'other_tree': self.other_tree,
 
243
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree, 
 
244
                  'other_tree': self.other_tree, 
296
245
                  'interesting_ids': self.interesting_ids,
297
246
                  'pp': self.pp}
298
247
        if self.merge_type.requires_base:
307
256
        elif self.show_base:
308
257
            raise BzrError("Showing base is not supported for this"
309
258
                                  " merge type. %s" % self.merge_type)
310
 
        self.this_tree.lock_tree_write()
311
 
        if self.base_tree is not None:
312
 
            self.base_tree.lock_read()
313
 
        if self.other_tree is not None:
314
 
            self.other_tree.lock_read()
315
 
        try:
316
 
            merge = self.merge_type(pb=self._pb,
317
 
                                    change_reporter=self.change_reporter,
318
 
                                    **kwargs)
319
 
            if self.recurse == 'down':
320
 
                for path, file_id in self.this_tree.iter_references():
321
 
                    sub_tree = self.this_tree.get_nested_tree(file_id, path)
322
 
                    other_revision = self.other_tree.get_reference_revision(
323
 
                        file_id, path)
324
 
                    if  other_revision == sub_tree.last_revision():
325
 
                        continue
326
 
                    sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
327
 
                    sub_merge.merge_type = self.merge_type
328
 
                    relpath = self.this_tree.relpath(path)
329
 
                    other_branch = self.other_branch.reference_parent(file_id, relpath)
330
 
                    sub_merge.set_other_revision(other_revision, other_branch)
331
 
                    base_revision = self.base_tree.get_reference_revision(file_id)
332
 
                    sub_merge.base_tree = \
333
 
                        sub_tree.branch.repository.revision_tree(base_revision)
334
 
                    sub_merge.do_merge()
335
 
 
336
 
        finally:
337
 
            if self.other_tree is not None:
338
 
                self.other_tree.unlock()
339
 
            if self.base_tree is not None:
340
 
                self.base_tree.unlock()
341
 
            self.this_tree.unlock()
 
259
        merge = self.merge_type(pb=self._pb, **kwargs)
342
260
        if len(merge.cooked_conflicts) == 0:
343
261
            if not self.ignore_zero:
344
262
                note("All changes applied successfully.")
397
315
            else:
398
316
                parent = by_path[os.path.dirname(path)]
399
317
            abspath = pathjoin(self.this_tree.basedir, path)
400
 
            kind = osutils.file_kind(abspath)
 
318
            kind = bzrlib.osutils.file_kind(abspath)
401
319
            if file_id in self.base_tree.inventory:
402
320
                executable = getattr(self.base_tree.inventory[file_id], 'executable', False)
403
321
            else:
426
344
 
427
345
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
428
346
                 interesting_ids=None, reprocess=False, show_base=False,
429
 
                 pb=DummyProgress(), pp=None, change_reporter=None):
 
347
                 pb=DummyProgress(), pp=None):
430
348
        """Initialize the merger object and perform the merge."""
431
349
        object.__init__(self)
432
350
        self.this_tree = working_tree
433
 
        self.this_tree.lock_tree_write()
434
351
        self.base_tree = base_tree
435
 
        self.base_tree.lock_read()
436
352
        self.other_tree = other_tree
437
 
        self.other_tree.lock_read()
438
353
        self._raw_conflicts = []
439
354
        self.cooked_conflicts = []
440
355
        self.reprocess = reprocess
441
356
        self.show_base = show_base
442
357
        self.pb = pb
443
358
        self.pp = pp
444
 
        self.change_reporter = change_reporter
445
359
        if self.pp is None:
446
360
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
447
361
 
450
364
        else:
451
365
            all_ids = set(base_tree)
452
366
            all_ids.update(other_tree)
 
367
        working_tree.lock_write()
453
368
        self.tt = TreeTransform(working_tree, self.pb)
454
369
        try:
455
370
            self.pp.next_phase()
456
 
            child_pb = ui.ui_factory.nested_progress_bar()
 
371
            child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
457
372
            try:
458
373
                for num, file_id in enumerate(all_ids):
459
374
                    child_pb.update('Preparing file merge', num, len(all_ids))
462
377
                    self.merge_executable(file_id, file_status)
463
378
            finally:
464
379
                child_pb.finished()
465
 
            self.fix_root()
 
380
                
466
381
            self.pp.next_phase()
467
 
            child_pb = ui.ui_factory.nested_progress_bar()
 
382
            child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
468
383
            try:
469
384
                fs_conflicts = resolve_conflicts(self.tt, child_pb)
470
385
            finally:
471
386
                child_pb.finished()
472
 
            if change_reporter is not None:
473
 
                from bzrlib import delta
474
 
                delta.report_changes(self.tt._iter_changes(), change_reporter)
475
387
            self.cook_conflicts(fs_conflicts)
476
388
            for conflict in self.cooked_conflicts:
477
389
                warning(conflict)
479
391
            results = self.tt.apply()
480
392
            self.write_modified(results)
481
393
            try:
482
 
                working_tree.add_conflicts(self.cooked_conflicts)
 
394
                working_tree.set_conflicts(ConflictList(self.cooked_conflicts))
483
395
            except UnsupportedOperation:
484
396
                pass
485
397
        finally:
486
 
            self.tt.finalize()
487
 
            self.other_tree.unlock()
488
 
            self.base_tree.unlock()
489
 
            self.this_tree.unlock()
 
398
            try:
 
399
                self.tt.finalize()
 
400
            except:
 
401
                pass
 
402
            working_tree.unlock()
490
403
            self.pb.clear()
491
404
 
492
 
    def fix_root(self):
493
 
        try:
494
 
            self.tt.final_kind(self.tt.root)
495
 
        except NoSuchFile:
496
 
            self.tt.cancel_deletion(self.tt.root)
497
 
        if self.tt.final_file_id(self.tt.root) is None:
498
 
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
499
 
                                 self.tt.root)
500
 
        if self.other_tree.inventory.root is None:
501
 
            return
502
 
        other_root_file_id = self.other_tree.inventory.root.file_id
503
 
        other_root = self.tt.trans_id_file_id(other_root_file_id)
504
 
        if other_root == self.tt.root:
505
 
            return
506
 
        try:
507
 
            self.tt.final_kind(other_root)
508
 
        except NoSuchFile:
509
 
            return
510
 
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
511
 
        self.tt.cancel_creation(other_root)
512
 
        self.tt.cancel_versioning(other_root)
513
 
 
514
 
    def reparent_children(self, ie, target):
515
 
        for thing, child in ie.children.iteritems():
516
 
            trans_id = self.tt.trans_id_file_id(child.file_id)
517
 
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
518
 
 
519
405
    def write_modified(self, results):
520
406
        modified_hashes = {}
521
407
        for path in results.modified_paths:
626
512
                        "conflict": other_entry}
627
513
        trans_id = self.tt.trans_id_file_id(file_id)
628
514
        parent_id = winner_entry[parent_id_winner].parent_id
629
 
        if parent_id is not None:
630
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
631
 
            self.tt.adjust_path(winner_entry[name_winner].name, 
632
 
                                parent_trans_id, trans_id)
 
515
        parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
516
        self.tt.adjust_path(winner_entry[name_winner].name, parent_trans_id,
 
517
                            trans_id)
633
518
 
634
519
    def merge_contents(self, file_id):
635
520
        """Performa a merge on file_id contents."""
637
522
            if file_id not in tree:
638
523
                return (None, None)
639
524
            kind = tree.kind(file_id)
 
525
            if kind == "root_directory":
 
526
                kind = "directory"
640
527
            if kind == "file":
641
528
                contents = tree.get_file_sha1(file_id)
642
529
            elif kind == "symlink":
651
538
            parent_id = self.tt.final_parent(trans_id)
652
539
            if file_id in self.this_tree.inventory:
653
540
                self.tt.unversion_file(trans_id)
654
 
                if file_id in self.this_tree:
655
 
                    self.tt.delete_contents(trans_id)
 
541
                self.tt.delete_contents(trans_id)
656
542
            file_group = self._dump_conflicts(name, parent_id, file_id, 
657
543
                                              set_version=True)
658
544
            self._raw_conflicts.append(('contents conflict', file_group))
808
694
        if winner == "conflict":
809
695
        # There must be a None in here, if we have a conflict, but we
810
696
        # need executability since file status was not deleted.
811
 
            if self.executable(self.other_tree, file_id) is None:
 
697
            if self.other_tree.is_executable(file_id) is None:
812
698
                winner = "this"
813
699
            else:
814
700
                winner = "other"
877
763
            except KeyError:
878
764
                this_name = other_name = self.tt.final_name(trans_id)
879
765
            other_path = fp.get_path(trans_id)
880
 
            if this_parent is not None and this_name is not None:
 
766
            if this_parent is not None:
881
767
                this_parent_path = \
882
768
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
883
769
                this_path = pathjoin(this_parent_path, this_name)
897
783
 
898
784
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
899
785
                 interesting_ids=None, pb=DummyProgress(), pp=None,
900
 
                 reprocess=False, change_reporter=None):
 
786
                 reprocess=False):
901
787
        self.this_revision_tree = self._get_revision_tree(this_tree)
902
788
        self.other_revision_tree = self._get_revision_tree(other_tree)
903
789
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
904
790
                                          base_tree, other_tree, 
905
791
                                          interesting_ids=interesting_ids, 
906
 
                                          pb=pb, pp=pp, reprocess=reprocess,
907
 
                                          change_reporter=change_reporter)
 
792
                                          pb=pb, pp=pp, reprocess=reprocess)
908
793
 
909
794
    def _get_revision_tree(self, tree):
910
 
        """Return a revision tree related to this tree.
 
795
        """Return a revision tree releated to this tree.
911
796
        If the tree is a WorkingTree, the basis will be returned.
912
797
        """
913
798
        if getattr(tree, 'get_weave', False) is False:
961
846
 
962
847
class Diff3Merger(Merge3Merger):
963
848
    """Three-way merger using external diff3 for text merging"""
964
 
 
965
849
    def dump_file(self, temp_dir, name, tree, file_id):
966
850
        out_path = pathjoin(temp_dir, name)
967
 
        out_file = open(out_path, "wb")
968
 
        try:
969
 
            in_file = tree.get_file(file_id)
970
 
            for line in in_file:
971
 
                out_file.write(line)
972
 
        finally:
973
 
            out_file.close()
 
851
        out_file = file(out_path, "wb")
 
852
        in_file = tree.get_file(file_id)
 
853
        for line in in_file:
 
854
            out_file.write(line)
974
855
        return out_path
975
856
 
976
857
    def text_merge(self, file_id, trans_id):
979
860
        will be dumped, and a will be conflict noted.
980
861
        """
981
862
        import bzrlib.patch
982
 
        temp_dir = osutils.mkdtemp(prefix="bzr-")
 
863
        temp_dir = mkdtemp(prefix="bzr-")
983
864
        try:
984
865
            new_file = pathjoin(temp_dir, "new")
985
866
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
988
869
            status = bzrlib.patch.diff3(new_file, this, base, other)
989
870
            if status not in (0, 1):
990
871
                raise BzrError("Unhandled diff3 exit code")
991
 
            f = open(new_file, 'rb')
992
 
            try:
993
 
                self.tt.create_file(f, trans_id)
994
 
            finally:
995
 
                f.close()
 
872
            self.tt.create_file(file(new_file, "rb"), trans_id)
996
873
            if status == 1:
997
874
                name = self.tt.final_name(trans_id)
998
875
                parent_id = self.tt.final_parent(trans_id)
999
876
                self._dump_conflicts(name, parent_id, file_id)
1000
 
                self._raw_conflicts.append(('text conflict', trans_id))
 
877
            self._raw_conflicts.append(('text conflict', trans_id))
1001
878
        finally:
1002
 
            osutils.rmtree(temp_dir)
 
879
            rmtree(temp_dir)
1003
880
 
1004
881
 
1005
882
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1006
 
                backup_files=False,
1007
 
                merge_type=Merge3Merger,
1008
 
                interesting_ids=None,
1009
 
                show_base=False,
1010
 
                reprocess=False,
 
883
                backup_files=False, 
 
884
                merge_type=Merge3Merger, 
 
885
                interesting_ids=None, 
 
886
                show_base=False, 
 
887
                reprocess=False, 
1011
888
                other_rev_id=None,
1012
889
                interesting_files=None,
1013
890
                this_tree=None,
1014
 
                pb=DummyProgress(),
1015
 
                change_reporter=None):
 
891
                pb=DummyProgress()):
1016
892
    """Primary interface for merging. 
1017
893
 
1018
894
        typical use is probably 
1020
896
                     branch.get_revision_tree(base_revision))'
1021
897
        """
1022
898
    if this_tree is None:
1023
 
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1024
 
            "parameter as of bzrlib version 0.8.")
1025
 
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1026
 
                    pb=pb, change_reporter=change_reporter)
 
899
        warn("bzrlib.merge.merge_inner requires a this_tree parameter as of "
 
900
             "bzrlib version 0.8.",
 
901
             DeprecationWarning,
 
902
             stacklevel=2)
 
903
        this_tree = this_branch.bzrdir.open_workingtree()
 
904
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree, 
 
905
                    pb=pb)
1027
906
    merger.backup_files = backup_files
1028
907
    merger.merge_type = merge_type
1029
908
    merger.interesting_ids = interesting_ids
1032
911
        assert not interesting_ids, ('Only supply interesting_ids'
1033
912
                                     ' or interesting_files')
1034
913
        merger._set_interesting_files(interesting_files)
1035
 
    merger.show_base = show_base
 
914
    merger.show_base = show_base 
1036
915
    merger.reprocess = reprocess
1037
916
    merger.other_rev_id = other_rev_id
1038
917
    merger.other_basis = other_rev_id
1039
918
    return merger.do_merge()
1040
919
 
1041
 
def get_merge_type_registry():
1042
 
    """Merge type registry is in bzrlib.option to avoid circular imports.
1043
 
 
1044
 
    This method provides a sanctioned way to retrieve it.
1045
 
    """
1046
 
    from bzrlib import option
1047
 
    return option._merge_type_registry
 
920
 
 
921
merge_types = {     "merge3": (Merge3Merger, "Native diff3-style merge"), 
 
922
                     "diff3": (Diff3Merger,  "Merge using external diff3"),
 
923
                     'weave': (WeaveMerger, "Weave-based merge")
 
924
              }
 
925
 
 
926
 
 
927
def merge_type_help():
 
928
    templ = '%s%%7s: %%s' % (' '*12)
 
929
    lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
930
    return '\n'.join(lines)