~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2011-08-04 13:30:30 UTC
  • mfrom: (6050 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6052.
  • Revision ID: jelmer@samba.org-20110804133030-uwo00unp8b0n782c
merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
    bencode,
33
33
    bzrdir,
34
34
    commit,
 
35
    conflicts,
35
36
    delta,
36
37
    errors,
37
38
    inventory,
137
138
        # A counter of how many files have been renamed
138
139
        self.rename_count = 0
139
140
 
 
141
    def __enter__(self):
 
142
        """Support Context Manager API."""
 
143
        return self
 
144
 
 
145
    def __exit__(self, exc_type, exc_val, exc_tb):
 
146
        """Support Context Manager API."""
 
147
        self.finalize()
 
148
 
140
149
    def finalize(self):
141
150
        """Release the working tree lock, if held.
142
151
 
217
226
        This means that the old root trans-id becomes obsolete, so it is
218
227
        recommended only to invoke this after the root trans-id has become
219
228
        irrelevant.
 
229
 
220
230
        """
221
231
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
222
232
                     ROOT_PARENT]
228
238
            self._new_root = new_roots[0]
229
239
            return
230
240
        old_new_root = new_roots[0]
231
 
        # TODO: What to do if a old_new_root is present, but self._new_root is
232
 
        #       not listed as being removed? This code explicitly unversions
233
 
        #       the old root and versions it with the new file_id. Though that
234
 
        #       seems like an incomplete delta
235
 
 
236
241
        # unversion the new root's directory.
237
 
        file_id = self.final_file_id(old_new_root)
 
242
        if self.final_kind(self._new_root) is None:
 
243
            file_id = self.final_file_id(old_new_root)
 
244
        else:
 
245
            file_id = self.final_file_id(self._new_root)
238
246
        if old_new_root in self._new_id:
239
247
            self.cancel_versioning(old_new_root)
240
248
        else:
244
252
        if (self.tree_file_id(self._new_root) is not None and
245
253
            self._new_root not in self._removed_id):
246
254
            self.unversion_file(self._new_root)
247
 
        self.version_file(file_id, self._new_root)
 
255
        if file_id is not None:
 
256
            self.version_file(file_id, self._new_root)
248
257
 
249
258
        # Now move children of new root into old root directory.
250
259
        # Ensure all children are registered with the transaction, but don't
384
393
        return sorted(FinalPaths(self).get_paths(new_ids))
385
394
 
386
395
    def _inventory_altered(self):
387
 
        """Get the trans_ids and paths of files needing new inv entries."""
388
 
        new_ids = set()
