/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/git/tree.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-07-20 02:17:05 UTC
  • mfrom: (7518.1.2 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200720021705-5f11tmo1hdqjxm6x
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/387628

Show diffs side-by-side

added added

removed removed

Lines of Context:
66
66
    )
67
67
 
68
68
from .mapping import (
 
69
    encode_git_path,
 
70
    decode_git_path,
69
71
    mode_is_executable,
70
72
    mode_kind,
71
73
    default_mapping,
296
298
            info = self._submodule_info()[relpath]
297
299
        except KeyError:
298
300
            nested_repo_transport = self._repository.controldir.user_transport.clone(
299
 
                relpath.decode('utf-8'))
 
301
                decode_git_path(relpath))
300
302
        else:
301
303
            nested_repo_transport = self._repository.controldir.control_transport.clone(
302
 
                posixpath.join('modules', info[1].decode('utf-8')))
 
304
                posixpath.join('modules', decode_git_path(info[1])))
303
305
        nested_controldir = _mod_controldir.ControlDir.open_from_transport(
304
306
            nested_repo_transport)
305
307
        return nested_controldir.find_repository()
308
310
        return self._get_submodule_repository(relpath)._git.object_store
309
311
 
310
312
    def get_nested_tree(self, path):
311
 
        encoded_path = path.encode('utf-8')
 
313
        encoded_path = encode_git_path(path)
312
314
        nested_repo = self._get_submodule_repository(encoded_path)
313
315
        ref_rev = self.get_reference_revision(path)
314
316
        return nested_repo.revision_tree(ref_rev)
321
323
        if self.commit_id == ZERO_SHA:
322
324
            return NULL_REVISION
323
325
        (unused_path, commit_id) = change_scanner.find_last_change_revision(
324
 
            path.encode('utf-8'), self.commit_id)
 
326
            encode_git_path(path), self.commit_id)
325
327
        return self._repository.lookup_foreign_revision_id(
326
328
            commit_id, self.mapping)
327
329
 
368
370
            tree = store[tree_id]
369
371
            for name, mode, hexsha in tree.items():
370
372
                subpath = posixpath.join(path, name)
371
 
                ret.add(subpath.decode('utf-8'))
 
373
                ret.add(decode_git_path(subpath))
372
374
                if stat.S_ISDIR(mode):
373
375
                    todo.append((store, subpath, hexsha))
374
376
        return ret
377
379
        if self.tree is None:
378
380
            raise errors.NoSuchFile(path)
379
381
 
380
 
        encoded_path = path.encode('utf-8')
 
382
        encoded_path = encode_git_path(path)
381
383
        parts = encoded_path.split(b'/')
382
384
        hexsha = self.tree
383
385
        store = self.store
444
446
            parent_path = posixpath.dirname(from_dir)
445
447
            parent_id = self.mapping.generate_file_id(parent_path)
446
448
            if mode_kind(mode) == 'directory':
447
 
                root_ie = self._get_dir_ie(from_dir.encode("utf-8"), parent_id)
 
449
                root_ie = self._get_dir_ie(encode_git_path(from_dir), parent_id)
448
450
            else:
449
451
                root_ie = self._get_file_ie(
450
 
                    store, from_dir.encode("utf-8"),
 
452
                    store, encode_git_path(from_dir),
451
453
                    posixpath.basename(from_dir), mode, hexsha)
452
454
        if include_root:
453
455
            yield (from_dir, "V", root_ie.kind, root_ie)
454
456
        todo = []
455
457
        if root_ie.kind == 'directory':
