瀏覽代碼

Fix porcelain.path_to_tree_path for Python 3.5 (#777)

Boris Feld 4 年之前
父節點
當前提交
e5a028df6b
共有 3 個文件被更改,包括 52 次插入6 次删除
  1. 3 0
      .github/workflows/pythonpackage.yml
  2. 35 6
      dulwich/porcelain.py
  3. 14 0
      dulwich/tests/test_porcelain.py

+ 3 - 0
.github/workflows/pythonpackage.yml

@@ -20,6 +20,9 @@ jobs:
           # path encoding
           - os: windows-latest
             python-version: 3.5
+          # path encoding
+          - os: macos-latest
+            python-version: 3.5
       fail-fast: false
 
     steps:

+ 35 - 6
dulwich/porcelain.py

@@ -219,13 +219,42 @@ def path_to_tree_path(repopath, path, tree_encoding=DEFAULT_ENCODING):
       path: A path, absolute or relative to the cwd
     Returns: A path formatted for use in e.g. an index
     """
-    path = Path(path).resolve()
-    repopath = Path(repopath).resolve()
-    relpath = path.relative_to(repopath)
-    if sys.platform == 'win32':
-        return str(relpath).replace(os.path.sep, '/').encode(tree_encoding)
+    # Pathlib resolve before Python 3.6 could raises FileNotFoundError in case
+    # there is no file matching the path so we reuse the old implementation for
+    # Python 3.5
+    if sys.version_info < (3, 6):
+        if not isinstance(path, bytes):
+            path = os.fsencode(path)
+        if not isinstance(repopath, bytes):
+            repopath = os.fsencode(repopath)
+        treepath = os.path.relpath(path, repopath)
+        if treepath.startswith(b'..'):
+            err_msg = 'Path %r not in repo path (%r)' % (path, repopath)
+            raise ValueError(err_msg)
+        if os.path.sep != '/':
+            treepath = treepath.replace(os.path.sep.encode('ascii'), b'/')
+        return treepath
     else:
-        return bytes(relpath)
+        # Resolve might returns a relative path on Windows
+        # https://bugs.python.org/issue38671
+        if sys.platform == 'win32':
+            path = os.path.abspath(path)
+
+        path = Path(path).resolve()
+
+        # Resolve and abspath seems to behave differently regarding symlinks,
+        # as we are doing abspath on the file path, we need to do the same on
+        # the repo path or they might not match
+        if sys.platform == 'win32':
+            repopath = os.path.abspath(repopath)
+
+        repopath = Path(repopath).resolve()
+
+        relpath = path.relative_to(repopath)
+        if sys.platform == 'win32':
+            return str(relpath).replace(os.path.sep, '/').encode(tree_encoding)
+        else:
+            return bytes(relpath)
 
 
 class DivergedBranches(Error):

+ 14 - 0
dulwich/tests/test_porcelain.py

@@ -513,6 +513,20 @@ class RemoveTests(PorcelainTestCase):
         finally:
             os.chdir(cwd)
 
+    def test_remove_file_removed_on_disk(self):
+        fullpath = os.path.join(self.repo.path, 'foo')
+        with open(fullpath, 'w') as f:
+            f.write("BAR")
+        porcelain.add(self.repo.path, paths=[fullpath])
+        cwd = os.getcwd()
+        try:
+            os.chdir(self.repo.path)
+            os.remove(fullpath)
+            porcelain.remove(self.repo.path, paths=["foo"])
+        finally:
+            os.chdir(cwd)
+        self.assertFalse(os.path.exists(os.path.join(self.repo.path, 'foo')))
+
 
 class LogTests(PorcelainTestCase):