~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Alexander Belchenko
  • Date: 2007-08-10 09:04:38 UTC
  • mto: This revision was merged to the branch mainline in revision 2694.
  • Revision ID: bialix@ukr.net-20070810090438-0835xdz0rl8825qv
fixes after Ian's review

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
    BzrCheckError,
51
51
    BzrError,
52
52
    )
53
 
from bzrlib.symbol_versioning import deprecated_method
54
53
from bzrlib.trace import mutter
55
54
 
56
55
 
142
141
        """
143
142
        return False, False
144
143
 
145
 
    @deprecated_method(symbol_versioning.one_zero)
146
144
    def diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
147
145
             output_to, reverse=False):
148
146
        """Perform a diff from this to to_entry.
163
161
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
164
162
             output_to, reverse=False):
165
163
        """Perform a diff between two entries of the same kind."""
166
 
    
167
 
    def parent_candidates(self, previous_inventories):
168
 
        """Find possible per-file graph parents.
169
 
 
170
 
        This is currently defined by:
171
 
         - Select the last changed revision in the parent inventory.
172
 
         - Do deal with a short lived bug in bzr 0.8's development two entries
173
 
           that have the same last changed but different 'x' bit settings are
174
 
           changed in-place.
 
164
 
 
165
    def find_previous_heads(self, previous_inventories,
 
166
                            versioned_file_store,
 
167
                            transaction,
 
168
                            entry_vf=None):
 
169
        """Return the revisions and entries that directly precede this.
 
170
 
 
171
        Returned as a map from revision to inventory entry.
 
172
 
 
173
        This is a map containing the file revisions in all parents
 
174
        for which the file exists, and its revision is not a parent of
 
175
        any other. If the file is new, the set will be empty.
 
176
 
 
177
        :param versioned_file_store: A store where ancestry data on this
 
178
                                     file id can be queried.
 
179
        :param transaction: The transaction that queries to the versioned 
 
180
                            file store should be completed under.
 
181
        :param entry_vf: The entry versioned file, if its already available.
175
182
        """
 
183
        def get_ancestors(weave, entry):
 
184
            return set(weave.get_ancestry(entry.revision, topo_sorted=False))
176
185
        # revision:ie mapping for each ie found in previous_inventories.
177
186
        candidates = {}
 
187
        # revision:ie mapping with one revision for each head.
 
188
        heads = {}
 
189
        # revision: ancestor list for each head
 
190
        head_ancestors = {}
178
191
        # identify candidate head revision ids.
179
192
        for inv in previous_inventories:
180
193
            if self.file_id in inv:
181
194
                ie = inv[self.file_id]
182
195
                assert ie.file_id == self.file_id
 
196
                if ie.kind != self.kind:
 
197
                    # Can't be a candidate if the kind has changed.
 
198
                    continue
183
199
                if ie.revision in candidates:
184
200
                    # same revision value in two different inventories:
185
201
                    # correct possible inconsistencies:
196
212
                else:
197
213
                    # add this revision as a candidate.
198
214
                    candidates[ie.revision] = ie
199
 
        return candidates
200
 
 
201
 
    @deprecated_method(symbol_versioning.zero_ninetyone)
202
 
    def find_previous_heads(self, previous_inventories,
203
 
                            versioned_file_store,
204
 
                            transaction,
205
 
                            entry_vf=None):
206
 
        """Return the revisions and entries that directly precede this.
207
 
 
208
 
        Returned as a map from revision to inventory entry.
209
 
 
210
 
        This is a map containing the file revisions in all parents
211
 
        for which the file exists, and its revision is not a parent of
212
 
        any other. If the file is new, the set will be empty.
213
 
 
214
 
        :param versioned_file_store: A store where ancestry data on this
215
 
                                     file id can be queried.
216
 
        :param transaction: The transaction that queries to the versioned 
217
 
                            file store should be completed under.
218
 
        :param entry_vf: The entry versioned file, if its already available.
