~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Andrew Bennetts
  • Date: 2008-12-12 03:53:56 UTC
  • mto: This revision was merged to the branch mainline in revision 3900.
  • Revision ID: andrew.bennetts@canonical.com-20081212035356-uqcu89gy4nqf017x
Fix compilation error in _dirstate_helpers_c on SunOS/Solaris. (Jari Aalto)

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
18
18
import errno
36
36
                           ReusingTransform, NotVersionedError, CantMoveRoot,
37
37
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
38
38
                           UnableCreateSymlink)
39
 
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
40
39
from bzrlib.inventory import InventoryEntry
41
40
from bzrlib.osutils import (
42
41
    delete_any,
122
121
        self._non_present_ids = {}
123
122
        # Mapping of new file_id -> trans_id
124
123
        self._r_new_id = {}
125
 
        # Set of trans_ids that will be removed
 
124
        # Set of file_ids that will be removed
126
125
        self._removed_id = set()
127
126
        # Mapping of path in old tree -> trans_id
128
127
        self._tree_path_ids = {}
234
233
 
235
234
    def adjust_root_path(self, name, parent):
236
235
        """Emulate moving the root by moving all children, instead.
237
 
 
 
236
        
238
237
        We do this by undoing the association of root's transaction id with the
239
238
        current tree.  This allows us to create a new directory with that
240
 
        transaction id.  We unversion the root directory and version the
 
239
        transaction id.  We unversion the root directory and version the 
241
240
        physically new directory, and hope someone versions the tree root
242
241
        later.
243
242
        """
246
245
        # force moving all children of root
247
246
        for child_id in self.iter_tree_children(old_root):
248
247
            if child_id != parent:
249
 
                self.adjust_path(self.final_name(child_id),
 
248
                self.adjust_path(self.final_name(child_id), 
250
249
                                 self.final_parent(child_id), child_id)
251
250
            file_id = self.final_file_id(child_id)
252
251
            if file_id is not None:
253
252
                self.unversion_file(child_id)
254
253
            self.version_file(file_id, child_id)
255
 
 
 
254
        
256
255
        # the physical root needs a new transaction id
257
256
        self._tree_path_ids.pop("")
258
257
        self._tree_id_paths.pop(old_root)
266
265
 
267
266
    def trans_id_tree_file_id(self, inventory_id):
268
267
        """Determine the transaction id of a working tree file.
269
 
 
 
268
        
270
269
        This reflects only files that already exist, not ones that will be
271
270
        added by transactions.
272
271
        """
336
335
        """Schedule creation of a new file.
337
336
 
338
337
        See also new_file.
339
 
 
 
338
        
340
339
        Contents is an iterator of strings, all of which will be written
341
340
        to the target destination.
342
341
 
405
404
 
406
405
    def create_directory(self, trans_id):
407
406
        """Schedule creation of a new directory.
408
 
 
 
407
        
409
408
        See also new_directory.
410
409
        """
411
410
        os.mkdir(self._limbo_name(trans_id))
536
535
 
537
536
    def final_kind(self, trans_id):
538
537
        """Determine the final file kind, after any changes applied.
539
 
 
 
538
        
540
539
        Raises NoSuchFile if the file does not exist/has no contents.
541
540
        (It is conceivable that a path would be created without the
542
541
        corresponding contents insertion command)
562
561
 
563
562
    def final_file_id(self, trans_id):
564
563
        """Determine the file id after any changes are applied, or None.
565
 
 
 
564
        
566
565
        None indicates that the file will not be versioned after changes are
567
566
        applied.
568
567
        """
607
606
 
608
607
    def by_parent(self):
609
608
        """Return a map of parent: children for known parents.
610
 
 
 
609
        
611
610
        Only new paths and parents of tree files with assigned ids are used.
612
611
        """
613
612
        by_parent = {}
614
613
        items = list(self._new_parent.iteritems())
615
 
        items.extend((t, self.final_parent(t)) for t in
 
614
        items.extend((t, self.final_parent(t)) for t in 
616
615
                      self._tree_id_paths.keys())
617
616
        for trans_id, parent_id in items:
618
617
            if parent_id not in by_parent:
653
652
        removed.  This is a necessary first step in detecting conflicts.
654
653
        """
655
654
        parents = self.by_parent().keys()
656
 
        parents.extend([t for t in self._removed_contents if
 
655
        parents.extend([t for t in self._removed_contents if 
657
656
                        self.tree_kind(t) == 'directory'])
658
657
        for trans_id in self._removed_id:
659
658
            file_id = self.tree_file_id(trans_id)
746
745
 
747
746
    def _improper_versioning(self):
748
747
        """Cannot version a file with no contents, or a bad type.
749
 
 
 
748
        
750
749
        However, existing entries with no contents are okay.
751
750
        """
752
751
        conflicts = []
762
761
 
763
762
    def _executability_conflicts(self):
764
763
        """Check for bad executability changes.
765
 
 
 
764
        
766
765
        Only versioned files may have their executability set, because
767
766
        1. only versioned entries can have executability under windows
768
767
        2. only files can be executable.  (The execute bit on a directory
937
936
            self.version_file(file_id, trans_id)
938
937
        return trans_id
939
938
 
940
 
    def new_file(self, name, parent_id, contents, file_id=None,
 
939
    def new_file(self, name, parent_id, contents, file_id=None, 
941
940
                 executable=None):
942
941
        """Convenience method to create files.
943
 
 
 
942
        
944
943
        name is the name of the file to create.
945
944
        parent_id is the transaction id of the parent directory of the file.
946
945
        contents is an iterator of bytestrings, which will be used to produce
966
965
        """
967
966
        trans_id = self._new_entry(name, parent_id, file_id)
968
967
        self.create_directory(trans_id)
969
 
        return trans_id
 
968
        return trans_id 
970
969
 
971
970
    def new_symlink(self, name, parent_id, target, file_id=None):
972
971
        """Convenience method to create symbolic link.
973
 
 
 
972
        
974
973
        name is the name of the symlink to create.
975
974
        parent_id is the transaction id of the parent directory of the symlink.
976
975
        target is a bytestring of the target of the symlink.
1650
1649
    def has_id(self, file_id):
1651
1650
        if file_id in self._transform._r_new_id:
1652
1651
            return True
1653
 
        elif file_id in set([self._transform.tree_file_id(trans_id) for
1654
 
            trans_id in self._transform._removed_id]):
 
1652
        elif file_id in self._transform._removed_id:
1655
1653
            return False
1656
1654
        else:
1657
1655
            return self._transform._tree.has_id(file_id)
1846
1844
                size = None
1847
1845
                executable = None
1848
1846
            if kind == 'symlink':
1849
 
                link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
 
1847
                link_or_sha1 = os.readlink(limbo_name)
1850
1848
        if supports_executable():
1851
1849
            executable = tt._new_executability.get(trans_id, executable)
1852
1850
        return kind, size, executable, link_or_sha1
2001
1999
def build_tree(tree, wt, accelerator_tree=None, hardlink=False,
2002
2000
               delta_from_tree=False):
2003
2001
    """Create working tree for a branch, using a TreeTransform.
2004
 
 
 
2002
    
2005
2003
    This function should be used on empty trees, having a tree root at most.
2006
2004
    (see merge and revert functionality for working with existing trees)
2007
2005
 
2008
2006
    Existing files are handled like so:
2009
 
 
 
2007
    
2010
2008
    - Existing bzrdirs take precedence over creating new items.  They are
2011
2009
      created as '%s.diverted' % name.
2012
2010
    - Otherwise, if the content on disk matches the content we are building,
2114
2112
                    executable = tree.is_executable(file_id, tree_path)
2115
2113
                    if executable:
2116
2114
                        tt.set_executability(executable, trans_id)
2117
 
                    trans_data = (trans_id, tree_path)
2118
 
                    deferred_contents.append((file_id, trans_data))
 
2115
                    deferred_contents.append((file_id, trans_id))
2119
2116
                else:
2120
2117
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
2121
2118
                                                          tree)
2152
2149
def _create_files(tt, tree, desired_files, pb, offset, accelerator_tree,
2153
2150
                  hardlink):
2154
2151
    total = len(desired_files) + offset
2155
 
    wt = tt._tree
2156
2152
    if accelerator_tree is None:
2157
2153
        new_desired_files = desired_files
2158
2154
    else:
2161
2157
                         in iter if not (c or e[0] != e[1]))
2162
2158
        new_desired_files = []
2163
2159
        count = 0
2164
 
        for file_id, (trans_id, tree_path) in desired_files:
 
2160
        for file_id, trans_id in desired_files:
2165
2161
            accelerator_path = unchanged.get(file_id)
2166
2162
            if accelerator_path is None:
2167
 
                new_desired_files.append((file_id, (trans_id, tree_path)))
 
2163
                new_desired_files.append((file_id, trans_id))
2168
2164
                continue
2169
2165
            pb.update('Adding file contents', count + offset, total)
2170
2166
            if hardlink:
2172
2168
                                   trans_id)
2173
2169
            else:
2174
2170
                contents = accelerator_tree.get_file(file_id, accelerator_path)
2175
 
                if wt.supports_content_filtering():
2176
 
                    filters = wt._content_filter_stack(tree_path)
2177
 
                    contents = filtered_output_bytes(contents, filters,
2178
 
                        ContentFilterContext(tree_path, tree))
2179
2171
                try:
2180
2172
                    tt.create_file(contents, trans_id)
2181
2173
                finally:
2182
 
                    try:
2183
 
                        contents.close()
2184
 
                    except AttributeError:
2185
 
                        # after filtering, contents may no longer be file-like
2186
 
                        pass
 
2174
                    contents.close()
2187
2175
            count += 1
2188
2176
        offset += count
2189
 
    for count, ((trans_id, tree_path), contents) in enumerate(
2190
 
            tree.iter_files_bytes(new_desired_files)):
2191
 
        if wt.supports_content_filtering():
2192
 
            filters = wt._content_filter_stack(tree_path)
2193
 
            contents = filtered_output_bytes(contents, filters,
2194
 
                ContentFilterContext(tree_path, tree))
 
2177
    for count, (trans_id, contents) in enumerate(tree.iter_files_bytes(
 
2178
                                                 new_desired_files)):
2195
2179
        tt.create_file(contents, trans_id)
2196
2180
        pb.update('Adding file contents', count + offset, total)
2197
2181
 
2257
2241
    if kind == 'file':
2258
2242
        contents = tree.get_file(entry.file_id).readlines()
2259
2243
        executable = tree.is_executable(entry.file_id)
2260
 
        return tt.new_file(name, parent_id, contents, entry.file_id,
 
2244
        return tt.new_file(name, parent_id, contents, entry.file_id, 
2261
2245
                           executable)
2262
2246
    elif kind in ('directory', 'tree-reference'):
2263
2247
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
2264
2248
        if kind == 'tree-reference':
2265
2249
            tt.set_tree_reference(entry.reference_revision, trans_id)
2266
 
        return trans_id
 
2250
        return trans_id 
2267
2251
    elif kind == 'symlink':
2268
2252
        target = tree.get_symlink_target(entry.file_id)
2269
2253
        return tt.new_symlink(name, parent_id, target, entry.file_id)
2348
2332
        if entry.kind != working_kind:
2349
2333
            contents_mod, meta_mod = True, False
2350
2334
        else:
2351
 
            cur_entry._read_tree_state(working_tree.id2path(file_id),
 
2335
            cur_entry._read_tree_state(working_tree.id2path(file_id), 
2352
2336
                                       working_tree)
2353
2337
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
2354
2338
            cur_entry._forget_tree_state()
2543
2527
                existing_file, new_file = conflict[1], conflict[2]
2544
2528
            new_name = tt.final_name(existing_file)+'.moved'
2545
2529
            tt.adjust_path(new_name, final_parent, existing_file)
2546
 
            new_conflicts.add((c_type, 'Moved existing file to',
 
2530
            new_conflicts.add((c_type, 'Moved existing file to', 
2547
2531
                               existing_file, new_file))
2548
2532
        elif c_type == 'parent loop':
2549
2533
            # break the loop by undoing one of the ops that caused the loop
2553
2537
            new_conflicts.add((c_type, 'Cancelled move', cur,
2554
2538
                               tt.final_parent(cur),))
2555
2539
            tt.adjust_path(tt.final_name(cur), tt.get_tree_parent(cur), cur)
2556
 
 
 
2540
            
2557
2541
        elif c_type == 'missing parent':
2558
2542
            trans_id = conflict[1]
2559
2543
            try:
2560
2544
                tt.cancel_deletion(trans_id)
2561
 
                new_conflicts.add(('deleting parent', 'Not deleting',
 
2545
                new_conflicts.add(('deleting parent', 'Not deleting', 
2562
2546
                                   trans_id))
2563
2547
            except KeyError:
2564
2548
                create = True
2629
2613
        if len(conflict) == 3:
2630
2614
            yield Conflict.factory(c_type, action=action, path=modified_path,
2631
2615
                                     file_id=modified_id)
2632
 
 
 
2616
             
2633
2617
        else:
2634
2618
            conflicting_path = fp.get_path(conflict[3])
2635
2619
            conflicting_id = tt.final_file_id(conflict[3])
2636
2620
            yield Conflict.factory(c_type, action=action, path=modified_path,
2637
 
                                   file_id=modified_id,
 
2621
                                   file_id=modified_id, 
2638
2622
                                   conflict_path=conflicting_path,
2639
2623
                                   conflict_file_id=conflicting_id)
2640
2624