~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
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
17
# TODO: build_working_dir can be built on something simpler than merge()
18
1185.1.2 by Martin Pool
- merge various windows and other fixes from Ollie Rutherfurd
19
import os
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
20
import errno
1113 by Martin Pool
- fix is_ancestor import problem in merge
21
1545.2.1 by Aaron Bentley
Made the merge internals private
22
import bzrlib
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
23
from bzrlib._changeset import generate_changeset, ExceptionConflictHandler
24
from bzrlib._changeset import Inventory, Diff3Merge, ReplaceContents
1545.2.1 by Aaron Bentley
Made the merge internals private
25
from bzrlib._merge_core import WeaveMerge
26
from bzrlib._merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
27
from bzrlib.branch import Branch
1545.2.1 by Aaron Bentley
Made the merge internals private
28
from bzrlib.delta import compare_trees
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
29
from bzrlib.errors import (BzrCommandError,
1534.4.28 by Robert Collins
first cut at merge from integration.
30
                           BzrError,
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
31
                           NoCommonAncestor,
32
                           NoCommits,
1534.4.28 by Robert Collins
first cut at merge from integration.
33
                           NoSuchRevision,
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
34
                           NotBranchError,
1185.33.27 by Martin Pool
[merge] much integrated work from robert and john
35
                           NotVersionedError,
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
36
                           UnrelatedBranches,
37
                           WorkingTreeNotRevision,
1534.4.28 by Robert Collins
first cut at merge from integration.
38
                           )
1390 by Robert Collins
pair programming worx... merge integration and weave
39
from bzrlib.fetch import greedy_fetch, fetch
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
40
import bzrlib.osutils
41
from bzrlib.osutils import rename, pathjoin
1534.4.28 by Robert Collins
first cut at merge from integration.
42
from bzrlib.revision import common_ancestor, is_ancestor, NULL_REVISION
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
43
from bzrlib.trace import mutter, warning, note
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
44
from bzrlib.workingtree import WorkingTree
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
45
46
# TODO: Report back as changes are merged in
47
48
# comments from abentley on irc: merge happens in two stages, each
49
# of which generates a changeset object
50
51
# stage 1: generate OLD->OTHER,
52
# stage 2: use MINE and OLD->OTHER to generate MINE -> RESULT
53
54
class _MergeConflictHandler(ExceptionConflictHandler):
55
    """Handle conflicts encountered while merging.
56
57
    This subclasses ExceptionConflictHandler, so that any types of
58
    conflict that are not explicitly handled cause an exception and
59
    terminate the merge.
60
    """
61
    def __init__(self, this_tree, base_tree, other_tree, ignore_zero=False):
62
        ExceptionConflictHandler.__init__(self)
63
        self.conflicts = 0
64
        self.ignore_zero = ignore_zero
65
        self.this_tree = this_tree
66
        self.base_tree = base_tree
67
        self.other_tree = other_tree
68
69
    def copy(self, source, dest):
70
        """Copy the text and mode of a file
71
        :param source: The path of the file to copy
72
        :param dest: The distination file to create
73
        """
74
        s_file = file(source, "rb")
75
        d_file = file(dest, "wb")
76
        for line in s_file:
77
            d_file.write(line)
78
        os.chmod(dest, 0777 & os.stat(source).st_mode)
79
80
    def dump(self, lines, dest):
81
        """Copy the text and mode of a file
82
        :param source: The path of the file to copy
83
        :param dest: The distination file to create
84
        """
85
        d_file = file(dest, "wb")
86
        for line in lines:
87
            d_file.write(line)
88
89
    def add_suffix(self, name, suffix, last_new_name=None, fix_inventory=True):
90
        """Rename a file to append a suffix.  If the new name exists, the
91
        suffix is added repeatedly until a non-existant name is found
92
93
        :param name: The path of the file
94
        :param suffix: The suffix to append
95
        :param last_new_name: (used for recursive calls) the last name tried
96
        """
97
        if last_new_name is None:
98
            last_new_name = name
99
        new_name = last_new_name+suffix
100
        try:
101
            rename(name, new_name)
102
            if fix_inventory is True:
103
                try:
104
                    relpath = self.this_tree.relpath(name)
105
                except NotBranchError:
106
                    relpath = None
107
                if relpath is not None:
108
                    file_id = self.this_tree.path2id(relpath)
