~bzr-pqm/bzr/bzr.dev

1110 by Martin Pool
- merge aaron's merge improvements:
1
# Copyright (C) 2005 Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
1185.1.2 by Martin Pool
- merge various windows and other fixes from Ollie Rutherfurd
18
import os
1113 by Martin Pool
- fix is_ancestor import problem in merge
19
import tempfile
20
import shutil
21
import errno
22
23
import bzrlib.osutils
24
import bzrlib.revision
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
25
from bzrlib.merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
1185.12.83 by Aaron Bentley
Preliminary weave merge support
26
from bzrlib.merge_core import WeaveMerge
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
27
from bzrlib.changeset import generate_changeset, ExceptionConflictHandler
1185.10.8 by Aaron Bentley
Conflict handling where OTHER is deleted
28
from bzrlib.changeset import Inventory, Diff3Merge, ReplaceContents
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
29
from bzrlib.branch import Branch
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
30
from bzrlib.errors import (BzrCommandError,
31
                           UnrelatedBranches,
32
                           NoCommonAncestor,
33
                           NoCommits,
34
                           WorkingTreeNotRevision,
35
                           NotBranchError,
1185.33.27 by Martin Pool
[merge] much integrated work from robert and john
36
                           NotVersionedError,
37
                           BzrError)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
38
from bzrlib.delta import compare_trees
1185.12.17 by Aaron Bentley
Killed 0 conflicts encountered in selftest
39
from bzrlib.trace import mutter, warning, note
1390 by Robert Collins
pair programming worx... merge integration and weave
40
from bzrlib.fetch import greedy_fetch, fetch
1185.12.98 by Aaron Bentley
Support for forcing merges of unrelated trees
41
from bzrlib.revision import is_ancestor, NULL_REVISION
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
42
from bzrlib.osutils import rename
1390 by Robert Collins
pair programming worx... merge integration and weave
43
from bzrlib.revision import common_ancestor, MultipleRevisionSources
44
from bzrlib.errors import NoSuchRevision
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
45
1185.16.92 by mbp at sourcefrog
API docs for merge
46
# TODO: Report back as changes are merged in
47
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
48
# TODO: build_working_dir can be built on something simpler than merge()
49
50
# FIXME: merge() parameters seem oriented towards the command line
1185.12.17 by Aaron Bentley
Killed 0 conflicts encountered in selftest
51
# NOTABUG: merge is a helper for commandline functions.  merge_inner is the
52
#          the core functionality.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
53
54
# comments from abentley on irc: merge happens in two stages, each
55
# of which generates a changeset object
56
57
# stage 1: generate OLD->OTHER,
58
# stage 2: use MINE and OLD->OTHER to generate MINE -> RESULT
59
493 by Martin Pool
- Merge aaron's merge command
60
class MergeConflictHandler(ExceptionConflictHandler):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
61
    """Handle conflicts encountered while merging.
62
63
    This subclasses ExceptionConflictHandler, so that any types of
64
    conflict that are not explicitly handled cause an exception and
65
    terminate the merge.
66
    """
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
67
    def __init__(self, this_tree, base_tree, other_tree, ignore_zero=False):
974.1.83 by Aaron Bentley
Removed unused dir parameter from ExceptionConflictHandler
68
        ExceptionConflictHandler.__init__(self)
622 by Martin Pool
Updated merge patch from Aaron
69
        self.conflicts = 0
70
        self.ignore_zero = ignore_zero
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
71
        self.this_tree = this_tree
72
        self.base_tree = base_tree
73
        self.other_tree = other_tree
622 by Martin Pool
Updated merge patch from Aaron
74
493 by Martin Pool
- Merge aaron's merge command
75
    def copy(self, source, dest):
76
        """Copy the text and mode of a file
77
        :param source: The path of the file to copy
78
        :param dest: The distination file to create
79
        """
80
        s_file = file(source, "rb")
81
        d_file = file(dest, "wb")
82
        for line in s_file:
83
            d_file.write(line)
84
        os.chmod(dest, 0777 & os.stat(source).st_mode)
85
974.1.23 by Aaron Bentley
Avoided unnecessary temp files
86
    def dump(self, lines, dest):