456
 
            todo.append((store, from_dir.encode("utf-8"),
 
458
            todo.append((store, encode_git_path(from_dir),
457
459
                         b"", hexsha, root_ie.file_id))
458
460
        while todo:
459
461
            (store, path, relpath, hexsha, parent_id) = todo.pop()
476
478
                else:
477
479
                    ie = self._get_file_ie(
478
480
                        store, child_path, name, mode, hexsha, parent_id)
479
 
                yield (child_relpath.decode('utf-8'), "V", ie.kind, ie)
 
481
                yield (decode_git_path(child_relpath), "V", ie.kind, ie)
480
482
 
481
483
    def _get_file_ie(self, store, path, name, mode, hexsha, parent_id):
482
484
        if not isinstance(path, bytes):
484
486
        if not isinstance(name, bytes):
485
487
            raise TypeError(name)
486
488
        kind = mode_kind(mode)
487
 
        path = path.decode('utf-8')
488
 
        name = name.decode("utf-8")
 
489
        path = decode_git_path(path)
 
490
        name = decode_git_path(name)
489
491
        file_id = self.mapping.generate_file_id(path)
490
492
        ie = entry_factory[kind](file_id, name, parent_id)
491
493
        if kind == 'symlink':
492
 
            ie.symlink_target = store[hexsha].data.decode('utf-8')
 
494
            ie.symlink_target = decode_git_path(store[hexsha].data)
493
495
        elif kind == 'tree-reference':
494
496
            ie.reference_revision = self.mapping.revision_id_foreign_to_bzr(
495
497
                hexsha)
501
503
        return ie
502
504
 
503
505
    def _get_dir_ie(self, path, parent_id):
504
 
        path = path.decode('utf-8')
 
506
        path = decode_git_path(path)
505
507
        file_id = self.mapping.generate_file_id(path)
506
508
        return GitTreeDirectory(file_id, posixpath.basename(path), parent_id)
507
509
 
511
513
        if mode is not None and not stat.S_ISDIR(mode):
512
514
            return
513
515
 
514
 
        encoded_path = path.encode('utf-8')
 
516
        encoded_path = encode_git_path(path)
515
517
        file_id = self.path2id(path)
516
518
        tree = store[tree_sha]
517
519
        for name, mode, hexsha in tree.iteritems():
532
534
            if specific_files in ([""], []):
533
535
                specific_files = None
534
536
            else:
535
 
                specific_files = set([p.encode('utf-8')
 
537
                specific_files = set([encode_git_path(p)
536
538
                                      for p in specific_files])
537
539
        todo = deque([(self.store, b"", self.tree, self.path2id(''))])
538
540
        if specific_files is None or u"" in specific_files:
545
547
                if self.mapping.is_special_file(name):
546
548
                    continue
547
549
                child_path = posixpath.join(path, name)
548
 
                child_path_decoded = child_path.decode('utf-8')
 
550
                child_path_decoded = decode_git_path(child_path)
549
551
                if recurse_nested and S_ISGITLINK(mode):
550
552
                    mode = stat.S_IFDIR
551
553
                    store = self._get_submodule_store(child_path)
604
606
        """See RevisionTree.get_symlink_target."""
605
607
        (store, mode, hexsha) = self._lookup_path(path)
606
608
        if stat.S_ISLNK(mode):
607
 
            return store[hexsha].data.decode('utf-8')
 
609
            return decode_git_path(store[hexsha].data)
608
610
        else:
609
611
            return None
610
612
 
613
615
        (store, mode, hexsha) = self._lookup_path(path)
614
616
        if S_ISGITLINK(mode):
615
617
            try:
616
 
                nested_repo = self._get_submodule_repository(path.encode('utf-8'))
 
618
                nested_repo = self._get_submodule_repository(encode_git_path(path))
617
619
            except errors.NotBranchError:
618
620
                return self.mapping.revision_id_foreign_to_bzr(hexsha)
619
621
            else:
639
641
            return (kind, len(contents), executable,
640
642
                    osutils.sha_string(contents))
641
643
        elif kind == 'symlink':
642
 
            return (kind, None, None, store[hexsha].data.decode('utf-8'))
 
644
            return (kind, None, None, decode_git_path(store[hexsha].data))
643
645
        elif kind == 'tree-reference':
644
 
            nested_repo = self._get_submodule_repository(path.encode('utf-8'))
 
646
            nested_repo = self._get_submodule_repository(encode_git_path(path))
645
647
            return (kind, None, None,
646
648
                    nested_repo.lookup_foreign_revision_id(hexsha))
647
649
        else:
697
699
    def walkdirs(self, prefix=u""):
698
700
        (store, mode, hexsha) = self._lookup_path(prefix)
699
701
        todo = deque(
700
 
            [(store, prefix.encode('utf-8'), hexsha, self.path2id(prefix))])
 
702
            [(store, encode_git_path(prefix), hexsha, self.path2id(prefix))])
701
703
        while todo:
702
704
            store, path, tree_sha, parent_id = todo.popleft()
703
 
            path_decoded = path.decode('utf-8')
 
705
            path_decoded = decode_git_path(path)
704
706
            tree = store[tree_sha]
705
707
            children = []
706
708
            for name, mode, hexsha in tree.iteritems():
707
709
                if self.mapping.is_special_file(name):
708
710
                    continue
709
711
                child_path = posixpath.join(path, name)
710
 
                file_id = self.path2id(child_path.decode('utf-8'))
 
712
                file_id = self.path2id(decode_git_path(child_path))
711
713
                if stat.S_ISDIR(mode):
712
714
                    todo.append((store, child_path, hexsha, file_id))
713
715
                children.append(
714
 
                    (child_path.decode('utf-8'), name.decode('utf-8'),
 
716
                    (decode_git_path(child_path), decode_git_path(name),
715
717
                        mode_kind(mode), None,
716
718
                        file_id, mode_kind(mode)))
717
719
            yield (path_decoded, parent_id), children
718
720
 
 
721
    def preview_transform(self, pb=None):
 
722
        from .transform import GitTransformPreview
 
723
        return GitTransformPreview(self, pb=pb)
 
724
 
719
725
 
720
726
def tree_delta_from_git_changes(changes, mappings,
721
727
                                specific_files=None,
740
746
            continue
741
747
        copied = (change_type == 'copy')
742
748
        if oldpath is not None:
743
 
            oldpath_decoded = oldpath.decode('utf-8')
 
749
            oldpath_decoded = decode_git_path(oldpath)
744
750
        else:
745
751
            oldpath_decoded = None
746
752
        if newpath is not None:
747
 
            newpath_decoded = newpath.decode('utf-8')
 
753
            newpath_decoded = decode_git_path(newpath)
748
754
        else:
749
755
            newpath_decoded = None
750
756
        if not (specific_files is None or
849
855
    for path, kind in added:
850
856
        if kind == 'directory' and path not in implicit_dirs:
851
857
            continue
852
 
        path_decoded = osutils.normalized_filename(path)[0]
 
858
        path_decoded = decode_git_path(path)
853
859
        parent_path, basename = osutils.split(path_decoded)
854
860
        parent_id = new_mapping.generate_file_id(parent_path)
855
861
        file_id = new_mapping.generate_file_id(path_decoded)
881
887
        (oldpath, oldmode, oldsha) = old
882
888
        (newpath, newmode, newsha) = new
883
889
        if oldpath is not None:
884
 
            oldpath_decoded = oldpath.decode('utf-8')
 
890
            oldpath_decoded = decode_git_path(oldpath)
885
891
        else:
886
892
            oldpath_decoded = None
887
893
        if newpath is not None:
888
 
            newpath_decoded = newpath.decode('utf-8')
 
894
            newpath_decoded = decode_git_path(newpath)
889
895
        else:
890
896
            newpath_decoded = None
891
897
        if not (specific_files is None or
1005
1011
 
1006
1012
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1007
1013
                          require_versioned=False, extra_trees=None,
1008
 
                          want_unversioned=False):
 
1014
                          want_unversioned=False, include_trees=True):
1009
1015
        raise NotImplementedError(self._iter_git_changes)
1010
1016
 
1011
1017
    def find_target_path(self, path, recurse='none'):
1019
1025
    def find_target_paths(self, paths, recurse='none'):
1020
1026
        paths = set(paths)
1021
1027
        ret = {}
1022
 
        changes = self._iter_git_changes(specific_files=paths)[0]
 
1028
        changes = self._iter_git_changes(
 
1029
            specific_files=paths, include_trees=False)[0]
1023
1030
        for (change_type, old, new) in changes:
1024
 
            oldpath = old[0]
1025
 
            newpath = new[0]
 
1031
            if old[0] is None:
 
1032
                continue
 
1033
            oldpath = decode_git_path(old[0])
1026
1034
            if oldpath in paths:
1027
 
                ret[oldpath] = newpath
 
1035
                ret[oldpath] = decode_git_path(new[0]) if new[0] else None
1028
1036
        for path in paths:
1029
1037
            if path not in ret:
1030
1038
                if self.source.has_filename(path):
1039
1047
    def find_source_paths(self, paths, recurse='none'):
1040
1048
        paths = set(paths)
1041
1049
        ret = {}
1042
 
        changes = self._iter_git_changes(specific_files=paths)[0]
 
1050
        changes = self._iter_git_changes(
 
1051
            specific_files=paths, include_trees=False)[0]
1043
1052
        for (change_type, old, new) in changes:
1044
 
            oldpath = old[0]
1045
 
            newpath = new[0]
 
1053
            if new[0] is None:
 
1054
                continue
 
1055
            newpath = decode_git_path(new[0])
1046
1056
            if newpath in paths:
1047
 
                ret[newpath] = oldpath
 
1057
                ret[newpath] = decode_git_path(old[0]) if old[0] else None
1048
1058
        for path in paths:
1049
1059
            if path not in ret:
1050
1060
                if self.target.has_filename(path):
1071
1081
 
1072
1082
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1073
1083
                          require_versioned=True, extra_trees=None,
1074
 
                          want_unversioned=False):
 
1084
                          want_unversioned=False, include_trees=True):
1075
1085
        trees = [self.source]
1076
1086
        if extra_trees is not None:
1077
1087
            trees.extend(extra_trees)
1090
1100
        rename_detector = RenameDetector(store)
1091
1101
        changes = tree_changes(
1092
1102
            store, self.source.tree, self.target.tree,
1093
 
            want_unchanged=want_unchanged, include_trees=True,
 
1103
            want_unchanged=want_unchanged, include_trees=include_trees,
1094
1104
            change_type_same=True, rename_detector=rename_detector)
1095
1105
        return changes, set(), set()
1096
1106
 
1109
1119
 
1110
1120
    def is_versioned(self, path):
1111
1121
        with self.lock_read():
1112
 
            path = path.rstrip('/').encode('utf-8')
 
1122
            path = encode_git_path(path.rstrip('/'))
1113
1123
            (index, subpath) = self._lookup_index(path)
1114
1124
            return (subpath in index or self._has_dir(path))
1115
1125
 
1251
1261
                # old index
1252
1262
                stat_val = os.stat_result(
1253
1263
                    (stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
1254
 
            blob.set_raw_string(
1255
 
                self.get_symlink_target(path).encode("utf-8"))
 
1264
            blob.set_raw_string(encode_git_path(self.get_symlink_target(path)))
1256
1265
            # Add object to the repository if it didn't exist yet
1257
1266
            if blob.id not in self.store:
1258
1267
                self.store.add_object(blob)
1275
1284
            raise AssertionError("unknown kind '%s'" % kind)
1276
1285
        # Add an entry to the index or update the existing entry
1277
1286
        ensure_normalized_path(path)
1278
 
        encoded_path = path.encode("utf-8")
 
1287
        encoded_path = encode_git_path(path)
1279
1288
        if b'\r' in encoded_path or b'\n' in encoded_path:
1280
1289
            # TODO(jelmer): Why do we need to do this?
1281
1290
            trace.mutter('ignoring path with invalid newline in it: %r', path)
1320
1329
                    recurse_nested=recurse_nested):
1321
1330
                if self.mapping.is_special_file(path):
1322
1331
                    continue
1323
 
                path = path.decode("utf-8")
 
1332
                path = decode_git_path(path)
1324
1333
                if specific_files is not None and path not in specific_files:
1325
1334
                    continue
1326
1335
                (parent, name) = posixpath.split(path)
1406
1415
    def _unversion_path(self, path):
1407
1416
        if self._lock_mode is None:
1408
1417
            raise errors.ObjectNotLocked(self)
1409
 
        encoded_path = path.encode("utf-8")
 
1418
        encoded_path = encode_git_path(path)
1410
1419
        count = 0
1411
1420
        (index, subpath) = self._lookup_index(encoded_path)
1412
1421
        try:
1439
1448
        for (old_path, new_path, file_id, ie) in delta:
1440
1449
            if old_path is not None:
1441
1450
                (index, old_subpath) = self._lookup_index(
1442
 
                    old_path.encode('utf-8'))
 
1451
                    encode_git_path(old_path))
1443
1452
                if old_subpath in index:
1444
1453
                    self._index_del_entry(index, old_subpath)
1445
1454
                    self._versioned_dirs = None
1465
1474
            return rename_tuples
1466
1475
 
1467
1476
    def rename_one(self, from_rel, to_rel, after=None):
1468
 
        from_path = from_rel.encode("utf-8")
 
1477
        from_path = encode_git_path(from_rel)
1469
1478
        to_rel, can_access = osutils.normalized_filename(to_rel)
1470
1479
        if not can_access:
1471
1480
            raise errors.InvalidNormalization(to_rel)
1472
 
        to_path = to_rel.encode("utf-8")
 
1481
        to_path = encode_git_path(to_rel)
1473
1482
        with self.lock_tree_write():
1474
1483
            if not after:
1475
1484
                # Perhaps it's already moved?
1595
1604
            return (kind, None, None, None)
1596
1605
 
1597
1606
    def stored_kind(self, relpath):
1598
 
        (index, index_path) = self._lookup_index(relpath.encode('utf-8'))
 
1607
        (index, index_path) = self._lookup_index(encode_git_path(relpath))
1599
1608
        if index is None:
1600
1609
            return kind
1601
1610
        try:
1619
1628
    def _live_entry(self, relpath):
1620
1629
        raise NotImplementedError(self._live_entry)
1621
1630
 
1622
 
    def get_transform(self, pb=None):
1623
 
        from ..transform import TreeTransform
1624
 
        return TreeTransform(self, pb=pb)
1625
 
 
1626
 
 
1627
 
 
1628
 
class InterIndexGitTree(InterGitTrees):
 
1631
    def transform(self, pb=None):
 
1632
        from .transform import GitTreeTransform
 
1633
        return GitTreeTransform(self, pb=pb)
 
1634
 
 
1635
    def preview_transform(self, pb=None):
 
1636
        from .transform import GitTransformPreview
 
1637
        return GitTransformPreview(self, pb=pb)
 
1638
 
 
1639
 
 
1640
class InterToIndexGitTree(InterGitTrees):
1629
1641
    """InterTree that works between a Git revision tree and an index."""
1630
1642
 
1631
1643
    def __init__(self, source, target):
1632
 
        super(InterIndexGitTree, self).__init__(source, target)
1633
 
        self._index = target.index
 
1644
        super(InterToIndexGitTree, self).__init__(source, target)
1634
1645
        if self.source.store == self.target.store:
1635
1646
            self.store = self.source.store
1636
1647
        else:
1645
1656
 
1646
1657
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1647
1658
                          require_versioned=False, extra_trees=None,
1648
 
                          want_unversioned=False):
 
1659
                          want_unversioned=False, include_trees=True):
1649
1660
        trees = [self.source]
1650
1661
        if extra_trees is not None:
1651
1662
            trees.extend(extra_trees)
1659
1670
                self.source.store, self.source.tree,
1660
1671
                self.target, want_unchanged=want_unchanged,
1661
1672
                want_unversioned=want_unversioned,
1662
 
                rename_detector=self.rename_detector)
 
