/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
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):
1185.50.14 by John Arbash Meinel
Changing final merge message to All changes applied successfully, rather than 0 conflicts applied.
229
        if self.conflicts == 0:
230
            if not self.ignore_zero:
231
                note("All changes applied successfully.")
232
        else:
1185.33.44 by Martin Pool
[patch] show number of revisions pushed/pulled/merged (Robey Pointer)
233
            note("%d conflicts encountered." % self.conflicts)
493 by Martin Pool
- Merge aaron's merge command
234
            
1185.12.41 by abentley
Got rid of MergeAdapterTree
235
def get_tree(treespec, local_branch=None):
622 by Martin Pool
Updated merge patch from Aaron
236
    location, revno = treespec
1442.1.64 by Robert Collins
Branch.open_containing now returns a tuple (Branch, relative-path).
237
    branch = Branch.open_containing(location)[0]
493 by Martin Pool
- Merge aaron's merge command
238
    if revno is None:
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
239
        revision = None
240
    elif revno == -1:
1241 by Martin Pool
- rename last_patch to last_revision
241
        revision = branch.last_revision()
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
242
    else:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
243
        revision = branch.get_rev_id(revno)
1185.12.98 by Aaron Bentley
Support for forcing merges of unrelated trees
244
        if revision is None:
245
            revision = NULL_REVISION
1185.12.41 by abentley
Got rid of MergeAdapterTree
246
    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
247
1185.12.41 by abentley
Got rid of MergeAdapterTree
248
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
249
    if revision is None:
1185.12.39 by abentley
Propogated has_or_had_id to Tree
250
        base_tree = branch.working_tree()
493 by Martin Pool
- Merge aaron's merge command
251
    else:
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
252
        if local_branch is not None:
974.1.36 by aaron.bentley at utoronto
Committed it even though the test case doesn't work
253
            greedy_fetch(local_branch, branch, revision)
1185.12.39 by abentley
Propogated has_or_had_id to Tree
254
            base_tree = local_branch.revision_tree(revision)
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
255
        else:
1185.12.39 by abentley
Propogated has_or_had_id to Tree
256
            base_tree = branch.revision_tree(revision)
1185.12.41 by abentley
Got rid of MergeAdapterTree
257
    return base_tree
493 by Martin Pool
- Merge aaron's merge command
258
259
260
def file_exists(tree, file_id):
261
    return tree.has_filename(tree.id2path(file_id))
262
    
263
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
264
def build_working_dir(to_dir):
265
    """Build a working directory in an empty directory.
266
267
    to_dir is a directory containing branch metadata but no working files,
268
    typically constructed by cloning an existing branch. 
269
270
    This is split out as a special idiomatic case of merge.  It could
271
    eventually be done by just building the tree directly calling into 
272
    lower-level code (e.g. constructing a changeset).
273
    """
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
274
    # RBC 20051019 is this not just 'export' ?
1457.1.12 by Robert Collins
Update comment to reflect author.
275
    # AB Well, export doesn't take care of inventory...
1185.12.75 by Aaron Bentley
Introduced transform_tree
276
    this_branch = Branch.open_containing(to_dir)[0]
277
    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
278
1457.1.12 by Robert Collins
Update comment to reflect author.
279
1185.35.4 by Aaron Bentley
Implemented remerge
280
def transform_tree(from_tree, to_tree, interesting_ids=None):
281
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
282
                interesting_ids=interesting_ids)
628 by Martin Pool
- merge aaron's updated merge/pull code
283
1457.1.12 by Robert Collins
Update comment to reflect author.
284
628 by Martin Pool
- merge aaron's updated merge/pull code
285
def merge(other_revision, base_revision,
286
          check_clean=True, ignore_zero=False,
974.1.10 by aaron.bentley at utoronto
Added file-selection to merge-revert
287
          this_dir=None, backup_files=False, merge_type=ApplyMerge3,
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
288
          file_list=None, show_base=False, reprocess=False):