87
        """Copy the text and mode of a file
88
        :param source: The path of the file to copy
89
        :param dest: The distination file to create
90
        """
91
        d_file = file(dest, "wb")
92
        for line in lines:
93
            d_file.write(line)
94
1185.12.103 by Aaron Bentley
Fixed add_suffix when renaming tree version to foo.THIS
95
    def add_suffix(self, name, suffix, last_new_name=None, fix_inventory=True):
493 by Martin Pool
- Merge aaron's merge command
96
        """Rename a file to append a suffix.  If the new name exists, the
97
        suffix is added repeatedly until a non-existant name is found
98
99
        :param name: The path of the file
100
        :param suffix: The suffix to append
101
        :param last_new_name: (used for recursive calls) the last name tried
102
        """
103
        if last_new_name is None:
104
            last_new_name = name
105
        new_name = last_new_name+suffix
106
        try:
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
107
            rename(name, new_name)
1185.12.103 by Aaron Bentley
Fixed add_suffix when renaming tree version to foo.THIS
108
            if fix_inventory is True:
109
                try:
110
                    relpath = self.this_tree.relpath(name)
111
                except NotBranchError:
112
                    relpath = None
113
                if relpath is not None:
114
                    file_id = self.this_tree.path2id(relpath)
115
                    if file_id is not None:
116
                        new_path = self.this_tree.relpath(new_name)
117
                        rename(new_name, name)
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
118
                        self.this_tree.rename_one(relpath, new_path)
1185.12.103 by Aaron Bentley
Fixed add_suffix when renaming tree version to foo.THIS
119
                        assert self.this_tree.id2path(file_id) == new_path
493 by Martin Pool
- Merge aaron's merge command
120
        except OSError, e:
121
            if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY:
122
                raise
1185.12.103 by Aaron Bentley
Fixed add_suffix when renaming tree version to foo.THIS
123
            return self.add_suffix(name, suffix, last_new_name=new_name, 
124
                                   fix_inventory=fix_inventory)
1185.12.99 by Aaron Bentley
Handled conflicts with versioned files better
125
        return new_name
622 by Martin Pool
Updated merge patch from Aaron
126
127
    def conflict(self, text):
128
        warning(text)
129
        self.conflicts += 1
130
        
493 by Martin Pool
- Merge aaron's merge command
131
974.1.23 by Aaron Bentley
Avoided unnecessary temp files
132
    def merge_conflict(self, new_file, this_path, base_lines, other_lines):
493 by Martin Pool
- Merge aaron's merge command
133
        """
134
        Handle diff3 conflicts by producing a .THIS, .BASE and .OTHER.  The
135
        main file will be a version with diff3 conflicts.
136
        :param new_file: Path to the output file with diff3 markers
137
        :param this_path: Path to the file text for the THIS tree
138
        :param base_path: Path to the file text for the BASE tree
139
        :param other_path: Path to the file text for the OTHER tree
140
        """
1185.12.103 by Aaron Bentley
Fixed add_suffix when renaming tree version to foo.THIS
141
        self.add_suffix(this_path, ".THIS", fix_inventory=False)
974.1.23 by Aaron Bentley
Avoided unnecessary temp files
142
        self.dump(base_lines, this_path+".BASE")
143
        self.dump(other_lines, this_path+".OTHER")
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
144
        rename(new_file, this_path)
622 by Martin Pool
Updated merge patch from Aaron
145
        self.conflict("Diff3 conflict encountered in %s" % this_path)
493 by Martin Pool
- Merge aaron's merge command
146
1185.12.85 by Aaron Bentley
Added conflict handling for weave merges
147
    def weave_merge_conflict(self, filename, weave, other_i, out_file):
148
        """
149
        Handle weave conflicts by producing a .THIS, and .OTHER.  The
150
        main file will be a version with diff3-style conflicts.
151
        """
1185.35.7 by Aaron Bentley
Fixed weave conflict handling
152
        self.add_suffix(filename, ".THIS", fix_inventory=False)
1185.12.85 by Aaron Bentley
Added conflict handling for weave merges
153
        out_file.commit()