1673
                rename_detector=self.rename_detector,
 
1674
                include_trees=include_trees)
1663
1675
            return changes, set(), target_extras
1664
1676
 
1665
1677
 
 
1678
_mod_tree.InterTree.register_optimiser(InterToIndexGitTree)
 
1679
 
 
1680
 
 
1681
class InterFromIndexGitTree(InterGitTrees):
 
1682
    """InterTree that works between a Git revision tree and an index."""
 
1683
 
 
1684
    def __init__(self, source, target):
 
1685
        super(InterFromIndexGitTree, self).__init__(source, target)
 
1686
        if self.source.store == self.target.store:
 
1687
            self.store = self.source.store
 
1688
        else:
 
1689
            self.store = OverlayObjectStore(
 
1690
                [self.source.store, self.target.store])
 
1691
        self.rename_detector = RenameDetector(self.store)
 
1692
 
 
1693
    @classmethod
 
1694
    def is_compatible(cls, source, target):
 
1695
        return (isinstance(target, GitRevisionTree) and
 
1696
                isinstance(source, MutableGitIndexTree))
 
1697
 
 
1698
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
 
1699
                          require_versioned=False, extra_trees=None,
 
1700
                          want_unversioned=False, include_trees=True):
 
1701
        trees = [self.source]
 