109
                    if file_id is not None:
110
                        new_path = self.this_tree.relpath(new_name)
111
                        rename(new_name, name)
112
                        self.this_tree.rename_one(relpath, new_path)
113
                        assert self.this_tree.id2path(file_id) == new_path
114
        except OSError, e:
115
            if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY:
116
                raise
117
            return self.add_suffix(name, suffix, last_new_name=new_name, 
118
                                   fix_inventory=fix_inventory)
119
        return new_name
120
121
    def conflict(self, text):
122
        warning(text)
123
        self.conflicts += 1
124
        
125
126
    def merge_conflict(self, new_file, this_path, base_lines, other_lines):
127
        """
128
        Handle diff3 conflicts by producing a .THIS, .BASE and .OTHER.  The
129
        main file will be a version with diff3 conflicts.
130
        :param new_file: Path to the output file with diff3 markers
131
        :param this_path: Path to the file text for the THIS tree
132
        :param base_path: Path to the file text for the BASE tree
133
        :param other_path: Path to the file text for the OTHER tree
134
        """
135
        self.add_suffix(this_path, ".THIS", fix_inventory=False)
136
        self.dump(base_lines, this_path+".BASE")
137
        self.dump(other_lines, this_path+".OTHER")
138
        rename(new_file, this_path)
139
        self.conflict("Diff3 conflict encountered in %s" % this_path)
140
141
    def weave_merge_conflict(self, filename, weave, other_i, out_file):
142
        """
143
        Handle weave conflicts by producing a .THIS, and .OTHER.  The
144
        main file will be a version with diff3-style conflicts.
145
        """
146
        self.add_suffix(filename, ".THIS", fix_inventory=False)
147
        out_file.commit()
148
        self.dump(weave.get_iter(other_i), filename+".OTHER")
149
        self.conflict("Text conflict encountered in %s" % filename)
150
151
    def new_contents_conflict(self, filename, other_contents):
152
        """Conflicting contents for newly added file."""
153
        other_contents(filename + ".OTHER", self, False)
154
        self.conflict("Conflict in newly added file %s" % filename)
155
    
156
157
    def target_exists(self, entry, target, old_path):
158
        """Handle the case when the target file or dir exists"""
159
        moved_path = self.add_suffix(target, ".moved")
160
        self.conflict("Moved existing %s to %s" % (target, moved_path))
161
162
    def rmdir_non_empty(self, filename):
163
        """Handle the case where the dir to be removed still has contents"""
164
        self.conflict("Directory %s not removed because it is not empty"\
165
            % filename)
166
        return "skip"
167
168
    def rem_contents_conflict(self, filename, this_contents, base_contents):
169
        base_contents(filename+".BASE", self)
170
        this_contents(filename+".THIS", self)
171
        self.conflict("Other branch deleted locally modified file %s" %
172
                      filename)
173
        return ReplaceContents(this_contents, None)
174
175
    def abs_this_path(self, file_id):
176
        """Return the absolute path for a file_id in the this tree."""
177
        return self.this_tree.id2abspath(file_id)
178
179
    def add_missing_parents(self, file_id, tree):
180
        """If some of the parents for file_id are missing, add them."""
181
        entry = tree.inventory[file_id]
182
        if entry.parent_id not in self.this_tree:
183
            return self.create_all_missing(entry.parent_id, tree)
184
        else:
185
            return self.abs_this_path(entry.parent_id)
186
187
    def create_all_missing(self, file_id, tree):
188
        """Add contents for a file_id and all its parents to a tree."""
189
        entry = tree.inventory[file_id]
190
        if entry.parent_id is not None and entry.parent_id not in self.this_tree:
191
            abspath = self.create_all_missing(entry.parent_id, tree)
192
        else:
193
            abspath = self.abs_this_path(entry.parent_id)
194
        entry_path = pathjoin(abspath, entry.name)
195
        if not os.path.isdir(entry_path):
196
            self.create(file_id, entry_path, tree)
197
        return entry_path
198
199
    def create(self, file_id, path, tree):
200
        """Uses tree data to create a filesystem object for the file_id"""
1545.2.10 by Aaron Bentley
Fixed up non-absolute imports
201
        from bzrlib._changeset import get_contents
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
202
        get_contents(tree, file_id)(path, self)