219
 
        """
220
 
        candidates = self.parent_candidates(previous_inventories)
221
 
 
222
 
        # revision:ie mapping with one revision for each head.
223
 
        heads = {}
 
215
 
224
216
        # common case optimisation
225
217
        if len(candidates) == 1:
226
218
            # if there is only one candidate revision found
227
 
            # then we can avoid opening the versioned file to access ancestry:
 
219
            # then we can opening the versioned file to access ancestry:
228
220
            # there cannot be any ancestors to eliminate when there is 
229
221
            # only one revision available.
230
 
            return candidates
231
 
        
232
 
        # --- what follows is now encapsulated in repository.get_graph.heads(), 
233
 
        #     but that is not accessible from here as we have no repository
234
 
        #     pointer. Note that the repository.get_graph.heads() call can return
235
 
        #     different results *at the moment* because of the kind-changing check
236
 
        #     we have in parent_candidates().
 
222
            heads[ie.revision] = ie
 
223
            return heads
237
224
 
238
225
        # eliminate ancestors amongst the available candidates:
239
226
        # heads are those that are not an ancestor of any other candidate
240
227
        # - this provides convergence at a per-file level.
241
 
        def get_ancestors(weave, entry):
242
 
            return set(weave.get_ancestry(entry.revision, topo_sorted=False))
243
 
        # revision: ancestor list for each head
244
 
        head_ancestors = {}
245
228
        for ie in candidates.values():
246
229
            # may be an ancestor of a known head:
247
230
            already_present = 0 != len(
433
416
                   self.parent_id,
434
417
                   self.revision))
435
418
 
 
419
    def snapshot(self, revision, path, previous_entries,
 
420
                 work_tree, commit_builder):
 
421
        """Make a snapshot of this entry which may or may not have changed.
 
422
        
 
423
        This means that all its fields are populated, that it has its
 
424
        text stored in the text store or weave.
 
425
        """
 
426
        # mutter('new parents of %s are %r', path, previous_entries)
 
427
        self._read_tree_state(path, work_tree)
 
428
        # TODO: Where should we determine whether to reuse a
 
429
        # previous revision id or create a new revision? 20060606
 
430
        if len(previous_entries) == 1:
 
431
            # cannot be unchanged unless there is only one parent file rev.
 
432
            parent_ie = previous_entries.values()[0]
 
433
            if self._unchanged(parent_ie):
 
434
                # mutter("found unchanged entry")
 
435
                self.revision = parent_ie.revision
 
436
                return "unchanged"
 
437
        return self._snapshot_into_revision(revision, previous_entries, 
 
438
                                            work_tree, commit_builder)
 
439
 
 
440
    def _snapshot_into_revision(self, revision, previous_entries, work_tree,
 
441
                                commit_builder):
 
442
        """Record this revision unconditionally into a store.
 
443
 
 
444
        The entry's last-changed revision property (`revision`) is updated to 
 
445
        that of the new revision.
 
446
        
 
447
        :param revision: id of the new revision that is being recorded.
 
448
 
 
449
        :returns: String description of the commit (e.g. "merged", "modified"), etc.
 
450
        """
 
451
        # mutter('new revision {%s} for {%s}', revision, self.file_id)
 
452
        self.revision = revision
 
453
        self._snapshot_text(previous_entries, work_tree, commit_builder)
 
454
 
 
455
    def _snapshot_text(self, file_parents, work_tree, commit_builder): 
 
456
        """Record the 'text' of this entry, whatever form that takes.
 
457
        
 
458
        This default implementation simply adds an empty text.
 