1702
        if extra_trees is not None:
 
1703
            trees.extend(extra_trees)
 
1704
        if specific_files is not None:
 
1705
            specific_files = self.target.find_related_paths_across_trees(
 
1706
                specific_files, trees,
 
1707
                require_versioned=require_versioned)
 
1708
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
 
1709
        with self.lock_read():
 
1710
            from_tree_sha, extras = snapshot_workingtree(self.source, want_unversioned=want_unversioned)
 
1711
            return tree_changes(
 
1712
                self.store, from_tree_sha, self.target.tree,
 
1713
                include_trees=include_trees,
 
1714
                rename_detector=self.rename_detector,
 
1715
                want_unchanged=want_unchanged, change_type_same=True), extras
 
1716
 
 
1717
 
 
1718
_mod_tree.InterTree.register_optimiser(InterFromIndexGitTree)
 
1719
 
 
1720
 
 
1721
class InterIndexGitTree(InterGitTrees):
 
1722
    """InterTree that works between a Git revision tree and an index."""
 
1723
 
 
1724
    def __init__(self, source, target):
 
1725
        super(InterIndexGitTree, self).__init__(source, target)
 
1726
        if self.source.store == self.target.store:
 
1727
            self.store = self.source.store
 
1728
        else:
 
1729
            self.store = OverlayObjectStore(
 
1730
                [self.source.store, self.target.store])
 