389
 
        for id_set in [self._new_name, self._new_parent, self._new_id,
 
396
        """Determine which trans_ids need new Inventory entries.
 
397
 
 
398
        An new entry is needed when anything that would be reflected by an
 
399
        inventory entry changes, including file name, file_id, parent file_id,
 
400
        file kind, and the execute bit.
 
401
 
 
402
        Some care is taken to return entries with real changes, not cases
 
403
        where the value is deleted and then restored to its original value,
 
404
        but some actually unchanged values may be returned.
 
405
 
 
406
        :returns: A list of (path, trans_id) for all items requiring an
 
407
            inventory change. Ordered by path.
 
408
        """
 
409
        changed_ids = set()
 
410
        # Find entries whose file_ids are new (or changed).
 
411
        new_file_id = set(t for t in self._new_id
 
412
                          if self._new_id[t] != self.tree_file_id(t))
 
413
        for id_set in [self._new_name, self._new_parent, new_file_id,
390
414
                       self._new_executability]:
391
 
            new_ids.update(id_set)
 
415
            changed_ids.update(id_set)
 
416
        # removing implies a kind change
392
417
        changed_kind = set(self._removed_contents)
 
418
        # so does adding
393
419
        changed_kind.intersection_update(self._new_contents)
394
 
        changed_kind.difference_update(new_ids)
 
420
        # Ignore entries that are already known to have changed.
 
421
        changed_kind.difference_update(changed_ids)
 
422
        #  to keep only the truly changed ones
395
423
        changed_kind = (t for t in changed_kind
396
424
                        if self.tree_kind(t) != self.final_kind(t))
397
 
        new_ids.update(changed_kind)
398
 
        return sorted(FinalPaths(self).get_paths(new_ids))
 
425
        # all kind changes will alter the inventory
 
426
        changed_ids.update(changed_kind)
 
427
        # To find entries with changed parent_ids, find parents which existed,
 
428
        # but changed file_id.
 
429
        changed_file_id = set(t for t in new_file_id if t in self._removed_id)
 
430
        # Now add all their children to the set.
 
431
        for parent_trans_id in new_file_id:
 
432
            changed_ids.update(self.iter_tree_children(parent_trans_id))
 
433
        return sorted(FinalPaths(self).get_paths(changed_ids))
399
434
 
400
435
    def final_kind(self, trans_id):
401
436
        """Determine the final file kind, after any changes applied.
1154
1189
        self._deletiondir = None
1155
1190
        # A mapping of transform ids to their limbo filename
1156
1191
        self._limbo_files = {}
 
1192
        self._possibly_stale_limbo_files = set()
1157
1193
        # A mapping of transform ids to a set of the transform ids of children
1158
1194
        # that their limbo directory has
1159
1195
        self._limbo_children = {}
1172
1208
        if self._tree is None:
1173
1209
            return
1174
1210
        try:
1175
 
            entries = [(self._limbo_name(t), t, k) for t, k in
1176
 
                       self._new_contents.iteritems()]
1177
 
            entries.sort(reverse=True)
1178
 
            for path, trans_id, kind in entries:
1179
 
                delete_any(path)
 
1211
            limbo_paths = self._limbo_files.values() + list(
 
1212
                self._possibly_stale_limbo_files)
 
1213
            limbo_paths = sorted(limbo_paths, reverse=True)
 
1214
            for path in limbo_paths:
 
1215
                try:
 
1216
                    delete_any(path)
 
1217
                except OSError, e:
 
1218
                    if e.errno != errno.ENOENT:
 
1219
                        raise
 
1220
                    # XXX: warn? perhaps we just got interrupted at an
 
1221
                    # inconvenient moment, but perhaps files are disappearing
 
1222
                    # from under us?
1180
1223
            try:
1181
1224
                delete_any(self._limbodir)
1182
1225
            except OSError:
1231
1274
        entries from _limbo_files, because they are now stale.
1232
1275
        """
1233
1276
        for trans_id in trans_ids:
1234
 
            old_path = self._limbo_files.pop(trans_id)
 
1277
            old_path = self._limbo_files[trans_id]
 
1278
            self._possibly_stale_limbo_files.add(old_path)
 
1279
            del self._limbo_files[trans_id]
1235
1280
            if trans_id not in self._new_contents:
1236
1281
                continue
1237
1282
            new_path = self._limbo_name(trans_id)
1238
1283
            os.rename(old_path, new_path)
 
1284
            self._possibly_stale_limbo_files.remove(old_path)
1239
1285
            for descendant in self._limbo_descendants(trans_id):
1240
1286
                desc_path = self._limbo_files[descendant]
1241
1287
                desc_path = new_path + desc_path[len(old_path):]
1265
1311
        name = self._limbo_name(trans_id)
1266
1312
        f = open(name, 'wb')
1267
1313
        try:
1268
 
            try:
1269
 
                unique_add(self._new_contents, trans_id, 'file')
1270
 
            except:
1271
 
                # Clean up the file, it never got registered so
1272
 
                # TreeTransform.finalize() won't clean it up.
1273
 
                f.close()
1274
 
                os.unlink(name)
1275
 
                raise
 
1314
            unique_add(self._new_contents, trans_id, 'file')
1276
1315
            f.writelines(contents)
1277
1316
        finally:
1278
1317
            f.close()
1709
1748
                mover.apply_deletions()
1710
1749
        finally:
1711
1750
            child_pb.finished()
 
1751
        if self.final_file_id(self.root) is None:
 
1752
            inventory_delta = [e for e in inventory_delta if e[0] != '']
1712
1753
        self._tree.apply_inventory_delta(inventory_delta)
1713
1754
        self._apply_observed_sha1s()
1714
1755
        self._done = True
1788
1829
        tree_paths.sort(reverse=True)
1789
1830
        child_pb = ui.ui_factory.nested_progress_bar()
1790
1831
        try:
1791
 
            for num, data in enumerate(tree_paths):
1792
 
                path, trans_id = data
 
1832
            for num, (path, trans_id) in enumerate(tree_paths):
 
1833
                # do not attempt to move root into a subdirectory of itself.
 
1834
                if path == '':
 
1835
                    continue
1793
1836
                child_pb.update('removing file', num, len(tree_paths))
1794
1837
                full_path = self._tree.abspath(path)
1795
1838
                if trans_id in self._removed_contents:
1851
1894
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1852
1895
        finally:
1853
1896
            child_pb.finished()
 
1897
        for path, trans_id in new_paths:
 
1898
            # new_paths includes stuff like workingtree conflicts. Only the
 
1899
            # stuff in new_contents actually comes from limbo.
 
1900
            if trans_id in self._limbo_files:
 
1901
                del self._limbo_files[trans_id]
1854
1902
        self._new_contents.clear()
1855
1903
        return modified_paths
1856
1904
 
2578
2626
            precomputed_delta = None
2579
2627
        conflicts = cook_conflicts(raw_conflicts, tt)
2580
2628
        for conflict in conflicts:
2581
 
            trace.warning(conflict)
 
2629
            trace.warning(unicode(conflict))
2582
2630
        try:
2583
2631
            wt.add_conflicts(conflicts)
2584
2632
        except errors.UnsupportedOperation:
2820
2868
                unversioned_filter=working_tree.is_ignored)
