~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Martin Pool
  • Date: 2005-08-24 08:59:32 UTC
  • Revision ID: mbp@sourcefrog.net-20050824085932-c61f1f1f1c930e13
- Add a simple UIFactory 

  The idea of this is to let a client of bzrlib set some 
  policy about how output is displayed.

  In this revision all that's done is that progress bars
  are constructed by a policy established by the application
  rather than being randomly constructed in the library 
  or passed down the calls.  This avoids progress bars
  popping up while running the test suite and cleans up
  some code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
48
48
            d_file.write(line)
49
49
        os.chmod(dest, 0777 & os.stat(source).st_mode)
50
50
 
 
51
    def dump(self, lines, dest):
 
52
        """Copy the text and mode of a file
 
53
        :param source: The path of the file to copy
 
54
        :param dest: The distination file to create
 
55
        """
 
56
        d_file = file(dest, "wb")
 
57
        for line in lines:
 
58
            d_file.write(line)
 
59
 
51
60
    def add_suffix(self, name, suffix, last_new_name=None):
52
61
        """Rename a file to append a suffix.  If the new name exists, the
53
62
        suffix is added repeatedly until a non-existant name is found
72
81
        self.conflicts += 1
73
82
        
74
83
 
75
 
    def merge_conflict(self, new_file, this_path, base_path, other_path):
 
84
    def merge_conflict(self, new_file, this_path, base_lines, other_lines):
76
85
        """
77
86
        Handle diff3 conflicts by producing a .THIS, .BASE and .OTHER.  The
78
87
        main file will be a version with diff3 conflicts.
82
91
        :param other_path: Path to the file text for the OTHER tree
83
92
        """
84
93
        self.add_suffix(this_path, ".THIS")
85
 
        self.copy(base_path, this_path+".BASE")
86
 
        self.copy(other_path, this_path+".OTHER")
 
94
        self.dump(base_lines, this_path+".BASE")
 
95
        self.dump(other_lines, this_path+".OTHER")
87
96
        os.rename(new_file, this_path)
88
97
        self.conflict("Diff3 conflict encountered in %s" % this_path)
89
98
 
108
117
        if not self.ignore_zero:
109
118
            print "%d conflicts encountered.\n" % self.conflicts
110
119
            
111
 
class SourceFile(object):
112
 
    def __init__(self, path, id, present=None, isdir=None):
113
 
        self.path = path
114
 
        self.id = id
115
 
        self.present = present
116
 
        self.isdir = isdir
117
 
        self.interesting = True
118
 
 
119
 
    def __repr__(self):
120
 
        return "SourceFile(%s, %s)" % (self.path, self.id)
121
 
 
122
120
def get_tree(treespec, temp_root, label):
123
121
    location, revno = treespec
124
122
    branch = find_branch(location)
133
131
    return branch, MergeTree(base_tree, temp_path)
134
132
 
135
133
 
136
 
def abspath(tree, file_id):
137
 
    path = tree.inventory.id2path(file_id)
138
 
    if path == "":
139
 
        return "./."
140
 
    return "./" + path
141
 
 
142
134
def file_exists(tree, file_id):
143
135
    return tree.has_filename(tree.id2path(file_id))
144
136
    
145
 
def inventory_map(tree):
146
 
    inventory = {}
147
 
    for file_id in tree.inventory:
148
 
        path = abspath(tree, file_id)
149
 
        inventory[path] = SourceFile(path, file_id)
150
 
    return inventory
151
 
 
152
137
 
153
138
class MergeTree(object):
154
139
    def __init__(self, tree, tempdir):
157
142
            self.root = tree.basedir
158
143
        else:
159
144
            self.root = None
160
 
        self.inventory = inventory_map(tree)
161
145
        self.tree = tree
162
146
        self.tempdir = tempdir
163
147
        os.mkdir(os.path.join(self.tempdir, "texts"))
164
148
        self.cached = {}
165
149
 
 
150
    def __iter__(self):
 
151
        return self.tree.__iter__()
 
152
 
166
153
    def __contains__(self, file_id):
167
 
        return id in self.tree
 
154
        return file_id in self.tree
 
155
 
 
156
    def get_file(self, file_id):
 
157
        return self.tree.get_file(file_id)
168
158
 
169
159
    def get_file_sha1(self, id):
170
160
        return self.tree.get_file_sha1(id)
171
161
 
 
162
    def id2path(self, file_id):
 
163
        return self.tree.id2path(file_id)
 
164
 
 
165
    def has_id(self, file_id):
 
166
        return self.tree.has_id(file_id)
 
167
 
 
168
    def has_or_had_id(self, file_id):
 
169
        if file_id == self.tree.inventory.root.file_id:
 
170
            return True
 
171
        return self.tree.inventory.has_id(file_id)
 
172
 
172
173
    def readonly_path(self, id):
173
174
        if id not in self.tree:
174
175
            return None
256
257
             source_file.interesting = source_file.id in interesting_ids
257
258
 
258
259
 
259
 
def generate_cset_optimized(tree_a, tree_b, inventory_a, inventory_b,
260
 
                            interesting_ids=None):
 
260
def generate_cset_optimized(tree_a, tree_b, interesting_ids=None):
261
261
    """Generate a changeset.  If interesting_ids is supplied, only changes