1731
        self.rename_detector = RenameDetector(self.store)
 
1732
 
 
1733
    @classmethod
 
1734
    def is_compatible(cls, source, target):
 
1735
        return (isinstance(target, MutableGitIndexTree) and
 
1736
                isinstance(source, MutableGitIndexTree))
 
1737
 
 
1738
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
 
1739
                          require_versioned=False, extra_trees=None,
 
1740
                          want_unversioned=False, include_trees=True):
 
1741
        trees = [self.source]
 
1742
        if extra_trees is not None:
 
1743
            trees.extend(extra_trees)
 
1744
        if specific_files is not None:
 
1745
            specific_files = self.target.find_related_paths_across_trees(
 
1746
                specific_files, trees,
 
1747
                require_versioned=require_versioned)
 
1748
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
 
1749
        with self.lock_read():
 
1750
            from_tree_sha, from_extras = snapshot_workingtree(
 
1751
                self.source, want_unversioned=want_unversioned)
 
1752
            to_tree_sha, to_extras = snapshot_workingtree(
 
1753
                self.target, want_unversioned=want_unversioned)
 
1754
            changes = tree_changes(
 
1755
                self.store, from_tree_sha, to_tree_sha,
 
1756
                include_trees=include_trees,
 
1757
                rename_detector=self.rename_detector,
 
1758
                want_unchanged=want_unchanged, change_type_same=True)
 