2821
2869
            delta.report_changes(tt.iter_changes(), change_reporter)
2822
2870
        for conflict in conflicts:
2823
 
            trace.warning(conflict)
 
2871
            trace.warning(unicode(conflict))
2824
2872
        pp.next_phase()
2825
2873
        tt.apply()
2826
2874
        working_tree.set_merge_modified(merge_modified)
2892
2940
                        if basis_tree is None:
2893
2941
                            basis_tree = working_tree.basis_tree()
2894
2942
                            basis_tree.lock_read()
2895
 
                        if file_id in basis_tree:
 
2943
                        if basis_tree.has_id(file_id):
2896
2944
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
2897
2945
                                keep_content = True
2898
2946
                        elif target_kind is None and not target_versioned:
2928
2976
                        basis_tree = working_tree.basis_tree()
2929
2977
                        basis_tree.lock_read()
2930
2978
                    new_sha1 = target_tree.get_file_sha1(file_id)
2931
 
                    if (file_id in basis_tree and new_sha1 ==
2932
 
                        basis_tree.get_file_sha1(file_id)):
 
2979
                    if (basis_tree.has_id(file_id) and
 
2980
                        new_sha1 == basis_tree.get_file_sha1(file_id)):
2933
2981
                        if file_id in merge_modified:
2934
2982
                            del merge_modified[file_id]
2935
2983
                    else:
3086
3134
        elif c_type == 'unversioned parent':
3087
3135
            file_id = tt.inactive_file_id(conflict[1])
3088
3136
            # special-case the other tree root (move its children instead)
3089
 
            if path_tree and file_id in path_tree:
 
3137
            if path_tree and path_tree.has_id(file_id):
3090
3138
                if path_tree.path2id('') == file_id:
3091
3139
                    # This is the root entry, skip it
3092
3140
                    continue
3110
3158
 
3111
3159
def cook_conflicts(raw_conflicts, tt):
3112
3160
    """Generate a list of cooked conflicts, sorted by file path"""
3113
 
    from bzrlib.conflicts import Conflict
3114
3161
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
3115
 
    return sorted(conflict_iter, key=Conflict.sort_key)
 
3162
    return sorted(conflict_iter, key=conflicts.Conflict.sort_key)
3116
3163
 
3117
3164
 
3118
3165
def iter_cook_conflicts(raw_conflicts, tt):
3119
 
    from bzrlib.conflicts import Conflict
3120
3166
    fp = FinalPaths(tt)
3121
3167
    for conflict in raw_conflicts:
3122
3168
        c_type = conflict[0]
3124
3170
        modified_path = fp.get_path(conflict[2])
3125
3171
        modified_id = tt.final_file_id(conflict[2])
3126
3172
        if len(conflict) == 3:
3127
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
3128
 
                                     file_id=modified_id)
 
3173
            yield conflicts.Conflict.factory(
 
3174
                c_type, action=action, path=modified_path, file_id=modified_id)
3129
3175
 
3130
3176
        else:
3131
3177
            conflicting_path = fp.get_path(conflict[3])
3132
3178
            conflicting_id = tt.final_file_id(conflict[3])
3133
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
3134
 
                                   file_id=modified_id,
3135
 
                                   conflict_path=conflicting_path,
3136
 
                                   conflict_file_id=conflicting_id)
 
3179
            yield conflicts.Conflict.factory(
 
3180
                c_type, action=action, path=modified_path,
 
3181
                file_id=modified_id,
 
3182
                conflict_path=conflicting_path,
 
3183
                conflict_file_id=conflicting_id)
3137
3184
 
3138
3185
 
3139
3186
class _FileMover(object):