203
204
    def missing_for_merge(self, file_id, other_path):
205
        """The file_id doesn't exist in THIS, but does in OTHER and BASE"""
206
        self.conflict("Other branch modified locally deleted file %s" %
207
                      other_path)
208
        parent_dir = self.add_missing_parents(file_id, self.other_tree)
209
        stem = pathjoin(parent_dir, os.path.basename(other_path))
210
        self.create(file_id, stem+".OTHER", self.other_tree)
211
        self.create(file_id, stem+".BASE", self.base_tree)
212
213
    def threeway_contents_conflict(filename, this_contents, base_contents,
214
                                   other_contents):
215
        self.conflict("Three-way conflict merging %s" % filename)
216
217
    def finalize(self):
218
        if self.conflicts == 0:
219
            if not self.ignore_zero:
220
                note("All changes applied successfully.")
221
        else:
222
            note("%d conflicts encountered." % self.conflicts)
1545.2.4 by Aaron Bentley
PEP8 fixes
223
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
224
def _get_tree(treespec, local_branch=None):
622 by Martin Pool
Updated merge patch from Aaron
225
    location, revno = treespec
1442.1.64 by Robert Collins
Branch.open_containing now returns a tuple (Branch, relative-path).
226
    branch = Branch.open_containing(location)[0]
493 by Martin Pool
- Merge aaron's merge command
227
    if revno is None:
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
228
        revision = None
229
    elif revno == -1:
1241 by Martin Pool
- rename last_patch to last_revision
230
        revision = branch.last_revision()
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
231
    else:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
232
        revision = branch.get_rev_id(revno)
1185.12.98 by Aaron Bentley
Support for forcing merges of unrelated trees
233
        if revision is None:
234
            revision = NULL_REVISION
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
235
    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
236
1545.2.4 by Aaron Bentley
PEP8 fixes
237
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
238
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
239
    if revision is None:
1185.12.39 by abentley
Propogated has_or_had_id to Tree
240
        base_tree = branch.working_tree()
493 by Martin Pool
- Merge aaron's merge command
241
    else:
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
242
        if local_branch is not None:
1185.65.30 by Robert Collins
Merge integration.
243
            if local_branch.base != branch.base:
244
                greedy_fetch(local_branch, branch, revision)
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
245
            base_tree = local_branch.repository.revision_tree(revision)
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
246
        else:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
247
            base_tree = branch.repository.revision_tree(revision)
1185.12.41 by abentley
Got rid of MergeAdapterTree
248
    return base_tree
493 by Martin Pool
- Merge aaron's merge command
249
250
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
251
def build_working_dir(to_dir):
252
    """Build a working directory in an empty directory.
253
254
    to_dir is a directory containing branch metadata but no working files,
255
    typically constructed by cloning an existing branch. 
256
257
    This is split out as a special idiomatic case of merge.  It could
258
    eventually be done by just building the tree directly calling into 
259
    lower-level code (e.g. constructing a changeset).
260
    """
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
261
    # RBC 20051019 is this not just 'export' ?
1457.1.12 by Robert Collins
Update comment to reflect author.
262
    # AB Well, export doesn't take care of inventory...
1185.12.75 by Aaron Bentley
Introduced transform_tree
263
    this_branch = Branch.open_containing(to_dir)[0]
264
    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
265
1457.1.12 by Robert Collins
Update comment to reflect author.
266
1185.35.4 by Aaron Bentley
Implemented remerge
267
def transform_tree(from_tree, to_tree, interesting_ids=None):
268
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
269
                interesting_ids=interesting_ids)
628 by Martin Pool
- merge aaron's updated merge/pull code
270
1457.1.12 by Robert Collins
Update comment to reflect author.
271
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
272
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
273
                backup_files=False, 
274
                merge_type=ApplyMerge3, 
275
                interesting_ids=None, 
276
                show_base=False, 
277
                reprocess=False, 
278
                other_rev_id=None,
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
279
                interesting_files=None,