154
        self.dump(weave.get_iter(other_i), filename+".OTHER")
155
        self.conflict("Text conflict encountered in %s" % filename)
156
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
157
    def new_contents_conflict(self, filename, other_contents):
158
        """Conflicting contents for newly added file."""
1185.12.66 by Aaron Bentley
committed fixes for new file conflict
159
        other_contents(filename + ".OTHER", self, False)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
160
        self.conflict("Conflict in newly added file %s" % filename)
161
    
162
493 by Martin Pool
- Merge aaron's merge command
163
    def target_exists(self, entry, target, old_path):
164
        """Handle the case when the target file or dir exists"""
622 by Martin Pool
Updated merge patch from Aaron
165
        moved_path = self.add_suffix(target, ".moved")
166
        self.conflict("Moved existing %s to %s" % (target, moved_path))
167
850 by Martin Pool
- Merge merge updates from aaron
168
    def rmdir_non_empty(self, filename):
169
        """Handle the case where the dir to be removed still has contents"""
170
        self.conflict("Directory %s not removed because it is not empty"\
171
            % filename)
172
        return "skip"
173
1185.10.8 by Aaron Bentley
Conflict handling where OTHER is deleted
174
    def rem_contents_conflict(self, filename, this_contents, base_contents):
175
        base_contents(filename+".BASE", self, False)
176
        this_contents(filename+".THIS", self, False)
177
        return ReplaceContents(this_contents, None)
178
1185.1.50 by Robert Collins
merge from newformat stuff and upgrade
179
    def rem_contents_conflict(self, filename, this_contents, base_contents):
180
        base_contents(filename+".BASE", self, False)
181
        this_contents(filename+".THIS", self, False)
1185.10.9 by Aaron Bentley
Added conflict message
182
        self.conflict("Other branch deleted locally modified file %s" %
183
                      filename)
1185.10.8 by Aaron Bentley
Conflict handling where OTHER is deleted
184
        return ReplaceContents(this_contents, None)
185
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
186
    def abs_this_path(self, file_id):
187
        """Return the absolute path for a file_id in the this tree."""
1185.12.40 by abentley
Got even closer to standard Tree interface
188
        return self.this_tree.id2abspath(file_id)
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
189
190
    def add_missing_parents(self, file_id, tree):
191
        """If some of the parents for file_id are missing, add them."""
1185.12.40 by abentley
Got even closer to standard Tree interface
192
        entry = tree.inventory[file_id]
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
193
        if entry.parent_id not in self.this_tree:
194
            return self.create_all_missing(entry.parent_id, tree)
195
        else:
196
            return self.abs_this_path(entry.parent_id)
197
198
    def create_all_missing(self, file_id, tree):
199
        """Add contents for a file_id and all its parents to a tree."""
1185.12.40 by abentley
Got even closer to standard Tree interface
200
        entry = tree.inventory[file_id]
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
201
        if entry.parent_id is not None and entry.parent_id not in self.this_tree:
202
            abspath = self.create_all_missing(entry.parent_id, tree)
203
        else:
204
            abspath = self.abs_this_path(entry.parent_id)
205
        entry_path = os.path.join(abspath, entry.name)
206
        if not os.path.isdir(entry_path):
207
            self.create(file_id, entry_path, tree)
208
        return entry_path
209
210
    def create(self, file_id, path, tree, reverse=False):
211
        """Uses tree data to create a filesystem object for the file_id"""
1185.12.30 by Aaron Bentley
Moved all fooCreate into get_contents
212
        from changeset import get_contents
213
        get_contents(tree, file_id)(path, self, reverse)
1185.10.10 by Aaron Bentley
Handled modified files missing from THIS
214
215
    def missing_for_merge(self, file_id, other_path):
216
        """The file_id doesn't exist in THIS, but does in OTHER and BASE"""
217
        self.conflict("Other branch modified locally deleted file %s" %
218
                      other_path)
219
        parent_dir = self.add_missing_parents(file_id, self.other_tree)
220
        stem = os.path.join(parent_dir, os.path.basename(other_path))