459
        """
 
460
        raise NotImplementedError(self._snapshot_text)
 
461
 
436
462
    def __eq__(self, other):
437
463
        if not isinstance(other, InventoryEntry):
438
464
            return NotImplemented
557
583
        """See InventoryEntry._put_on_disk."""
558
584
        os.mkdir(fullpath)
559
585
 
 
586
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
587
        """See InventoryEntry._snapshot_text."""
 
588
        commit_builder.modified_directory(self.file_id, file_parents)
 
589
 
560
590
 
561
591
class InventoryFile(InventoryEntry):
562
592
    """A file in an inventory."""
571
601
        if t in checker.checked_texts:
572
602
            prev_sha = checker.checked_texts[t]
573
603
            if prev_sha != self.text_sha1:
574
 
                raise BzrCheckError(
575
 
                    'mismatched sha1 on {%s} in {%s} (%s != %s) %r' %
576
 
                    (self.file_id, tree_revision_id, prev_sha, self.text_sha1,
577
 
                     t))
 
604
                raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
 
605
                                    (self.file_id, tree_revision_id))
578
606
            else:
579
607
                checker.repeated_text_cnt += 1
580
608
                return
620
648
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
621
649
             output_to, reverse=False):
622
650
        """See InventoryEntry._diff."""
623
 
        from bzrlib.diff import DiffText
624
 
        from_file_id = self.file_id
625
 
        if to_entry:
626
 
            to_file_id = to_entry.file_id
627
 
        else:
628
 
            to_file_id = None
629
 
        if reverse:
630
 
            to_file_id, from_file_id = from_file_id, to_file_id
631
 
            tree, to_tree = to_tree, tree
632
 
            from_label, to_label = to_label, from_label
633
 
        differ = DiffText(tree, to_tree, output_to, 'utf-8', '', '',
634
 
                          text_diff)
635
 
        return differ.diff_text(from_file_id, to_file_id, from_label, to_label)
 
651
        try:
 
652
            from_text = tree.get_file(self.file_id).readlines()
 
653
            if to_entry:
 
654
                to_text = to_tree.get_file(to_entry.file_id).readlines()
 
655
            else:
 
656
                to_text = []
 
657
            if not reverse:
 
658
                text_diff(from_label, from_text,
 
659
                          to_label, to_text, output_to)
 
660
            else:
 
661
                text_diff(to_label, to_text,
 
662
                          from_label, from_text, output_to)
 
663
        except errors.BinaryFile:
 
664
            if reverse:
 
665
                label_pair = (to_label, from_label)
 
666
            else:
 
667
                label_pair = (from_label, to_label)
 
668
            print >> output_to, \
 
669
                  ("Binary files %s and %s differ" % label_pair).encode('utf8')
636
670
 
637
671
    def has_text(self):
638
672
        """See InventoryEntry.has_text."""
682
716
    def _forget_tree_state(self):
683
717
        self.text_sha1 = None
684
718
 
 
719
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
720
        """See InventoryEntry._snapshot_text."""
 
721
        def get_content_byte_lines():
 
722
            return work_tree.get_file(self.file_id).readlines()
 
723
        self.text_sha1, self.text_size = commit_builder.modified_file_text(
 
724
            self.file_id, file_parents, get_content_byte_lines, self.text_sha1, self.text_size)
 
725
 
685
726
    def _unchanged(self, previous_ie):
686
727
        """See InventoryEntry._unchanged."""
687
728
        compatible = super(InventoryFile, self)._unchanged(previous_ie)
730
771
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
731
772
             output_to, reverse=False):
732
773
        """See InventoryEntry._diff."""
733
 
        from bzrlib.diff import DiffSymlink
734
 
        old_target = self.symlink_target
 
774
        from_text = self.symlink_target
735
775
        if to_entry is not None:
736
 
            new_target = to_entry.symlink_target
737
 
        else:
738
 
            new_target = None
739
 
        if not reverse:
740
 
            old_tree = tree
741
 
            new_tree = to_tree
742
 
        else:
743
 
            old_tree = to_tree
744
 
            new_tree = tree
745
 
            new_target, old_target = old_target, new_target
746
 
        differ = DiffSymlink(old_tree, new_tree, output_to)
747
 
        return differ.diff_symlink(old_target, new_target)
 
776
            to_text = to_entry.symlink_target
 
777
            if reverse:
 
778
                temp = from_text
 
779
                from_text = to_text
 
780
                to_text = temp
 
781
            print >>output_to, '=== target changed %r => %r' % (from_text, to_text)
 
782
        else:
 
783
            if not reverse:
 
784
                print >>output_to, '=== target was %r' % self.symlink_target
 
785
            else:
 
786
                print >>output_to, '=== target is %r' % self.symlink_target
748
787
 
749
788
    def __init__(self, file_id, name, parent_id):
750
789
        super(InventoryLink, self).__init__(file_id, name, parent_id)
784
823
            compatible = False
785
824
        return compatible
786
825
 
 
826
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
827
        """See InventoryEntry._snapshot_text."""
 
828
        commit_builder.modified_link(
 
829
            self.file_id, file_parents, self.symlink_target)
 
830
 
787
831
 
788
832
class TreeReference(InventoryEntry):
789
833
    
799
843
        return TreeReference(self.file_id, self.name, self.parent_id,
800
844
                             self.revision, self.reference_revision)
801
845
 
 
846
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
 
847
        commit_builder.modified_reference(self.file_id, file_parents)
 
848
 
802
849
    def _read_tree_state(self, path, work_tree):
803
850
        """Populate fields in the inventory entry from the given tree.