280
                this_tree=None):
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
281
    """Primary interface for merging. 
282
283
        typical use is probably 
284
        'merge_inner(branch, branch.get_revision_tree(other_revision),
285
                     branch.get_revision_tree(base_revision))'
286
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
287
    if this_tree is None:
288
        this_tree = this_branch.working_tree()
289
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree)
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
290
    merger.backup_files = backup_files
1185.35.4 by Aaron Bentley
Implemented remerge
291
    merger.merge_type = merge_type
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
292
    merger.interesting_ids = interesting_ids
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
293
    if interesting_files:
294
        assert not interesting_ids, ('Only supply interesting_ids'
295
                                     ' or interesting_files')
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
296
        merger._set_interesting_files(interesting_files)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
297
    merger.show_base = show_base 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
298
    merger.reprocess = reprocess
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
299
    merger.conflict_handler = _MergeConflictHandler(merger.this_tree, 
300
                                                    base_tree, other_tree,
301
                                                    ignore_zero=ignore_zero)
1185.35.4 by Aaron Bentley
Implemented remerge
302
    merger.other_rev_id = other_rev_id
303
    merger.other_basis = other_rev_id
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
304
    return merger.do_merge()
305
306
307
class Merger(object):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
308
    def __init__(self, this_branch, other_tree=None, base_tree=None, this_tree=None):
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
309
        object.__init__(self)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
310
        assert this_tree is not None, "this_tree is required"
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
311
        self.this_branch = this_branch
312
        self.this_basis = this_branch.last_revision()
313
        self.this_rev_id = None
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
314
        self.this_tree = this_tree
1185.12.83 by Aaron Bentley
Preliminary weave merge support
315
        self.this_revision_tree = None
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
316
        self.this_basis_tree = None
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
317
        self.other_tree = other_tree
318
        self.base_tree = base_tree
319
        self.ignore_zero = False
320
        self.backup_files = False
321
        self.interesting_ids = None
322
        self.show_base = False
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
323
        self.reprocess = False
1545.2.6 by Aaron Bentley
Removed _merge, renamed MergeConflictHandler to _MergeConflictHandler
324
        self.conflict_handler = _MergeConflictHandler(self.this_tree, 
325
                                                      base_tree, other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
326
327
    def revision_tree(self, revision_id):
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
328
        return self.this_branch.repository.revision_tree(revision_id)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
329
330
    def ensure_revision_trees(self):
331
        if self.this_revision_tree is None:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
332
            self.this_basis_tree = self.this_branch.repository.revision_tree(
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
333
                self.this_basis)
334
            if self.this_basis == self.this_rev_id:
335
                self.this_revision_tree = self.this_basis_tree
336
1185.12.83 by Aaron Bentley
Preliminary weave merge support
337
        if self.other_rev_id is None:
338
            other_basis_tree = self.revision_tree(self.other_basis)
339
            changes = compare_trees(self.other_tree, other_basis_tree)
340
            if changes.has_changed():
341
                raise WorkingTreeNotRevision(self.this_tree)
342
            other_rev_id = other_basis
343
            self.other_tree = other_basis_tree
344
345
    def file_revisions(self, file_id):
346
        self.ensure_revision_trees()
347
        def get_id(tree, file_id):
348
            revision_id = tree.inventory[file_id].revision
349
            assert revision_id is not None
350
            return revision_id
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
351
        if self.this_rev_id is None:
352
            if self.this_basis_tree.get_file_sha1(file_id) != \
353
                self.this_tree.get_file_sha1(file_id):
354
                raise WorkingTreeNotRevision(self.this_tree)
355
356
        trees = (self.this_basis_tree, self.other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
357
        return [get_id(tree, file_id) for tree in trees]
358
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
359
    def merge_factory(self, file_id, base, other):
1185.12.83 by Aaron Bentley
Preliminary weave merge support
360
        if self.merge_type.history_based:
1185.35.4 by Aaron Bentley
Implemented remerge
361
            if self.show_base is True:
362
                raise BzrError("Cannot show base for hisory-based merges")
363
            if self.reprocess is True:
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
364
                raise BzrError("Cannot reprocess history-based merges")
1185.35.4 by Aaron Bentley
Implemented remerge
365
                
1185.12.83 by Aaron Bentley
Preliminary weave merge support
366
            t_revid, o_revid = self.file_revisions(file_id)
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
367
            weave = self.this_basis_tree.get_weave(file_id)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
368
            contents_change = self.merge_type(weave, t_revid, o_revid)
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
369
        else:
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
370
            if self.show_base is True or self.reprocess is True:
1185.12.83 by Aaron Bentley
Preliminary weave merge support
371
                contents_change = self.merge_type(file_id, base, other, 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
372
                                                  show_base=self.show_base, 
373
                                                  reprocess=self.reprocess)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
374
            else:
375
                contents_change = self.merge_type(file_id, base, other)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
376
        if self.backup_files:
377
            contents_change = BackupBeforeChange(contents_change)
378
        return contents_change
379
380
    def check_basis(self, check_clean):
381
        if self.this_basis is None:
382
            raise BzrCommandError("This branch has no commits")
383
        if check_clean:
384
            self.compare_basis()
385
            if self.this_basis != self.this_rev_id:
386
                raise BzrCommandError("Working tree has uncommitted changes.")
387
388
    def compare_basis(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
389
        changes = compare_trees(self.this_tree, 
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
390
                                self.this_branch.basis_tree(), False)
391
        if not changes.has_changed():
392
            self.this_rev_id = self.this_basis
393
394
    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.
395
        try:
396
            self._set_interesting_files(file_list)
397
        except NotVersionedError, e:
398
            raise BzrCommandError("%s is not a source file in any"
399
                                      " tree." % e.path)
400
401
    def _set_interesting_files(self, file_list):
402
        """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
