~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Robert Collins
  • Date: 2005-10-02 21:51:29 UTC
  • mfrom: (1396)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20051002215128-5686c7d24bf9bdb9
merge from martins newformat branch - brings in transport abstraction

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import bzrlib.revision
25
25
from bzrlib.merge_core import merge_flex, ApplyMerge3, BackupBeforeChange
26
26
from bzrlib.changeset import generate_changeset, ExceptionConflictHandler
27
 
from bzrlib.changeset import Inventory, Diff3Merge
 
27
from bzrlib.changeset import Inventory, Diff3Merge, ReplaceContents
28
28
from bzrlib.branch import Branch
29
29
from bzrlib.errors import BzrCommandError, UnrelatedBranches, NoCommonAncestor
30
30
from bzrlib.errors import NoCommits
36
36
from bzrlib.revision import common_ancestor, MultipleRevisionSources
37
37
from bzrlib.errors import NoSuchRevision
38
38
 
 
39
# TODO: build_working_dir can be built on something simpler than merge()
 
40
 
 
41
# FIXME: merge() parameters seem oriented towards the command line
39
42
 
40
43
# comments from abentley on irc: merge happens in two stages, each
41
44
# of which generates a changeset object
50
53
    conflict that are not explicitly handled cause an exception and
51
54
    terminate the merge.
52
55
    """
53
 
    def __init__(self, ignore_zero=False):
 
56
    def __init__(self, this_tree, base_tree, other_tree, ignore_zero=False):
54
57
        ExceptionConflictHandler.__init__(self)
55
58
        self.conflicts = 0
56
59
        self.ignore_zero = ignore_zero
 
60
        self.this_tree = this_tree
 
61
        self.base_tree = base_tree
 
62
        self.other_tree = other_tree
57
63
 
58
64
    def copy(self, source, dest):
59
65
        """Copy the text and mode of a file
131
137
            % filename)
132
138
        return "skip"
133
139
 
 
140
    def rem_contents_conflict(self, filename, this_contents, base_contents):
 
141
        base_contents(filename+".BASE", self, False)
 
142
        this_contents(filename+".THIS", self, False)
 
143
        self.conflict("Other branch deleted locally modified file %s" %
 
144
                      filename)
 
145
        return ReplaceContents(this_contents, None)
 
146
 
 
147
    def abs_this_path(self, file_id):
 
148
        """Return the absolute path for a file_id in the this tree."""
 
149
        relpath = self.this_tree.id2path(file_id)
 
150
        return self.this_tree.tree.abspath(relpath)
 
151
 
 
152
    def add_missing_parents(self, file_id, tree):
 
153
        """If some of the parents for file_id are missing, add them."""
 
154
        entry = tree.tree.inventory[file_id]
 
155
        if entry.parent_id not in self.this_tree:
 
156
            return self.create_all_missing(entry.parent_id, tree)
 
157
        else:
 
158
            return self.abs_this_path(entry.parent_id)
 
159
 
 
160
    def create_all_missing(self, file_id, tree):
 
161
        """Add contents for a file_id and all its parents to a tree."""
 
162
        entry = tree.tree.inventory[file_id]
 
163
        if entry.parent_id is not None and entry.parent_id not in self.this_tree:
 
164
            abspath = self.create_all_missing(entry.parent_id, tree)
 
165
        else:
 
166
            abspath = self.abs_this_path(entry.parent_id)
 
167
        entry_path = os.path.join(abspath, entry.name)
 
168
        if not os.path.isdir(entry_path):
 
169
            self.create(file_id, entry_path, tree)
 
170
        return entry_path
 
171
 
 
172
    def create(self, file_id, path, tree, reverse=False):
 
173
        """Uses tree data to create a filesystem object for the file_id"""
 
174
        from merge_core import get_id_contents
 
175
        get_id_contents(file_id, tree)(path, self, reverse)
 
176
 
 
177
    def missing_for_merge(self, file_id, other_path):
 
178
        """The file_id doesn't exist in THIS, but does in OTHER and BASE"""
 
179
        self.conflict("Other branch modified locally deleted file %s" %
 
180
                      other_path)
 
181
        parent_dir = self.add_missing_parents(file_id, self.other_tree)
 
182
        stem = os.path.join(parent_dir, os.path.basename(other_path))
 
183
        self.create(file_id, stem+".OTHER", self.other_tree)
 
184
        self.create(file_id, stem+".BASE", self.base_tree)
 
185
 
134
186
    def finalize(self):
135
187
        if not self.ignore_zero:
136
188
            print "%d conflicts encountered.\n" % self.conflicts
220
272
                    path = os.path.join(self.tempdir, "texts", id)
221
273
                    outfile = file(path, "wb")
222
274
                    outfile.write(self.tree.get_file(id).read())
223
 
                    assert(os.path.exists(path))
 
275
                    assert(bzrlib.osutils.lexists(path))
224
276
                else:
225
277
                    assert kind == "symlink"
226
278
                    path = os.path.join(self.tempdir, "symlinks", id)
230
282
            return self.cached[id]
231
283
 
232
284
 
 
285
def build_working_dir(to_dir):
 
286
    """Build a working directory in an empty directory.
 
287
 
 
288
    to_dir is a directory containing branch metadata but no working files,
 
289
    typically constructed by cloning an existing branch. 
 
290
 
 
291
    This is split out as a special idiomatic case of merge.  It could
 
292
    eventually be done by just building the tree directly calling into 
 
293
    lower-level code (e.g. constructing a changeset).
 
294
    """
 
295
    merge((to_dir, -1), (to_dir, 0), this_dir=to_dir,
 
296
          check_clean=False, ignore_zero=True)
 
297
 
233
298
 
234
299
def merge(other_revision, base_revision,
235
300
          check_clean=True, ignore_zero=False,
246
311
    check_clean
247
312
        If true, this_dir must have no uncommitted changes before the
248
313
        merge begins.
 
314
    ignore_zero - If true, suppress the "zero conflicts" message when 
 
315
        there are no conflicts; should be set when doing something we expect
 
316
        to complete perfectly.
249
317
 
250
318
    All available ancestors of other_revision and base_revision are
251
319
    automatically pulled into the branch.
359
427
 
360
428
    inv_changes = merge_flex(this_tree, base_tree, other_tree,
361
429
                             generate_cset_optimized, get_inventory,
362
 
                             MergeConflictHandler(ignore_zero=ignore_zero),
 
430
                             MergeConflictHandler(this_tree, base_tree,
 
431
                             other_tree, ignore_zero=ignore_zero),
363
432
                             merge_factory=merge_factory, 
364
433
                             interesting_ids=interesting_ids)
365
434