804
851
        """
808
855
    def _forget_tree_state(self):
809
856
        self.reference_revision = None 
810
857
 
811
 
    def _unchanged(self, previous_ie):
812
 
        """See InventoryEntry._unchanged."""
813
 
        compatible = super(TreeReference, self)._unchanged(previous_ie)
814
 
        if self.reference_revision != previous_ie.reference_revision:
815
 
            compatible = False
816
 
        return compatible
817
 
 
818
858
 
819
859
class Inventory(object):
820
860
    """Inventory of versioned files in a tree.
875
915
            self._byid = {}
876
916
        self.revision_id = revision_id
877
917
 
878
 
    def __repr__(self):
879
 
        return "<Inventory object at %x, contents=%r>" % (id(self), self._byid)
880
 
 
881
 
    def apply_delta(self, delta):
882
 
        """Apply a delta to this inventory.
883
 
 
884
 
        :param delta: A list of changes to apply. After all the changes are
885
 
            applied the final inventory must be internally consistent, but it
886
 
            is ok to supply changes which, if only half-applied would have an
887
 
            invalid result - such as supplying two changes which rename two
888
 
            files, 'A' and 'B' with each other : [('A', 'B', 'A-id', a_entry),
889
 
            ('B', 'A', 'B-id', b_entry)].
890
 
 
891
 
            Each change is a tuple, of the form (old_path, new_path, file_id,
892
 
            new_entry).
893
 
            
894
 
            When new_path is None, the change indicates the removal of an entry
895
 
            from the inventory and new_entry will be ignored (using None is
896
 
            appropriate). If new_path is not None, then new_entry must be an
897
 
            InventoryEntry instance, which will be incorporated into the
898
 
            inventory (and replace any existing entry with the same file id).
899
 
            
900
 
            When old_path is None, the change indicates the addition of
901
 
            a new entry to the inventory.
902
 
            
903
 
            When neither new_path nor old_path are None, the change is a
904
 
            modification to an entry, such as a rename, reparent, kind change
905
 
            etc. 
906
 
 
907
 
            The children attribute of new_entry is ignored. This is because
908
 
            this method preserves children automatically across alterations to
909
 
            the parent of the children, and cases where the parent id of a
910
 
            child is changing require the child to be passed in as a separate
911
 
            change regardless. E.g. in the recursive deletion of a directory -
912
 
            the directory's children must be included in the delta, or the
913
 
            final inventory will be invalid.
914
 
        """
915
 
        children = {}
916
 
        # Remove all affected items which were in the original inventory,
917
 
        # starting with the longest paths, thus ensuring parents are examined
918
 
        # after their children, which means that everything we examine has no
919
 
        # modified children remaining by the time we examine it.
920
 
        for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
921
 
                                        if op is not None), reverse=True):
922
 
            if file_id not in self:
923
 
                # adds come later
924
 
                continue
925
 
            # Preserve unaltered children of file_id for later reinsertion.
926
 
            children[file_id] = getattr(self[file_id], 'children', {})
927
 
            # Remove file_id and the unaltered children. If file_id is not
928
 
            # being deleted it will be reinserted back later.
929
 
            self.remove_recursive_id(file_id)
930
 
        # Insert all affected which should be in the new inventory, reattaching
931
 
        # their children if they had any. This is done from shortest path to
932
 
        # longest, ensuring that items which were modified and whose parents in
933
 
        # the resulting inventory were also modified, are inserted after their
934
 
        # parents.
935
 
        for new_path, new_entry in sorted((np, e) for op, np, f, e in
936
 
                                          delta if np is not None):
937
 
            if new_entry.kind == 'directory':
938
 
                new_entry.children = children.get(new_entry.file_id, {})
939
 
            self.add(new_entry)
940
 
 
941
918
    def _set_root(self, ie):
942
919
        self.root = ie
943
920
        self._byid = {self.root.file_id: self.root}
945
922
    def copy(self):
946
923
        # TODO: jam 20051218 Should copy also copy the revision_id?
947
924
        entries = self.iter_entries()
948
 
        if self.root is None:
949
 
            return Inventory(root_id=None)