403
        if file_list is None:
404
            self.interesting_ids = None
405
            return
406
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
407
        interesting_ids = set()
1185.50.53 by John Arbash Meinel
[patch] Aaron Bentley: make revert work in a subdirectory.
408
        for path in file_list:
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
409
            found_id = False
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
410
            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.
411
                file_id = tree.inventory.path2id(path)
412
                if file_id is not None:
413
                    interesting_ids.add(file_id)
414
                    found_id = True
415
            if not found_id:
1185.50.53 by John Arbash Meinel
[patch] Aaron Bentley: make revert work in a subdirectory.
416
                raise NotVersionedError(path=path)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
417
        self.interesting_ids = interesting_ids
418
419
    def set_pending(self):
1185.12.77 by Aaron Bentley
Prevented all ancestors from being marked as pending merges
420
        if not self.base_is_ancestor:
421
            return
422
        if self.other_rev_id is None:
423
            return
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
424
        ancestry = self.this_branch.repository.get_ancestry(self.this_basis)
1185.66.2 by Aaron Bentley
Moved get_ancestry to RevisionStorage
425
        if self.other_rev_id in ancestry:
1185.12.77 by Aaron Bentley
Prevented all ancestors from being marked as pending merges
426
            return
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
427
        self.this_tree.add_pending_merge(self.other_rev_id)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
428
429
    def set_other(self, other_revision):
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
430
        other_branch, self.other_tree = _get_tree(other_revision, 
431
                                                  self.this_branch)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
432
        if other_revision[1] == -1:
433
            self.other_rev_id = other_branch.last_revision()
434
            if self.other_rev_id is None:
435
                raise NoCommits(other_branch)
436
            self.other_basis = self.other_rev_id
437
        elif other_revision[1] is not None:
438
            self.other_rev_id = other_branch.get_rev_id(other_revision[1])
439
            self.other_basis = self.other_rev_id
440
        else:
441
            self.other_rev_id = None
442
            self.other_basis = other_branch.last_revision()
443
            if self.other_basis is None:
444
                raise NoCommits(other_branch)
1185.65.30 by Robert Collins
Merge integration.
445
        if other_branch.base != self.this_branch.base:
446
            fetch(from_branch=other_branch, to_branch=self.this_branch, 
447
                  last_revision=self.other_basis)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
448
449
    def set_base(self, base_revision):
1185.12.96 by Aaron Bentley
Merge from mpool
450
        mutter("doing merge() with no base_revision specified")
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
451
        if base_revision == [None, None]:
452
            try:
453
                self.base_rev_id = common_ancestor(self.this_basis, 
454
                                                   self.other_basis, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
455
                                                   self.this_branch.repository)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
456
            except NoCommonAncestor:
457
                raise UnrelatedBranches()
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
458
            self.base_tree = _get_revid_tree(self.this_branch, self.base_rev_id,
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
459
                                            None)
460
            self.base_is_ancestor = True
461
        else:
1545.2.3 by Aaron Bentley
Updated following j-a-meinel's comments
462
            base_branch, self.base_tree = _get_tree(base_revision)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
463
            if base_revision[1] == -1:
464
                self.base_rev_id = base_branch.last_revision()