628 by Martin Pool
- merge aaron's updated merge/pull code
289
    """Merge changes into a tree.
290
291
    base_revision
1185.12.97 by Aaron Bentley
Updated docs
292
        list(path, revno) Base for three-way merge.  
293
        If [None, None] then a base will be automatically determined.
628 by Martin Pool
- merge aaron's updated merge/pull code
294
    other_revision
1185.12.97 by Aaron Bentley
Updated docs
295
        list(path, revno) Other revision for three-way merge.
628 by Martin Pool
- merge aaron's updated merge/pull code
296
    this_dir
297
        Directory to merge changes into; '.' by default.
298
    check_clean
299
        If true, this_dir must have no uncommitted changes before the
300
        merge begins.
1393.1.10 by Martin Pool
- factor out stereotyped use of merge to build working dir
301
    ignore_zero - If true, suppress the "zero conflicts" message when 
302
        there are no conflicts; should be set when doing something we expect
303
        to complete perfectly.
1185.12.97 by Aaron Bentley
Updated docs
304
    file_list - If supplied, merge only changes to selected files.
1390 by Robert Collins
pair programming worx... merge integration and weave
305
306
    All available ancestors of other_revision and base_revision are
974.1.32 by aaron.bentley at utoronto
Made merge do greedy fetching.
307
    automatically pulled into the branch.
1185.16.92 by mbp at sourcefrog
API docs for merge
308
1185.12.97 by Aaron Bentley
Updated docs
309
    The revno may be -1 to indicate the last revision on the branch, which is
310
    the typical case.
1185.16.92 by mbp at sourcefrog
API docs for merge
311
1185.12.97 by Aaron Bentley
Updated docs
312
    This function is intended for use from the command line; programmatic
313
    clients might prefer to call merge_inner(), which has less magic behavior.
628 by Martin Pool
- merge aaron's updated merge/pull code
314
    """
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
315
    if this_dir is None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
316
        this_dir = u'.'
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
317
    this_branch = Branch.open_containing(this_dir)[0]
1185.12.87 by Aaron Bentley
Updated NEWS, error out if --show-base supplied and unsupported
318
    if show_base and not merge_type is ApplyMerge3:
319
        raise BzrCommandError("Show-base is not supported for this merge"
320
                              " type. %s" % merge_type)
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
321
    if reprocess and not merge_type is ApplyMerge3:
322
        raise BzrCommandError("Reprocess is not supported for this merge"
323
                              " type. %s" % merge_type)
324
    if reprocess and show_base:
325
        raise BzrCommandError("Cannot reprocess and show base.")
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
326
    merger = Merger(this_branch)
327
    merger.check_basis(check_clean)
328
    merger.set_other(other_revision)
329
    merger.set_base(base_revision)
1185.35.2 by Aaron Bentley
Added early-exit message in merge
330
    if merger.base_rev_id == merger.other_rev_id:
331
        note('Nothing to do.')
332
        return 0
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
333
    merger.backup_files = backup_files
1185.12.81 by Aaron Bentley
Restored merge type selection
334
    merger.merge_type = merge_type 
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
335
    merger.set_interesting_files(file_list)
336
    merger.show_base = show_base 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
337
    merger.reprocess = reprocess
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
338
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, 
339
                                                   merger.base_tree, 
340
                                                   merger.other_tree,
341
                                                   ignore_zero=ignore_zero)
342
    conflicts = merger.do_merge()
343
    merger.set_pending()
344
    return conflicts
345
346
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
347
                backup_files=False, 
348
                merge_type=ApplyMerge3, 
349
                interesting_ids=None, 
350
                show_base=False, 
351
                reprocess=False, 
352
                other_rev_id=None,
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
353
                interesting_files=None):
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
354
    """Primary interface for merging. 
355
356
        typical use is probably 
357
        'merge_inner(branch, branch.get_revision_tree(other_revision),
358
                     branch.get_revision_tree(base_revision))'
359
        """
360
    merger = Merger(this_branch, other_tree, base_tree)
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
361
    merger.backup_files = backup_files
1185.35.4 by Aaron Bentley
Implemented remerge
362
    merger.merge_type = merge_type
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
363
    merger.interesting_ids = interesting_ids
1457.1.7 by Robert Collins
Change cmd_revert to use merge_inner.
364
    if interesting_files:
365
        assert not interesting_ids, ('Only supply interesting_ids'
366
                                     ' or interesting_files')
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
367
        merger._set_interesting_files(interesting_files)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
368
    merger.show_base = show_base 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
369
    merger.reprocess = reprocess
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
370
    merger.conflict_handler = MergeConflictHandler(merger.this_tree, base_tree, 
371
                                                   other_tree,
372
                                                   ignore_zero=ignore_zero)
1185.35.4 by Aaron Bentley
Implemented remerge
373
    merger.other_rev_id = other_rev_id
374
    merger.other_basis = other_rev_id
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
375
    return merger.do_merge()
376
377
378
class Merger(object):
379
    def __init__(self, this_branch, other_tree=None, base_tree=None):
380
        object.__init__(self)
381
        self.this_branch = this_branch
382
        self.this_basis = this_branch.last_revision()
383
        self.this_rev_id = None
384
        self.this_tree = this_branch.working_tree()
1185.12.83 by Aaron Bentley
Preliminary weave merge support
385
        self.this_revision_tree = None
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
386
        self.this_basis_tree = None
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
387
        self.other_tree = other_tree
388
        self.base_tree = base_tree
389
        self.ignore_zero = False
390
        self.backup_files = False
391
        self.interesting_ids = None
392
        self.show_base = False
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
393
        self.reprocess = False
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
394
        self.conflict_handler = MergeConflictHandler(self.this_tree, base_tree, 
395
                                                     other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
396
397
    def revision_tree(self, revision_id):
398
        return self.this_branch.revision_tree(revision_id)
399
400
    def ensure_revision_trees(self):
401
        if self.this_revision_tree is None:
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
402
            self.this_basis_tree = self.this_branch.revision_tree(
403
                self.this_basis)
404
            if self.this_basis == self.this_rev_id:
405
                self.this_revision_tree = self.this_basis_tree
406
1185.12.83 by Aaron Bentley
Preliminary weave merge support
407
408
        if self.other_rev_id is None:
409
            other_basis_tree = self.revision_tree(self.other_basis)
410
            changes = compare_trees(self.other_tree, other_basis_tree)
411
            if changes.has_changed():
412
                raise WorkingTreeNotRevision(self.this_tree)
413
            other_rev_id = other_basis
414
            self.other_tree = other_basis_tree
415
416
417
    def file_revisions(self, file_id):
418
        self.ensure_revision_trees()
419
        def get_id(tree, file_id):
420
            revision_id = tree.inventory[file_id].revision
421
            assert revision_id is not None
422
            return revision_id
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
423
        if self.this_rev_id is None:
424
            if self.this_basis_tree.get_file_sha1(file_id) != \
425
                self.this_tree.get_file_sha1(file_id):
426
                raise WorkingTreeNotRevision(self.this_tree)
427
428
        trees = (self.this_basis_tree, self.other_tree)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
429
        return [get_id(tree, file_id) for tree in trees]
430
            
431
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
432
    def merge_factory(self, file_id, base, other):
1185.12.83 by Aaron Bentley
Preliminary weave merge support
433
        if self.merge_type.history_based:
1185.35.4 by Aaron Bentley
Implemented remerge
434
            if self.show_base is True:
435
                raise BzrError("Cannot show base for hisory-based merges")
436
            if self.reprocess is True:
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
437
                raise BzrError("Cannot reprocess history-based merges")
1185.35.4 by Aaron Bentley
Implemented remerge
438
                
1185.12.83 by Aaron Bentley
Preliminary weave merge support
439
            t_revid, o_revid = self.file_revisions(file_id)
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
440
            weave = self.this_basis_tree.get_weave(file_id)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
441
            contents_change = self.merge_type(weave, t_revid, o_revid)
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
442
        else:
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
443
            if self.show_base is True or self.reprocess is True:
1185.12.83 by Aaron Bentley
Preliminary weave merge support
444
                contents_change = self.merge_type(file_id, base, other, 
1185.24.3 by Aaron Bentley
Integrated reprocessing into the rest of the merge stuff
445
                                                  show_base=self.show_base, 
446
                                                  reprocess=self.reprocess)
1185.12.83 by Aaron Bentley
Preliminary weave merge support
447
            else:
448
                contents_change = self.merge_type(file_id, base, other)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
449
        if self.backup_files:
450
            contents_change = BackupBeforeChange(contents_change)
451
        return contents_change
452
453
    def check_basis(self, check_clean):
454
        if self.this_basis is None:
455
            raise BzrCommandError("This branch has no commits")
456
        if check_clean:
457
            self.compare_basis()
458
            if self.this_basis != self.this_rev_id:
459
                raise BzrCommandError("Working tree has uncommitted changes.")
460
461
    def compare_basis(self):
462
        changes = compare_trees(self.this_branch.working_tree(), 
463
                                self.this_branch.basis_tree(), False)
464
        if not changes.has_changed():
465
            self.this_rev_id = self.this_basis
466
467
    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.
468
        try:
469
            self._set_interesting_files(file_list)
470
        except NotVersionedError, e:
471
            raise BzrCommandError("%s is not a source file in any"
472
                                      " tree." % e.path)
473
474
    def _set_interesting_files(self, file_list):
475
        """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
476
        if file_list is None:
477
            self.interesting_ids = None
478
            return
479
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
480
        interesting_ids = set()
481
        for fname in file_list:
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
482
            path = self.this_tree.relpath(fname)
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
483
            found_id = False
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
484
            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.
485
                file_id = tree.inventory.path2id(path)
486
                if file_id is not None:
487
                    interesting_ids.add(file_id)
488
                    found_id = True
489
            if not found_id:
1457.1.8 by Robert Collins
Replace the WorkingTree.revert method algorithm with a call to merge_inner.
490
                raise NotVersionedError(path=fname)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
491
        self.interesting_ids = interesting_ids
492
493
    def set_pending(self):
1185.12.77 by Aaron Bentley
Prevented all ancestors from being marked as pending merges
494
        if not self.base_is_ancestor:
495
            return
496
        if self.other_rev_id is None:
497
            return
498
        if self.other_rev_id in self.this_branch.get_ancestry(self.this_basis):
499
            return
1457.1.15 by Robert Collins
Move add_pending_merge to WorkingTree.
500
        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
501
502
    def set_other(self, other_revision):
503
        other_branch, self.other_tree = get_tree(other_revision, 
504
                                                 self.this_branch)
505
        if other_revision[1] == -1:
506
            self.other_rev_id = other_branch.last_revision()
507
            if self.other_rev_id is None:
508
                raise NoCommits(other_branch)
509
            self.other_basis = self.other_rev_id
510
        elif other_revision[1] is not None:
511
            self.other_rev_id = other_branch.get_rev_id(other_revision[1])
512
            self.other_basis = self.other_rev_id
513
        else:
514
            self.other_rev_id = None
515
            self.other_basis = other_branch.last_revision()
516
            if self.other_basis is None:
517
                raise NoCommits(other_branch)
1185.12.96 by Aaron Bentley
Merge from mpool
518
        fetch(from_branch=other_branch, to_branch=self.this_branch, 
519
              last_revision=self.other_basis)
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
520
521
    def set_base(self, base_revision):
1185.12.96 by Aaron Bentley
Merge from mpool
522
        mutter("doing merge() with no base_revision specified")
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
523
        if base_revision == [None, None]:
524
            try:
525
                self.base_rev_id = common_ancestor(self.this_basis, 
526
                                                   self.other_basis, 
527
                                                   self.this_branch)
528
            except NoCommonAncestor:
529
                raise UnrelatedBranches()
530
            self.base_tree = get_revid_tree(self.this_branch, self.base_rev_id,
531
                                            None)
532
            self.base_is_ancestor = True
533
        else:
534
            base_branch, self.base_tree = get_tree(base_revision)
535
            if base_revision[1] == -1:
536
                self.base_rev_id = base_branch.last_revision()
537
            elif base_revision[1] is None:
538
                self.base_rev_id = None
493 by Martin Pool
- Merge aaron's merge command
539
            else:
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
540
                self.base_rev_id = base_branch.get_rev_id(base_revision[1])
541
            fetch(from_branch=base_branch, to_branch=self.this_branch)
542
            self.base_is_ancestor = is_ancestor(self.this_basis, 
543
                                                self.base_rev_id,
544
                                                self.this_branch)
545
546
    def do_merge(self):
547
        def get_inventory(tree):
548
            return tree.inventory
1185.35.5 by Aaron Bentley
Made weave merge succeed if interesting files match history
549
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
550
        inv_changes = merge_flex(self.this_tree, self.base_tree, 
551
                                 self.other_tree,
552
                                 generate_changeset, get_inventory,
553
                                 self.conflict_handler,
554
                                 merge_factory=self.merge_factory, 
555
                                 interesting_ids=self.interesting_ids)
556
557
        adjust_ids = []
558
        for id, path in inv_changes.iteritems():
559
            if path is not None:
1185.33.66 by Martin Pool
[patch] use unicode literals for all hardcoded paths (Alexander Belchenko)
560
                if path == u'.':
561
                    path = u''
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
562
                else:
1185.31.18 by John Arbash Meinel
[patch] Alexey Shamrin's patch for small win32 fixes
563
                    assert path.startswith('.' + '/') or path.startswith('.' + '\\'), "path is %s" % path
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
564
                path = path[2:]
565
            adjust_ids.append((path, id))
566
        if len(adjust_ids) > 0:
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
567
            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
568
        conflicts = self.conflict_handler.conflicts
569
        self.conflict_handler.finalize()
570
        return conflicts
571
572
    def regen_inventory(self, new_entries):
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
573
        old_entries = self.this_branch.working_tree().read_working_inventory()
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
574
        new_inventory = {}
575
        by_path = {}
576
        new_entries_map = {} 
577
        for path, file_id in new_entries:
578
            if path is None:
579
                continue
580
            new_entries_map[file_id] = path
581
582
        def id2path(file_id):
583
            path = new_entries_map.get(file_id)
584
            if path is not None:
585
                return path
586
            entry = old_entries[file_id]
587
            if entry.parent_id is None:
588
                return entry.name
589
            return os.path.join(id2path(entry.parent_id), entry.name)
590
            
591
        for file_id in old_entries:
592
            entry = old_entries[file_id]
593
            path = id2path(file_id)
594
            new_inventory[file_id] = (path, file_id, entry.parent_id, 
595
                                      entry.kind)
596
            by_path[path] = file_id
974.1.21 by aaron.bentley at utoronto
Handled path generation properly
597
        
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
598
        deletions = 0
599
        insertions = 0
600
        new_path_list = []
601
        for path, file_id in new_entries:
602
            if path is None:
603
                del new_inventory[file_id]
604
                deletions += 1
605
            else:
606
                new_path_list.append((path, file_id))
607
                if file_id not in old_entries:
608
                    insertions += 1
609
        # Ensure no file is added before its parent
610
        new_path_list.sort()
611
        for path, file_id in new_path_list:
612
            if path == '':
613
                parent = None
614
            else:
615
                parent = by_path[os.path.dirname(path)]
616
            abspath = os.path.join(self.this_tree.basedir, path)
617
            kind = bzrlib.osutils.file_kind(abspath)
618
            new_inventory[file_id] = (path, file_id, parent, kind)
619
            by_path[path] = file_id 
493 by Martin Pool
- Merge aaron's merge command
620
1185.12.76 by Aaron Bentley
Refactored merge and merge_inner to use Merger
621
        # Get a list in insertion order
622
        new_inventory_list = new_inventory.values()
623
        mutter ("""Inventory regeneration:
624
    old length: %i insertions: %i deletions: %i new_length: %i"""\
625
            % (len(old_entries), insertions, deletions, 
626
               len(new_inventory_list)))
627
        assert len(new_inventory_list) == len(old_entries) + insertions\
628
            - deletions
629
        new_inventory_list.sort()
630
        return new_inventory_list
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
631
632
merge_types = {     "merge3": (ApplyMerge3, "Native diff3-style merge"), 
1185.12.83 by Aaron Bentley
Preliminary weave merge support
633
                     "diff3": (Diff3Merge,  "Merge using external diff3"),
634
                     'weave': (WeaveMerge, "Weave-based merge")
974.1.9 by Aaron Bentley
Added merge-type parameter to merge.
635
              }
636