950
925
        other = Inventory(entries.next()[1].file_id)
951
926
        # copy recursively so we know directories will be added before
952
927
        # their children.  There are more efficient ways than this...
953
 
        for path, entry in entries:
 
928
        for path, entry in entries():
954
929
            other.add(entry.copy())
955
930
        return other
956
931
 
1005
980
                # if we finished all children, pop it off the stack
1006
981
                stack.pop()
1007
982
 
1008
 
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
1009
 
        yield_parents=False):
 
983
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None):
1010
984
        """Iterate over the entries in a directory first order.
1011
985
 
1012
986
        This returns all entries for a directory before returning
1014
988
        lexicographically sorted order, and is a hybrid between
1015
989
        depth-first and breadth-first.
1016
990
 
1017
 
        :param yield_parents: If True, yield the parents from the root leading
1018
 
            down to specific_file_ids that have been requested. This has no
1019
 
            impact if specific_file_ids is None.
1020
991
        :return: This yields (path, entry) pairs
1021
992
        """
1022
 
        if specific_file_ids and not isinstance(specific_file_ids, set):
1023
 
            specific_file_ids = set(specific_file_ids)
 
993
        if specific_file_ids:
 
994
            safe = osutils.safe_file_id
 
995
            specific_file_ids = set(safe(fid) for fid in specific_file_ids)
1024
996
        # TODO? Perhaps this should return the from_dir so that the root is
1025
997
        # yielded? or maybe an option?
1026
998
        if from_dir is None:
1027
999
            if self.root is None:
1028
1000
                return
1029
1001
            # Optimize a common case
1030
 
            if (not yield_parents and specific_file_ids is not None and
1031
 
                len(specific_file_ids) == 1):
 
1002
            if specific_file_ids is not None and len(specific_file_ids) == 1:
1032
1003
                file_id = list(specific_file_ids)[0]
1033
1004
                if file_id in self:
1034
1005
                    yield self.id2path(file_id), self[file_id]
1035
1006
                return 
1036
1007
            from_dir = self.root
1037
 
            if (specific_file_ids is None or yield_parents or
 
1008
            if (specific_file_ids is None or 
1038
1009
                self.root.file_id in specific_file_ids):
1039
1010
                yield u'', self.root
1040
1011
        elif isinstance(from_dir, basestring):
1069
1040
                child_relpath = cur_relpath + child_name
1070
1041
 
1071
1042
                if (specific_file_ids is None or 
1072
 
                    child_ie.file_id in specific_file_ids or
1073
 
                    (yield_parents and child_ie.file_id in parents)):
 
1043
                    child_ie.file_id in specific_file_ids):
1074
1044
                    yield child_relpath, child_ie
1075
1045
 
1076
1046
                if child_ie.kind == 'directory':
1127
1097
        >>> '456' in inv
1128
1098
        False
1129
1099
        """
 
1100
        file_id = osutils.safe_file_id(file_id)
1130
1101
        return (file_id in self._byid)
1131
1102
 
1132
1103
    def __getitem__(self, file_id):
1138
1109
        >>> inv['123123'].name
1139
1110
        'hello.c'
1140
1111
        """
 
1112
        file_id = osutils.safe_file_id(file_id)
1141
1113
        try:
1142
1114
            return self._byid[file_id]
1143
1115
        except KeyError:
1145
1117
            raise errors.NoSuchId(self, file_id)
1146
1118
 
1147
1119
    def get_file_kind(self, file_id):
 
1120
        file_id = osutils.safe_file_id(file_id)
1148
1121
        return self._byid[file_id].kind
1149
1122
 
1150
1123
    def get_child(self, parent_id, filename):
 
1124
        parent_id = osutils.safe_file_id(parent_id)
1151
1125
        return self[parent_id].children.get(filename)
1152
1126
 
1153
1127
    def _add_child(self, entry):
1201
1175
        if len(parts) == 0:
1202
1176
            if file_id is None:
1203
1177
                file_id = generate_ids.gen_root_id()
 
1178
            else:
 
1179
                file_id = osutils.safe_file_id(file_id)
1204
1180
            self.root = InventoryDirectory(file_id, '', None)
1205
1181
            self._byid = {self.root.file_id: self.root}
1206
1182
            return self.root
1224
1200
        >>> '123' in inv
1225
1201
        False