221
        self.create(file_id, stem+".OTHER", self.other_tree)
222
        self.create(file_id, stem+".BASE", self.base_tree)
223
1185.12.33 by Aaron Bentley
Fixed symlink reverting
224
    def threeway_contents_conflict(filename, this_contents, base_contents,
225
                                   other_contents):
226
        self.conflict("Three-way conflict merging %s" % filename)
227
622 by Martin Pool
Updated merge patch from Aaron
228
    def finalize(self):
229
        if not self.ignore_zero:
1185.33.44 by Martin Pool
[patch] show number of revisions pushed/pulled/merged (Robey Pointer)
230
            note("%d conflicts encountered." % self.conflicts)
493 by Martin Pool
- Merge aaron's merge command
231
            
1185.12.41 by abentley
Got rid of MergeAdapterTree
232
def get_tree(treespec, local_branch=None):
622 by Martin Pool
Updated merge patch from Aaron
233
    location, revno = treespec
1442.1.64 by Robert Collins
Branch.open_containing now returns a tuple (Branch, relative-path).
234
    branch = Branch.open_containing(location)[0]
493 by Martin Pool
- Merge aaron's merge command
235
    if revno is None:
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
236
        revision = None
237
    elif revno == -1:
1241 by Martin Pool
- rename last_patch to last_revision
238
        revision = branch.last_revision()
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
239
    else:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
240
        revision = branch.get_rev_id(revno)
1185.12.98 by Aaron Bentley
Support for forcing merges of unrelated trees
241
        if revision is None:
242
            revision = NULL_REVISION
1185.12.41 by abentley
Got rid of MergeAdapterTree
243
    return branch, get_revid_tree(branch, revision, local_branch)
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
244
1185.12.41 by abentley
Got rid of MergeAdapterTree
245
def get_revid_tree(branch, revision, local_branch):
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
246
    if revision is None:
1185.12.39 by abentley
Propogated has_or_had_id to Tree
247
        base_tree = branch.working_tree()
493 by Martin Pool
- Merge aaron's merge command
248
    else:
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
249
        if local_branch is not None:
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
250
            greedy_fetch(local_branch, branch, revision)
1185.12.39 by abentley
Propogated has_or_had_id to Tree
251
            base_tree = local_branch.revision_tree(revision)
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
252
        else:
1185.12.39 by abentley
Propogated has_or_had_id to Tree
253
            base_tree = branch.revision_tree(revision)
1185.12.41 by abentley
Got rid of MergeAdapterTree
254
    return base_tree
493 by Martin Pool
- Merge aaron's merge command
255
256
257
def file_exists(tree, file_id):
258
    return tree.has_filename(tree.id2path(file_id))
259
    
260
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
261
def build_working_dir(to_dir):
262
    """Build a working directory in an empty directory.
263
264
    to_dir is a directory containing branch metadata but no working files,
265
    typically constructed by cloning an existing branch. 
266
267
    This is split out as a special idiomatic case of merge.  It could
268
    eventually be done by just building the tree directly calling into 
269
    lower-level code (e.g. constructing a changeset).
270
    """
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
271
    # RBC 20051019 is this not just 'export' ?
1457.1.12 by Robert Collins
Update comment to reflect author.
272
    # AB Well, export doesn't take care of inventory...
1185.12.75 by Aaron Bentley
Introduced transform_tree
273
    this_branch = Branch.open_containing(to_dir)[0]
274
    transform_tree(this_branch.working_tree(), this_branch.basis_tree())
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
275
1457.1.12 by Robert Collins
Update comment to reflect author.
276
1185.35.4 by Aaron Bentley
Implemented remerge
277
def transform_tree(from_tree, to_tree, interesting_ids=None):
278
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
279
                interesting_ids=interesting_ids)
628 by Martin Pool
- merge aaron's updated merge/pull code
280
1457.1.12 by Robert Collins
Update comment to reflect author.
281
628 by Martin Pool
- merge aaron's updated merge/pull code
282
def merge(other_revision, base_revision,
283
          check_clean=True, ignore_zero=False,
974.1.10 by aaron.bentley at utoronto
Added file-selection to merge-revert
284
          this_dir=None, backup_files=False, merge_type=ApplyMerge3,
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
285
          file_list=None, show_base=False, reprocess=False):
