~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-11 14:26:41 UTC
  • mto: This revision was merged to the branch mainline in revision 5852.
  • Revision ID: jelmer@samba.org-20110511142641-wbx6svyykkgtq24u
Use osutils.sha_string() when possible.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
    bencode,
33
33
    bzrdir,
34
34
    commit,
35
 
    conflicts,
36
35
    delta,
37
36
    errors,
38
37
    inventory,
138
137
        # A counter of how many files have been renamed
139
138
        self.rename_count = 0
140
139
 
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
 
 
149
140
    def finalize(self):
150
141
        """Release the working tree lock, if held.
151
142
 
226
217
        This means that the old root trans-id becomes obsolete, so it is
227
218
        recommended only to invoke this after the root trans-id has become
228
219
        irrelevant.
229
 
 
230
220
        """
231
221
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
232
222
                     ROOT_PARENT]
238
228
            self._new_root = new_roots[0]
239
229
            return
240
230
        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
 
241
236
        # unversion the new root's directory.
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)
 
237
        file_id = self.final_file_id(old_new_root)
246
238
        if old_new_root in self._new_id:
247
239
            self.cancel_versioning(old_new_root)
248
240
        else:
252
244
        if (self.tree_file_id(self._new_root) is not None and
253
245
            self._new_root not in self._removed_id):
254
246
            self.unversion_file(self._new_root)
255
 
        if file_id is not None:
256
 
            self.version_file(file_id, self._new_root)
 
247
        self.version_file(file_id, self._new_root)
257
248
 
258
249
        # Now move children of new root into old root directory.
259
250
        # Ensure all children are registered with the transaction, but don't
393
384
        return sorted(FinalPaths(self).get_paths(new_ids))
394
385
 
395
386
    def _inventory_altered(self):
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,
 
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,
414
390
                       self._new_executability]:
415
 
            changed_ids.update(id_set)
416
 
        # removing implies a kind change
 
391
            new_ids.update(id_set)
417
392
        changed_kind = set(self._removed_contents)
418
 
        # so does adding
419
393
        changed_kind.intersection_update(self._new_contents)
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
 
394
        changed_kind.difference_update(new_ids)
423
395
        changed_kind = (t for t in changed_kind
424
396
                        if self.tree_kind(t) != self.final_kind(t))
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))
 
397
        new_ids.update(changed_kind)
 
398
        return sorted(FinalPaths(self).get_paths(new_ids))
434
399
 
435
400
    def final_kind(self, trans_id):
436
401
        """Determine the final file kind, after any changes applied.
1189
1154
        self._deletiondir = None
1190
1155
        # A mapping of transform ids to their limbo filename
1191
1156
        self._limbo_files = {}
1192
 
        self._possibly_stale_limbo_files = set()
1193
1157
        # A mapping of transform ids to a set of the transform ids of children
1194
1158
        # that their limbo directory has
1195
1159
        self._limbo_children = {}
1208
1172
        if self._tree is None:
1209
1173
            return
1210
1174
        try:
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?
 
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)
1223
1180
            try:
1224
1181
                delete_any(self._limbodir)
1225
1182
            except OSError:
1274
1231
        entries from _limbo_files, because they are now stale.
1275
1232
        """
1276
1233
        for trans_id in trans_ids:
1277
 
            old_path = self._limbo_files[trans_id]
1278
 
            self._possibly_stale_limbo_files.add(old_path)
1279
 
            del self._limbo_files[trans_id]
 
1234
            old_path = self._limbo_files.pop(trans_id)
1280
1235
            if trans_id not in self._new_contents:
1281
1236
                continue
1282
1237
            new_path = self._limbo_name(trans_id)
1283
1238
            os.rename(old_path, new_path)
1284
 
            self._possibly_stale_limbo_files.remove(old_path)
1285
1239
            for descendant in self._limbo_descendants(trans_id):
1286
1240
                desc_path = self._limbo_files[descendant]
1287
1241
                desc_path = new_path + desc_path[len(old_path):]
1311
1265
        name = self._limbo_name(trans_id)
1312
1266
        f = open(name, 'wb')
1313
1267
        try:
1314
 
            unique_add(self._new_contents, trans_id, 'file')
 
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
1315
1276
            f.writelines(contents)
1316
1277
        finally:
1317
1278
            f.close()
1748
1709
                mover.apply_deletions()
1749
1710
        finally:
1750
1711
            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] != '']
1753
1712
        self._tree.apply_inventory_delta(inventory_delta)
1754
1713
        self._apply_observed_sha1s()
1755
1714
        self._done = True
1829
1788
        tree_paths.sort(reverse=True)
1830
1789
        child_pb = ui.ui_factory.nested_progress_bar()
1831
1790
        try:
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
 
1791
            for num, data in enumerate(tree_paths):
 
1792
                path, trans_id = data
1836
1793
                child_pb.update('removing file', num, len(tree_paths))
1837
1794
                full_path = self._tree.abspath(path)
1838
1795
                if trans_id in self._removed_contents:
1894
1851
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1895
1852
        finally:
1896
1853
            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]
1902
1854
        self._new_contents.clear()
1903
1855
        return modified_paths
1904
1856
 
2258
2210
        else:
2259
2211
            return None
2260
2212
 
2261
 
    def get_file_verifier(self, file_id, path=None, stat_value=None):
2262
 
        trans_id = self._transform.trans_id_file_id(file_id)
2263
 
        kind = self._transform._new_contents.get(trans_id)
2264
 
        if kind is None:
2265
 
            return self._transform._tree.get_file_verifier(file_id)
2266
 
        if kind == 'file':
2267
 
            fileobj = self.get_file(file_id)
2268
 
            try:
2269
 
                return ("SHA1", sha_file(fileobj))
2270
 
            finally:
2271
 
                fileobj.close()
2272
 
 
2273
2213
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2274
2214
        trans_id = self._transform.trans_id_file_id(file_id)
2275
2215
        kind = self._transform._new_contents.get(trans_id)
2400
2340
                                   self.get_file(file_id).readlines(),
2401
2341
                                   default_revision)
2402
2342
 
2403
 
    def get_symlink_target(self, file_id, path=None):
 
2343
    def get_symlink_target(self, file_id):
2404
2344
        """See Tree.get_symlink_target"""
2405
2345
        if not self._content_change(file_id):
2406
2346
            return self._transform._tree.get_symlink_target(file_id)
2638
2578
            precomputed_delta = None
2639
2579
        conflicts = cook_conflicts(raw_conflicts, tt)
2640
2580
        for conflict in conflicts:
2641
 
            trace.warning(unicode(conflict))
 
2581
            trace.warning(conflict)
2642
2582
        try:
2643
2583
            wt.add_conflicts(conflicts)
2644
2584
        except errors.UnsupportedOperation:
2880
2820
                unversioned_filter=working_tree.is_ignored)
2881
2821
            delta.report_changes(tt.iter_changes(), change_reporter)
2882
2822
        for conflict in conflicts:
2883
 
            trace.warning(unicode(conflict))
 
2823
            trace.warning(conflict)
2884
2824
        pp.next_phase()
2885
2825
        tt.apply()
2886
2826
        working_tree.set_merge_modified(merge_modified)
2952
2892
                        if basis_tree is None:
2953
2893
                            basis_tree = working_tree.basis_tree()
2954
2894
                            basis_tree.lock_read()
2955
 
                        if basis_tree.has_id(file_id):
 
2895
                        if file_id in basis_tree:
2956
2896
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
2957
2897
                                keep_content = True
2958
2898
                        elif target_kind is None and not target_versioned:
2988
2928
                        basis_tree = working_tree.basis_tree()
2989
2929
                        basis_tree.lock_read()
2990
2930
                    new_sha1 = target_tree.get_file_sha1(file_id)
2991
 
                    if (basis_tree.has_id(file_id) and
2992
 
                        new_sha1 == basis_tree.get_file_sha1(file_id)):
 
2931
                    if (file_id in basis_tree and new_sha1 ==
 
2932
                        basis_tree.get_file_sha1(file_id)):
2993
2933
                        if file_id in merge_modified:
2994
2934
                            del merge_modified[file_id]
2995
2935
                    else:
3146
3086
        elif c_type == 'unversioned parent':
3147
3087
            file_id = tt.inactive_file_id(conflict[1])
3148
3088
            # special-case the other tree root (move its children instead)
3149
 
            if path_tree and path_tree.has_id(file_id):
 
3089
            if path_tree and file_id in path_tree:
3150
3090
                if path_tree.path2id('') == file_id:
3151
3091
                    # This is the root entry, skip it
3152
3092
                    continue
3170
3110
 
3171
3111
def cook_conflicts(raw_conflicts, tt):
3172
3112
    """Generate a list of cooked conflicts, sorted by file path"""
 
3113
    from bzrlib.conflicts import Conflict
3173
3114
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
3174
 
    return sorted(conflict_iter, key=conflicts.Conflict.sort_key)
 
3115
    return sorted(conflict_iter, key=Conflict.sort_key)
3175
3116
 
3176
3117
 
3177
3118
def iter_cook_conflicts(raw_conflicts, tt):
 
3119
    from bzrlib.conflicts import Conflict
3178
3120
    fp = FinalPaths(tt)
3179
3121
    for conflict in raw_conflicts:
3180
3122
        c_type = conflict[0]
3182
3124
        modified_path = fp.get_path(conflict[2])
3183
3125
        modified_id = tt.final_file_id(conflict[2])
3184
3126
        if len(conflict) == 3:
3185
 
            yield conflicts.Conflict.factory(
3186
 
                c_type, action=action, path=modified_path, file_id=modified_id)
 
3127
            yield Conflict.factory(c_type, action=action, path=modified_path,
 
3128
                                     file_id=modified_id)
3187
3129
 
3188
3130
        else:
3189
3131
            conflicting_path = fp.get_path(conflict[3])
3190
3132
            conflicting_id = tt.final_file_id(conflict[3])
3191
 
            yield conflicts.Conflict.factory(
3192
 
                c_type, action=action, path=modified_path,
3193
 
                file_id=modified_id,
3194
 
                conflict_path=conflicting_path,
3195
 
                conflict_file_id=conflicting_id)
 
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)
3196
3137
 
3197
3138
 
3198
3139
class _FileMover(object):