1759
            return changes, from_extras, to_extras
 
1760
 
 
1761
 
1666
1762
_mod_tree.InterTree.register_optimiser(InterIndexGitTree)
1667
1763
 
1668
1764
 
1669
 
def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target,
1670
 
                                              want_unchanged=False,
1671
 
                                              want_unversioned=False,
1672
 
                                              rename_detector=None):
1673
 
    """Determine the changes between a git tree and a working tree with index.
1674
 
 
1675
 
    """
 
1765
def snapshot_workingtree(target, want_unversioned=False):
1676
1766
    extras = set()
1677
1767
    blobs = {}
1678
1768
    # Report dirified directories to commit_tree first, so that they can be
1705
1795
                    else:
1706
1796
                        mode &= ~0o111
1707
1797
                if live_entry.sha != index_entry.sha:
1708
 
                    rp = path.decode('utf-8')
 
1798
                    rp = decode_git_path(path)
1709
1799
                    if stat.S_ISREG(live_entry.mode):
1710
1800
                        blob = Blob()
1711
1801
                        with target.get_file(rp) as f:
1725
1815
            except UnicodeDecodeError:
1726
1816
                raise errors.BadFilenameEncoding(
1727
1817
                    e, osutils._fs_enc)
1728
 
            np = e.encode('utf-8')
 
1818
            np = encode_git_path(e)
1729
1819
            if np in blobs:
1730
1820
                continue
1731
1821
            st = target._lstat(e)
1739
1829
            target.store.add_object(blob)
1740
1830
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
1741
1831
            extras.add(np)
1742
 
    to_tree_sha = commit_tree(
1743
 
        target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()])
 
1832
    return commit_tree(
 
1833
        target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()]), extras
 
1834
 
 
1835
 
 
1836
def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target,
 
1837
                                              want_unchanged=False,
 
1838
                                              want_unversioned=False,
 
1839
                                              rename_detector=None,
 
1840
                                              include_trees=True):
 
1841
    """Determine the changes between a git tree and a working tree with index.
 
1842
 
 
1843
    """
 
1844
    to_tree_sha, extras = snapshot_workingtree(target, want_unversioned=want_unversioned)
1744
1845
    store = OverlayObjectStore([source_store, target.store])
1745
1846
    return tree_changes(
1746
 
        store, from_tree_sha, to_tree_sha, include_trees=True,
 
1847
        store, from_tree_sha, to_tree_sha, include_trees=include_trees,
1747
1848
        rename_detector=rename_detector,
1748
1849
        want_unchanged=want_unchanged, change_type_same=True), extras