/brz/remove-bazaar

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