1226
1202
        """
 
1203
        file_id = osutils.safe_file_id(file_id)
1227
1204
        ie = self[file_id]
1228
1205
 
1229
1206
        assert ie.parent_id is None or \
1262
1239
 
1263
1240
    def _iter_file_id_parents(self, file_id):
1264
1241
        """Yield the parents of file_id up to the root."""
 
1242
        file_id = osutils.safe_file_id(file_id)
1265
1243
        while file_id is not None:
1266
1244
            try:
1267
1245
                ie = self._byid[file_id]
1278
1256
        is equal to the depth of the file in the tree, counting the
1279
1257
        root directory as depth 1.
1280
1258
        """
 
1259
        file_id = osutils.safe_file_id(file_id)
1281
1260
        p = []
1282
1261
        for parent in self._iter_file_id_parents(file_id):
1283
1262
            p.insert(0, parent.file_id)
1292
1271
        >>> print i.id2path('foo-id')
1293
1272
        src/foo.c
1294
1273
        """
 
1274
        file_id = osutils.safe_file_id(file_id)
1295
1275
        # get all names, skipping root
1296
1276
        return '/'.join(reversed(
1297
1277
            [parent.name for parent in 
1335
1315
        return bool(self.path2id(names))
1336
1316
 
1337
1317
    def has_id(self, file_id):
 
1318
        file_id = osutils.safe_file_id(file_id)
1338
1319
        return (file_id in self._byid)
1339
1320
 
1340
1321
    def remove_recursive_id(self, file_id):
1342
1323
        
1343
1324
        :param file_id: A file_id to remove.
1344
1325
        """
 
1326
        file_id = osutils.safe_file_id(file_id)
1345
1327
        to_find_delete = [self._byid[file_id]]
1346
1328
        to_delete = []
1347
1329
        while to_find_delete:
1364
1346
 
1365
1347
        This does not move the working file.
1366
1348
        """
1367
 
        new_name = ensure_normalized_name(new_name)
 
1349
        file_id = osutils.safe_file_id(file_id)
1368
1350
        if not is_valid_name(new_name):
1369
1351
            raise BzrError("not an acceptable filename: %r" % new_name)
1370
1352
 
1389
1371
        file_ie.parent_id = new_parent_id
1390
1372
 
1391
1373
    def is_root(self, file_id):
 
1374
        file_id = osutils.safe_file_id(file_id)
1392
1375
        return self.root is not None and file_id == self.root.file_id
1393
1376
 
1394
1377
 
1409
1392
    """
1410
1393
    if file_id is None:
1411
1394
        file_id = generate_ids.gen_file_id(name)
1412
 
    name = ensure_normalized_name(name)
1413
 
    try:
1414
 
        factory = entry_factory[kind]
1415
 
    except KeyError:
1416
 
        raise BzrError("unknown kind %r" % kind)
1417
 
    return factory(file_id, name, parent_id)
1418
 
 
1419
 
 
1420
 
def ensure_normalized_name(name):
1421
 
    """Normalize name.
1422
 
 
1423
 
    :raises InvalidNormalization: When name is not normalized, and cannot be
1424
 
        accessed on this platform by the normalized path.
1425
 
    :return: The NFC normalised version of name.
1426
 
    """
 
1395
    else:
 
1396
        file_id = osutils.safe_file_id(file_id)
 
1397
 
1427
1398
    #------- This has been copied to bzrlib.dirstate.DirState.add, please
1428
1399
    # keep them synchronised.
1429
1400
    # we dont import normalized_filename directly because we want to be
1431
1402
    norm_name, can_access = osutils.normalized_filename(name)
1432
1403
    if norm_name != name:
1433
1404
        if can_access:
1434
 
            return norm_name
 
1405
            name = norm_name
1435
1406
        else:
1436
1407
            # TODO: jam 20060701 This would probably be more useful
1437
1408
            #       if the error was raised with the full path
1438
1409
            raise errors.InvalidNormalization(name)
1439
 
    return name
 
1410
 
 
1411
    try:
 
1412
        factory = entry_factory[kind]
 
1413
    except KeyError:
 
1414
        raise BzrError("unknown kind %r" % kind)
 
1415
    return factory(file_id, name, parent_id)
1440
1416
 
1441
1417
 
1442
1418
_NAME_RE = None