465
            elif base_revision[1] is None:
466
                self.base_rev_id = None
493 by Martin Pool
- Merge aaron's merge command
467
            else:
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
468
                self.base_rev_id = base_branch.get_rev_id(base_revision[1])
469
            fetch(from_branch=base_branch, to_branch=self.this_branch)
470
            self.base_is_ancestor = is_ancestor(self.this_basis, 
471
                                                self.base_rev_id,
472
                                                self.this_branch)
473
474
    def do_merge(self):
475
        def get_inventory(tree):
476
            return tree.inventory
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
477
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
478
        inv_changes = merge_flex(self.this_tree, self.base_tree, 
479
                                 self.other_tree,
480
                                 generate_changeset, get_inventory,
481
                                 self.conflict_handler,
482
                                 merge_factory=self.merge_factory, 
483
                                 interesting_ids=self.interesting_ids)
484
485
        adjust_ids = []
486
        for id, path in inv_changes.iteritems():
487
            if path is not None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
488
                if path == u'.':
489
                    path = u''
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
490
                else:
1185.31.18 by John Arbash Meinel
[patch] Alexey Shamrin's patch for small win32 fixes
491
                    assert path.startswith('.' + '/') or path.startswith('.' + '\\'), "path is %s" % path
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
492
                path = path[2:]
493
            adjust_ids.append((path, id))
494
        if len(adjust_ids) > 0:
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
495
            self.this_tree.set_inventory(self.regen_inventory(adjust_ids))
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
496
        conflicts = self.conflict_handler.conflicts
497
        self.conflict_handler.finalize()
498
        return conflicts
499
500
    def regen_inventory(self, new_entries):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
501
        old_entries = self.this_tree.read_working_inventory()
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
502
        new_inventory = {}
503
        by_path = {}
504
        new_entries_map = {} 
505
        for path, file_id in new_entries:
506
            if path is None:
507
                continue
508
            new_entries_map[file_id] = path
509
510
        def id2path(file_id):
511
            path = new_entries_map.get(file_id)
512
            if path is not None:
513
                return path
514
            entry = old_entries[file_id]
515
            if entry.parent_id is None:
516
                return entry.name
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
517
            return pathjoin(id2path(entry.parent_id), entry.name)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
518
            
519
        for file_id in old_entries:
520
            entry = old_entries[file_id]
521
            path = id2path(file_id)
522
            new_inventory[file_id] = (path, file_id, entry.parent_id, 
523
                                      entry.kind)
524
            by_path[path] = file_id
974.1.21 by aaron.bentley at utoronto
Handled path generation properly
525
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
526
        deletions = 0
527
        insertions = 0
528
        new_path_list = []
529
        for path, file_id in new_entries:
530
            if path is None:
531
                del new_inventory[file_id]
532
                deletions += 1
533
            else:
534
                new_path_list.append((path, file_id))
535
                if file_id not in old_entries:
536
                    insertions += 1
537
        # Ensure no file is added before its parent
538
        new_path_list.sort()
539
        for path, file_id in new_path_list:
540
            if path == '':
541
                parent = None
542
            else:
543
                parent = by_path[os.path.dirname(path)]
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
544
            abspath = pathjoin(self.this_tree.basedir, path)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
545
            kind = bzrlib.osutils.file_kind(abspath)
546
            new_inventory[file_id] = (path, file_id, parent, kind)
547
            by_path[path] = file_id 
493 by Martin Pool
- Merge aaron's merge command
548
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
549
        # Get a list in insertion order
550
        new_inventory_list = new_inventory.values()
551
        mutter ("""Inventory regeneration:
552
    old length: %i insertions: %i deletions: %i new_length: %i"""\
553
            % (len(old_entries), insertions, deletions, 
554
               len(new_inventory_list)))
555
        assert len(new_inventory_list) == len(old_entries) + insertions\
556
            - deletions
557
        new_inventory_list.sort()
558
        return new_inventory_list
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
559
1545.2.4 by Aaron Bentley
PEP8 fixes
560
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
561
merge_types = {     "merge3": (ApplyMerge3, "Native diff3-style merge"), 
1185.12.83 by Aaron Bentley
Preliminary weave merge support
562
                     "diff3": (Diff3Merge,  "Merge using external diff3"),
563
                     'weave': (WeaveMerge, "Weave-based merge")
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
564
              }