2
0
Эх сурвалжийг харах

Merge branch 'checkout_paths2' of git://github.com/garyvdm/dulwich

Jelmer Vernooij 10 жил өмнө
parent
commit
572dbcab37

+ 57 - 19
dulwich/index.py

@@ -463,13 +463,13 @@ def validate_path(path, element_validator=validate_path_element_default):
         return True
 
 
-def build_index_from_tree(prefix, index_path, object_store, tree_id,
+def build_index_from_tree(root_path, index_path, object_store, tree_id,
                           honor_filemode=True,
                           validate_path_element=validate_path_element_default):
     """Generate and materialize index from a tree
 
     :param tree_id: Tree to materialize
-    :param prefix: Target dir for materialized index files
+    :param root_path: Target dir for materialized index files
     :param index_path: Target path for generated index
     :param object_store: Non-empty object store holding tree contents
     :param honor_filemode: An optional flag to honor core.filemode setting in
@@ -482,12 +482,14 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id,
     """
 
     index = Index(index_path)
+    if not isinstance(root_path, bytes):
+        root_path = root_path.encode(sys.getfilesystemencoding())
 
     for entry in object_store.iter_tree_contents(tree_id):
         if not validate_path(entry.path, validate_path_element):
             continue
-        full_path = os.path.join(prefix,
-            entry.path.decode(sys.getfilesystemencoding()))
+        fs_path = tree_to_fs_path(entry.path)
+        full_path = os.path.join(root_path, fs_path)
 
         if not os.path.exists(os.path.dirname(full_path)):
             os.makedirs(os.path.dirname(full_path))
@@ -503,37 +505,73 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id,
     index.write()
 
 
-def blob_from_path_and_stat(path, st):
+def blob_from_path_and_stat(fs_path, st):
     """Create a blob from a path and a stat object.
 
-    :param path: Full path to file
+    :param fs_path: Full file system path to file
     :param st: A stat object
     :return: A `Blob` object
     """
+    assert isinstance(fs_path, bytes)
     blob = Blob()
     if not stat.S_ISLNK(st.st_mode):
-        with open(path, 'rb') as f:
+        with open(fs_path, 'rb') as f:
             blob.data = f.read()
     else:
-        if platform.python_implementation() == 'PyPy':
-            # os.readlink on pypy seems to require bytes
-            # TODO: GaryvdM: test on other pypy configurations,
-            # e.g. windows, pypy3.
-            path = path.encode(sys.getfilesystemencoding())
-        blob.data = os.readlink(path).encode(sys.getfilesystemencoding())
+        blob.data = os.readlink(fs_path)
     return blob
 
 
-def get_unstaged_changes(index, path):
+def get_unstaged_changes(index, root_path):
     """Walk through an index and check for differences against working tree.
 
     :param index: index to check
-    :param path: path in which to find files
+    :param root_path: path in which to find files
     :return: iterator over paths with unstaged changes
     """
     # For each entry in the index check the sha1 & ensure not staged
-    for name, entry in index.iteritems():
-        fp = os.path.join(path, name.decode(sys.getfilesystemencoding()))
-        blob = blob_from_path_and_stat(fp, os.lstat(fp))
+    if not isinstance(root_path, bytes):
+        root_path = root_path.encode(sys.getfilesystemencoding())
+
+    for tree_path, entry in index.iteritems():
+        fs_path = tree_to_fs_path(tree_path)
+        full_path = os.path.join(root_path, fs_path)
+        blob = blob_from_path_and_stat(full_path, os.lstat(full_path))
         if blob.id != entry.sha:
-            yield name
+            yield tree_path
+
+
+os_sep_bytes = os.sep.encode('ascii')
+
+
+def tree_to_fs_path(tree_path):
+    """Convert a git tree path to a file system path.
+
+    :param tree_path: Git tree path as bytes
+
+    :return: File system path.
+    """
+    assert isinstance(tree_path, bytes)
+    if os_sep_bytes != b'/':
+        sep_corrected_path = tree_path.replace(b'/', os_sep_bytes)
+    else:
+        sep_corrected_path = tree_path
+    return sep_corrected_path
+
+
+def fs_to_tree_path(fs_path):
+    """Convert a file system path to a git tree path.
+
+    :param fs_path: File system path.
+
+    :return:  Git tree path as bytes
+    """
+    if not isinstance(fs_path, bytes):
+        fs_path_bytes = fs_path.encode(sys.getfilesystemencoding())
+    else:
+        fs_path_bytes = fs_path
+    if os_sep_bytes != b'/':
+        tree_path = fs_path_bytes.replace(os_sep_bytes, b'/')
+    else:
+        tree_path = fs_path_bytes
+    return tree_path

+ 15 - 8
dulwich/repo.py

@@ -730,32 +730,39 @@ class Repo(BaseRepo):
         # missing index file, which is treated as empty.
         return not self.bare
 
-    def stage(self, paths, fsencoding=sys.getfilesystemencoding()):
+    def stage(self, fs_paths):
         """Stage a set of paths.
 
-        :param paths: List of paths, relative to the repository path
+        :param fs_paths: List of paths, relative to the repository path
         """
-        if not isinstance(paths, list):
-            paths = [paths]
+
+        root_path_bytes = self.path.encode(sys.getfilesystemencoding())
+
+        if not isinstance(fs_paths, list):
+            fs_paths = [fs_paths]
         from dulwich.index import (
             blob_from_path_and_stat,
             index_entry_from_stat,
+            fs_to_tree_path,
             )
         index = self.open_index()
-        for path in paths:
-            full_path = os.path.join(self.path, path)
+        for fs_path in fs_paths:
+            if not isinstance(fs_path, bytes):
+                fs_path = fs_path.encode(sys.getfilesystemencoding())
+            tree_path = fs_to_tree_path(fs_path)
+            full_path = os.path.join(root_path_bytes, fs_path)
             try:
                 st = os.lstat(full_path)
             except OSError:
                 # File no longer exists
                 try:
-                    del index[path.encode(fsencoding)]
+                    del index[tree_path]
                 except KeyError:
                     pass  # already removed
             else:
                 blob = blob_from_path_and_stat(full_path, st)
                 self.object_store.add_object(blob)
-                index[path.encode(fsencoding)] = index_entry_from_stat(st, blob.id, 0)
+                index[tree_path] = index_entry_from_stat(st, blob.id, 0)
         index.write()
 
     def clone(self, target_path, mkdir=True, bare=False,

+ 21 - 2
dulwich/tests/test_index.py

@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # test_index.py -- Tests for the git index
 # encoding: utf-8
 # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
@@ -43,6 +44,8 @@ from dulwich.index import (
     write_cache_time,
     write_index,
     write_index_dict,
+    tree_to_fs_path,
+    fs_to_tree_path,
     )
 from dulwich.object_store import (
     MemoryObjectStore,
@@ -53,7 +56,6 @@ from dulwich.objects import (
     )
 from dulwich.repo import Repo
 from dulwich.tests import (
-    expectedFailure,
     TestCase,
     skipIf,
     )
@@ -395,7 +397,6 @@ class BuildIndexTests(TestCase):
                 filee.id)
             self.assertFileContents(epath, 'd', symlink=True)
 
-    @expectedFailure
     def test_no_decode_encode(self):
         repo_dir = tempfile.mkdtemp()
         repo_dir_bytes = repo_dir.encode(sys.getfilesystemencoding())
@@ -477,3 +478,21 @@ class TestValidatePathElement(TestCase):
         self.assertFalse(validate_path_element_ntfs(b".giT"))
         self.assertFalse(validate_path_element_ntfs(b".."))
         self.assertFalse(validate_path_element_ntfs(b"git~1"))
+
+
+class TestTreeFSPathConversion(TestCase):
+
+    def test_tree_to_fs_path(self):
+        tree_path = u'délwíçh/foo'.encode('utf8')
+        fs_path = tree_to_fs_path(tree_path)
+        self.assertEqual(fs_path, os.path.join(u'délwíçh', u'foo').encode('utf8'))
+
+    def test_fs_to_tree_path_str(self):
+        fs_path = os.path.join(os.path.join(u'délwíçh', u'foo'))
+        tree_path = fs_to_tree_path(fs_path)
+        self.assertEqual(tree_path, u'délwíçh/foo'.encode(sys.getfilesystemencoding()))
+
+    def test_fs_to_tree_path_bytes(self):
+        fs_path = os.path.join(os.path.join(u'délwíçh', u'foo').encode('utf8'))
+        tree_path = fs_to_tree_path(fs_path)
+        self.assertEqual(tree_path, u'délwíçh/foo'.encode('utf8'))

+ 1 - 3
dulwich/tests/test_repository.py

@@ -40,7 +40,6 @@ from dulwich.repo import (
     MemoryRepo,
     )
 from dulwich.tests import (
-    expectedFailure,
     TestCase,
     skipIf,
     )
@@ -54,7 +53,7 @@ missing_sha = b'b91fa4d900e17e99b433218e988c4eb4a3e9a097'
 
 
 def mkdtemp_unicode():
-    suffix = u'déłwíçh'
+    suffix = u'délwíçh'
     return tempfile.mkdtemp(suffix=suffix)
 
 
@@ -746,7 +745,6 @@ class BuildRepoRootTests(TestCase):
         r.stage(['a'])
         r.stage(['a'])  # double-stage a deleted path
 
-    @expectedFailure
     def test_commit_no_encode_decode(self):
         r = self._repo
         repo_path_bytes = r.path.encode(sys.getfilesystemencoding())