628 by Martin Pool
- merge aaron's updated merge/pull code
286
    """Merge changes into a tree.
287
288
    base_revision
1185.12.97 by Aaron Bentley
Updated docs
289
        list(path, revno) Base for three-way merge.  
290
        If [None, None] then a base will be automatically determined.
628 by Martin Pool
- merge aaron's updated merge/pull code
291
    other_revision
1185.12.97 by Aaron Bentley
Updated docs
292
        list(path, revno) Other revision for three-way merge.
628 by Martin Pool
- merge aaron's updated merge/pull code
293
    this_dir
294
        Directory to merge changes into; '.' by default.
295
    check_clean
296
        If true, this_dir must have no uncommitted changes before the
297
        merge begins.
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
298
    ignore_zero - If true, suppress the "zero conflicts" message when 
299
        there are no conflicts; should be set when doing something we expect
300
        to complete perfectly.
1185.12.97 by Aaron Bentley
Updated docs
301
    file_list - If supplied, merge only changes to selected files.
1390 by Robert Collins
pair programming worx... merge integration and weave
302
303
    All available ancestors of other_revision and base_revision are
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
304
    automatically pulled into the branch.
1185.16.92 by mbp at sourcefrog
API docs for merge
305
1185.12.97 by Aaron Bentley
Updated docs
306
    The revno may be -1 to indicate the last revision on the branch, which is
307
    the typical case.
1185.16.92 by mbp at sourcefrog
API docs for merge
308
1185.12.97 by Aaron Bentley
Updated docs
309
    This function is intended for use from the command line; programmatic
310
    clients might prefer to call merge_inner(), which has less magic behavior.
628 by Martin Pool
- merge aaron's updated merge/pull code
311
    """
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
312
    if this_dir is None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
313
        this_dir = u'.'
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
314
    this_branch = Branch.open_containing(this_dir)[0]
1185.12.87 by Aaron Bentley
Updated NEWS, error out if --show-base supplied and unsupported
315
    if show_base and not merge_type is ApplyMerge3:
316
        raise BzrCommandError("Show-base is not supported for this merge"
317
                              " type. %s" % merge_type)
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
318
    if reprocess and not merge_type is ApplyMerge3:
319
        raise BzrCommandError("Reprocess is not supported for this merge"
320
                              " type. %s" % merge_type)
321
    if reprocess and show_base:
322
        raise BzrCommandError("Cannot reprocess and show base.")
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
323
    merger = Merger(this_branch)
324
    merger.check_basis(check_clean)
325
    merger.set_other(other_revision)
326
    merger.set_base(base_revision)
1185.35.2 by Aaron Bentley
Added early-exit message in merge
327
    if merger.base_rev_id == merger.other_rev_id:
328
        note('Nothing to do.')
329
        return 0
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
330
    merger.backup_files = backup_files
1185.12.81 by Aaron Bentley
Restored merge type selection
331
    merger.merge_type = merge_type 
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
332
    merger.set_interesting_files(file_list)
333
    merger.show_base = show_base 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
334
    merger.reprocess = reprocess
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
335
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, 
336
                                                   merger.base_tree, 
337
                                                   merger.other_tree,
338
                                                   ignore_zero=ignore_zero)
339
    conflicts = merger.do_merge()
340
    merger.set_pending()
341
    return conflicts
342
343
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1185.33.27 by Martin Pool
[merge] much integrated work from robert and john
344
                backup_files=False, 
345
                merge_type=ApplyMerge3, 
346
                interesting_ids=None, 
347
                show_base=False, 
348
                reprocess=False, 
349
                other_rev_id=None,
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
350
                interesting_files=None):
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
351
    """Primary interface for merging. 
352
353
        typical use is probably 
354
        'merge_inner(branch, branch.get_revision_tree(other_revision),
355
                     branch.get_revision_tree(base_revision))'
356
        """
357
    merger = Merger(this_branch, other_tree, base_tree)
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
358
    merger.backup_files = backup_files