262
262
    to those files will be shown.  Metadata changes are stripped.
263
263
    """ 
264
 
    if interesting_ids is not None:
265
 
        set_interesting(inventory_a, inventory_b, interesting_ids)
266
 
    cset =  generate_changeset(tree_a, tree_b, inventory_a, inventory_b)
 
264
    cset =  generate_changeset(tree_a, tree_b, interesting_ids)
267
265
    for entry in cset.entries.itervalues():
268
266
        entry.metadata_change = None
269
267
    return cset
273
271
                ignore_zero=False, merge_type=ApplyMerge3, backup_files=False,
274
272
                interesting_ids=None):
275
273
 
276
 
    def merge_factory(base_file, other_file):
277
 
        contents_change = merge_type(base_file, other_file)
 
274
    def merge_factory(file_id, base, other):
 
275
        contents_change = merge_type(file_id, base, other)
278
276
        if backup_files:
279
277
            contents_change = BackupBeforeChange(contents_change)
280
278
        return contents_change
281
 
    
282
 
    def generate_cset(tree_a, tree_b, inventory_a, inventory_b):
283
 
        return generate_cset_optimized(tree_a, tree_b, inventory_a, inventory_b,
284
 
                                       interesting_ids)
285
279
 
286
280
    this_tree = get_tree((this_branch.base, None), tempdir, "this")[1]
287
281
 
288
282
    def get_inventory(tree):
289
 
        return tree.inventory
 
283
        return tree.tree.inventory
290
284
 
291
285
    inv_changes = merge_flex(this_tree, base_tree, other_tree,
292
 
                             generate_cset, get_inventory,
 
286
                             generate_cset_optimized, get_inventory,
293
287
                             MergeConflictHandler(base_tree.root,
294
288
                                                  ignore_zero=ignore_zero),
295
 
                             merge_factory=merge_factory)
 
289
                             merge_factory=merge_factory, 
 
290
                             interesting_ids=interesting_ids)
296
291
 
297
292
    adjust_ids = []
298
293
    for id, path in inv_changes.iteritems():
300
295
            if path == '.':
301
296
                path = ''
302
297
            else:
303
 
                assert path.startswith('./')
 
298
                assert path.startswith('./'), "path is %s" % path
304
299
            path = path[2:]
305
300
        adjust_ids.append((path, id))
306
301
    if len(adjust_ids) > 0:
312
307
    old_entries = this_branch.read_working_inventory()
313
308
    new_inventory = {}
314
309
    by_path = {}
 
310
    new_entries_map = {} 
 
311
    for path, file_id in new_entries:
 
312
        if path is None:
 
313
            continue
 
314
        new_entries_map[file_id] = path
 
315
 
 
316
    def id2path(file_id):
 
317
        path = new_entries_map.get(file_id)
 
318
        if path is not None:
 
319
            return path
 
320
        entry = old_entries[file_id]
 
321
        if entry.parent_id is None:
 
322
            return entry.name
 
323
        return os.path.join(id2path(entry.parent_id), entry.name)
 
324
        
315
325
    for file_id in old_entries:
316
326
        entry = old_entries[file_id]
317
 
        path = old_entries.id2path(file_id)
 
327
        path = id2path(file_id)
318
328
        new_inventory[file_id] = (path, file_id, entry.parent_id, entry.kind)
319
329
        by_path[path] = file_id
320
330