~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

(jam) Bug #494269, TreeTransform can handle root-id changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009, 2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
161
161
 
162
162
    def adjust_path(self, name, parent, trans_id):
163
163
        """Change the path that is assigned to a transaction id."""
 
164
        if parent is None:
 
165
            raise ValueError("Parent trans-id may not be None")
164
166
        if trans_id == self._new_root:
165
167
            raise CantMoveRoot
166
168
        self._new_name[trans_id] = name
167
169
        self._new_parent[trans_id] = parent
168
 
        if parent == ROOT_PARENT:
169
 
            if self._new_root is not None:
170
 
                raise ValueError("Cannot have multiple roots.")
171
 
            self._new_root = trans_id
172
170
 
173
171
    def adjust_root_path(self, name, parent):
174
172
        """Emulate moving the root by moving all children, instead.
202
200
        self.version_file(old_root_file_id, old_root)
203
201
        self.unversion_file(self._new_root)
204
202
 
 
203
    def fixup_new_roots(self):
 
204
        """Reinterpret requests to change the root directory
 
205
 
 
206
        Instead of creating a root directory, or moving an existing directory,
 
207
        all the attributes and children of the new root are applied to the
 
208
        existing root directory.
 
209
 
 
210
        This means that the old root trans-id becomes obsolete, so it is
 
211
        recommended only to invoke this after the root trans-id has become
 
212
        irrelevant.
 
213
        """
 
214
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
 
215
                     ROOT_PARENT]
 
216
        if len(new_roots) < 1:
 
217
            return
 
218
        if len(new_roots) != 1:
 
219
            raise ValueError('A tree cannot have two roots!')
 
220
        if self._new_root is None:
 
221
            self._new_root = new_roots[0]
 
222
            return
 
223
        old_new_root = new_roots[0]
 
224
        # TODO: What to do if a old_new_root is present, but self._new_root is
 
225
        #       not listed as being removed? This code explicitly unversions
 
226
        #       the old root and versions it with the new file_id. Though that
 
227
        #       seems like an incomplete delta
 
228
 
 
229
        # unversion the new root's directory.
 
230
        file_id = self.final_file_id(old_new_root)
 
231
        if old_new_root in self._new_id:
 
232
            self.cancel_versioning(old_new_root)
 
233
        else:
 
234
            self.unversion_file(old_new_root)
 
235
        # if, at this stage, root still has an old file_id, zap it so we can
 
236
        # stick a new one in.
 
237
        if (self.tree_file_id(self._new_root) is not None and
 
238
            self._new_root not in self._removed_id):
 
239
            self.unversion_file(self._new_root)
 
240
        self.version_file(file_id, self._new_root)
 
241
 
 
242
        # Now move children of new root into old root directory.
 
243
        # Ensure all children are registered with the transaction, but don't
 
244
        # use directly-- some tree children have new parents
 
245
        list(self.iter_tree_children(old_new_root))
 
246
        # Move all children of new root into old root directory.
 
247
        for child in self.by_parent().get(old_new_root, []):
 
248
            self.adjust_path(self.final_name(child), self._new_root, child)
 
249
 
 
250
        # Ensure old_new_root has no directory.
 
251
        if old_new_root in self._new_contents:
 
252
            self.cancel_creation(old_new_root)
 
253
        else:
 
254
            self.delete_contents(old_new_root)
 
255
 
 
256
        # prevent deletion of root directory.
 
257
        if self._new_root in self._removed_contents:
 
258
            self.cancel_deletion(self._new_root)
 
259
 
 
260
        # destroy path info for old_new_root.
 
261
        del self._new_parent[old_new_root]
 
262
        del self._new_name[old_new_root]
 
263
 
205
264
    def trans_id_tree_file_id(self, inventory_id):
206
265
        """Determine the transaction id of a working tree file.
207
266
 
253
312
 
254
313
    def delete_contents(self, trans_id):
255
314
        """Schedule the contents of a path entry for deletion"""
 
315
        # Ensure that the object exists in the WorkingTree, this will raise an
 
316
        # exception if there is a problem
256
317
        self.tree_kind(trans_id)
257
318
        self._removed_contents.add(trans_id)
258
319
 
1075
1136
        if (trans_id in self._limbo_files and
1076
1137
            trans_id not in self._needs_rename):
1077
1138
            self._rename_in_limbo([trans_id])
1078
 
            self._limbo_children[previous_parent].remove(trans_id)
1079
 
            del self._limbo_children_names[previous_parent][previous_name]
 
1139
            if previous_parent != parent:
 
1140
                self._limbo_children[previous_parent].remove(trans_id)
 
1141
            if previous_parent != parent or previous_name != name:
 
1142
                del self._limbo_children_names[previous_parent][previous_name]
1080
1143
 
1081
1144
    def _rename_in_limbo(self, trans_ids):
1082
1145
        """Fix limbo names so that the right final path is produced.
1542
1605
                child_pb.update('removing file', num, len(tree_paths))
1543
1606
                full_path = self._tree.abspath(path)
1544
1607
                if trans_id in self._removed_contents:
1545
 
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
1546
 
                                     trans_id))
1547
 
                elif trans_id in self._new_name or trans_id in \
1548
 
                    self._new_parent:
 
1608
                    delete_path = os.path.join(self._deletiondir, trans_id)
 
1609
                    mover.pre_delete(full_path, delete_path)
 
1610
                elif (trans_id in self._new_name
 
1611
                      or trans_id in self._new_parent):
1549
1612
                    try:
1550
1613
                        mover.rename(full_path, self._limbo_name(trans_id))
1551
1614
                    except OSError, e:
2636
2699
                    parent_trans = ROOT_PARENT
2637
2700
                else:
2638
2701
                    parent_trans = tt.trans_id_file_id(parent[1])
2639
 
                tt.adjust_path(name[1], parent_trans, trans_id)
 
2702
                if parent[0] is None and versioned[0]:
 
2703
                    tt.adjust_root_path(name[1], parent_trans)
 
2704
                else:
 
2705
                    tt.adjust_path(name[1], parent_trans, trans_id)
2640
2706
            if executable[0] != executable[1] and kind[1] == "file":
2641
2707
                tt.set_executability(executable[1], trans_id)
2642
2708
        if working_tree.supports_content_filtering():
2655
2721
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2656
2722
                deferred_files):
2657
2723
                tt.create_file(bytes, trans_id, mode_id)
 
2724
        tt.fixup_new_roots()
2658
2725
    finally:
2659
2726
        if basis_tree is not None:
2660
2727
            basis_tree.unlock()