1185.35.4 by Aaron Bentley
Implemented remerge
359
    merger.merge_type = merge_type
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
360
    merger.interesting_ids = interesting_ids
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
361
    if interesting_files:
362
        assert not interesting_ids, ('Only supply interesting_ids'
363
                                     ' or interesting_files')
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
364
        merger._set_interesting_files(interesting_files)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
365
    merger.show_base = show_base 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
366
    merger.reprocess = reprocess
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
367
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, base_tree, 
368
                                                   other_tree,
369
                                                   ignore_zero=ignore_zero)
1185.35.4 by Aaron Bentley
Implemented remerge
370
    merger.other_rev_id = other_rev_id
371
    merger.other_basis = other_rev_id
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
372
    return merger.do_merge()
373
374
375
class Merger(object):
376
    def __init__(self, this_branch, other_tree=None, base_tree=None):
377
        object.__init__(self)
378
        self.this_branch = this_branch
379
        self.this_basis = this_branch.last_revision()
380
        self.this_rev_id = None
381
        self.this_tree = this_branch.working_tree()
1185.12.83 by Aaron Bentley
Preliminary weave merge support
382
        self.this_revision_tree = None
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
383
        self.this_basis_tree = None
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
384
        self.other_tree = other_tree
385
        self.base_tree = base_tree
386
        self.ignore_zero = False
387
        self.backup_files = False
388
        self.interesting_ids = None
389
        self.show_base = False
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
390
        self.reprocess = False
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
391
        self.conflict_handler = MergeConflictHandler(self.this_tree, base_tree, 
392
                                                     other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
393
394
    def revision_tree(self, revision_id):
395
        return self.this_branch.revision_tree(revision_id)
396
397
    def ensure_revision_trees(self):
398
        if self.this_revision_tree is None:
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
399
            self.this_basis_tree = self.this_branch.revision_tree(
400
                self.this_basis)
401
            if self.this_basis == self.this_rev_id:
402
                self.this_revision_tree = self.this_basis_tree
403
1185.12.83 by Aaron Bentley
Preliminary weave merge support
404
405
        if self.other_rev_id is None:
406
            other_basis_tree = self.revision_tree(self.other_basis)
407
            changes = compare_trees(self.other_tree, other_basis_tree)
408
            if changes.has_changed():
409
                raise WorkingTreeNotRevision(self.this_tree)
410
            other_rev_id = other_basis
411
            self.other_tree = other_basis_tree
412
413
414
    def file_revisions(self, file_id):
415
        self.ensure_revision_trees()
416
        def get_id(tree, file_id):
417
            revision_id = tree.inventory[file_id].revision
418
            assert revision_id is not None
419
            return revision_id
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
420
        if self.this_rev_id is None:
421
            if self.this_basis_tree.get_file_sha1(file_id) != \
422
                self.this_tree.get_file_sha1(file_id):
423
                raise WorkingTreeNotRevision(self.this_tree)
424
425
        trees = (self.this_basis_tree, self.other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
426
        return [get_id(tree, file_id) for tree in trees]
427
            
428
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
429
    def merge_factory(self, file_id, base, other):
1185.12.83 by Aaron Bentley
Preliminary weave merge support
430
        if self.merge_type.history_based:
1185.35.4 by Aaron Bentley
Implemented remerge
431
            if self.show_base is True:
432
                raise BzrError("Cannot show base for hisory-based merges")
433
            if self.reprocess is True:
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
434
                raise BzrError("Cannot reprocess history-based merges")
1185.35.4 by Aaron Bentley
Implemented remerge
435
                
1185.12.83 by Aaron Bentley
Preliminary weave merge support
436
            t_revid, o_revid = self.file_revisions(file_id)
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
437
            weave = self.this_basis_tree.get_weave(file_id)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
438
            contents_change = self.merge_type(weave, t_revid, o_revid)
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
439
        else:
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
440
            if self.show_base is True or self.reprocess is True:
1185.12.83 by Aaron Bentley
Preliminary weave merge support
441
                contents_change = self.merge_type(file_id, base, other, 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
442
                                                  show_base=self.show_base, 
443
                                                  reprocess=self.reprocess)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
444
            else:
445
                contents_change = self.merge_type(file_id, base, other)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
446
        if self.backup_files:
447
            contents_change = BackupBeforeChange(contents_change)
448
        return contents_change
449
450
    def check_basis(self, check_clean):
451
        if self.this_basis is None:
452
            raise BzrCommandError("This branch has no commits")
453
        if check_clean:
454
            self.compare_basis()
455
            if self.this_basis != self.this_rev_id:
456
                raise BzrCommandError("Working tree has uncommitted changes.")
457
458
    def compare_basis(self):
459
        changes = compare_trees(self.this_branch.working_tree(), 
460
                                self.this_branch.basis_tree(), False)
461
        if not changes.has_changed():
462
            self.this_rev_id = self.this_basis
463
464
    def set_interesting_files(self, file_list):
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
465
        try:
466
            self._set_interesting_files(file_list)
467
        except NotVersionedError, e:
468
            raise BzrCommandError("%s is not a source file in any"
469
                                      " tree." % e.path)
470
471
    def _set_interesting_files(self, file_list):
472
        """Set the list of interesting ids from a list of files."""
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
473
        if file_list is None:
474
            self.interesting_ids = None
475
            return
476
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
477
        interesting_ids = set()
478
        for fname in file_list:
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
479
            path = self.this_tree.relpath(fname)
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
480
            found_id = False
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
481
            for tree in (self.this_tree, self.base_tree, self.other_tree):
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
482
                file_id = tree.inventory.path2id(path)
483
                if file_id is not None:
484
                    interesting_ids.add(file_id)
485
                    found_id = True
486
            if not found_id:
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
487
                raise NotVersionedError(path=fname)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
488
        self.interesting_ids = interesting_ids
489
490
    def set_pending(self):
1185.12.77 by Aaron Bentley
Prevented all ancestors from being marked as pending merges
491
        if not self.base_is_ancestor:
492
            return
493
        if self.other_rev_id is None:
494
            return
495
        if self.other_rev_id in self.this_branch.get_ancestry(self.this_basis):
496
            return
1457.1.15 by Robert Collins
Move add_pending_merge to WorkingTree.
497
        self.this_branch.working_tree().add_pending_merge(self.other_rev_id)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
498
499
    def set_other(self, other_revision):
500
        other_branch, self.other_tree = get_tree(other_revision, 
501
                                                 self.this_branch)
502
        if other_revision[1] == -1:
503
            self.other_rev_id = other_branch.last_revision()
504
            if self.other_rev_id is None:
505
                raise NoCommits(other_branch)
506
            self.other_basis = self.other_rev_id
507
        elif other_revision[1] is not None:
508
            self.other_rev_id = other_branch.get_rev_id(other_revision[1])
509
            self.other_basis = self.other_rev_id
510
        else:
511
            self.other_rev_id = None
512
            self.other_basis = other_branch.last_revision()
513
            if self.other_basis is None:
514
                raise NoCommits(other_branch)
1185.12.96 by Aaron Bentley
Merge from mpool
515
        fetch(from_branch=other_branch, to_branch=self.this_branch, 
516
              last_revision=self.other_basis)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
517
518
    def set_base(self, base_revision):
1185.12.96 by Aaron Bentley
Merge from mpool
519
        mutter("doing merge() with no base_revision specified")
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
520
        if base_revision == [None, None]:
521
            try:
522
                self.base_rev_id = common_ancestor(self.this_basis, 
523
                                                   self.other_basis, 
524
                                                   self.this_branch)
525
            except NoCommonAncestor:
526
                raise UnrelatedBranches()
527
            self.base_tree = get_revid_tree(self.this_branch, self.base_rev_id,
528
                                            None)
529
            self.base_is_ancestor = True
530
        else:
531
            base_branch, self.base_tree = get_tree(base_revision)
532
            if base_revision[1] == -1:
533
                self.base_rev_id = base_branch.last_revision()
534
            elif base_revision[1] is None:
535
                self.base_rev_id = None
493 by Martin Pool
- Merge aaron's merge command
536
            else:
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
537
                self.base_rev_id = base_branch.get_rev_id(base_revision[1])
538
            fetch(from_branch=base_branch, to_branch=self.this_branch)
539
            self.base_is_ancestor = is_ancestor(self.this_basis, 
540
                                                self.base_rev_id,
541
                                                self.this_branch)
542
543
    def do_merge(self):
544
        def get_inventory(tree):
545
            return tree.inventory
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
546
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
547
        inv_changes = merge_flex(self.this_tree, self.base_tree, 
548
                                 self.other_tree,
549
                                 generate_changeset, get_inventory,
550
                                 self.conflict_handler,
551
                                 merge_factory=self.merge_factory, 
552
                                 interesting_ids=self.interesting_ids)
553
554
        adjust_ids = []
555
        for id, path in inv_changes.iteritems():
556
            if path is not None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
557
                if path == u'.':
558
                    path = u''
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
559
                else:
1185.31.18 by John Arbash Meinel
[patch] Alexey Shamrin's patch for small win32 fixes
560
                    assert path.startswith('.' + '/') or path.startswith('.' + '\\'), "path is %s" % path
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
561
                path = path[2:]
562
            adjust_ids.append((path, id))
563
        if len(adjust_ids) > 0:
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
564
            self.this_branch.working_tree().set_inventory(self.regen_inventory(adjust_ids))
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
565
        conflicts = self.conflict_handler.conflicts
566
        self.conflict_handler.finalize()
567
        return conflicts
568
569
    def regen_inventory(self, new_entries):
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
570
        old_entries = self.this_branch.working_tree().read_working_inventory()
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
571
        new_inventory = {}
572
        by_path = {}
573
        new_entries_map = {} 
574
        for path, file_id in new_entries:
575
            if path is None:
576
                continue
577
            new_entries_map[file_id] = path
578
579
        def id2path(file_id):
580
            path = new_entries_map.get(file_id)
581
            if path is not None:
582
                return path
583
            entry = old_entries[file_id]
584
            if entry.parent_id is None:
585
                return entry.name
586
            return os.path.join(id2path(entry.parent_id), entry.name)
587
            
588
        for file_id in old_entries:
589
            entry = old_entries[file_id]
590
            path = id2path(file_id)
591
            new_inventory[file_id] = (path, file_id, entry.parent_id, 
592
                                      entry.kind)
593
            by_path[path] = file_id
974.1.21 by aaron.bentley at utoronto
Handled path generation properly
594
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
595
        deletions = 0
596
        insertions = 0
597
        new_path_list = []
598
        for path, file_id in new_entries:
599
            if path is None:
600
                del new_inventory[file_id]
601
                deletions += 1
602
            else:
603
                new_path_list.append((path, file_id))
604
                if file_id not in old_entries:
605
                    insertions += 1
606
        # Ensure no file is added before its parent
607
        new_path_list.sort()
608
        for path, file_id in new_path_list:
609
            if path == '':
610
                parent = None
611
            else:
612
                parent = by_path[os.path.dirname(path)]
613
            abspath = os.path.join(self.this_tree.basedir, path)
614
            kind = bzrlib.osutils.file_kind(abspath)
615
            new_inventory[file_id] = (path, file_id, parent, kind)
616
            by_path[path] = file_id 
493 by Martin Pool
- Merge aaron's merge command
617
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
618
        # Get a list in insertion order
619
        new_inventory_list = new_inventory.values()
620
        mutter ("""Inventory regeneration:
621
    old length: %i insertions: %i deletions: %i new_length: %i"""\
622
            % (len(old_entries), insertions, deletions, 
623
               len(new_inventory_list)))
624
        assert len(new_inventory_list) == len(old_entries) + insertions\
625
            - deletions
626
        new_inventory_list.sort()
627
        return new_inventory_list
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
628
629
merge_types = {     "merge3": (ApplyMerge3, "Native diff3-style merge"), 
1185.12.83 by Aaron Bentley
Preliminary weave merge support
630
                     "diff3": (Diff3Merge,  "Merge using external diff3"),
631
                     'weave': (WeaveMerge, "Weave-based merge")
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
632
              }
633