瀏覽代碼

Import upstream version 0.20.27

Jelmer Vernooij 3 年之前
父節點
當前提交
662b2117d1

+ 1 - 0
.gitignore

@@ -25,3 +25,4 @@ docs/api/*.txt
 .mypy_cache/
 .eggs
 dulwich.dist-info
+.stestr

+ 2 - 0
.stestr.conf

@@ -0,0 +1,2 @@
+[DEFAULT]
+test_path=dulwich/tests

+ 8 - 0
NEWS

@@ -1,3 +1,11 @@
+0.20.27	2022-01-04
+
+ * Allow adding files to repository in pre-commit hook.
+   (Jelmer Vernooij, #916)
+
+ * Raise SubmoduleEncountered in ``tree_lookup_path``.
+   (Jelmer Vernooij)
+
 0.20.26	2021-10-29
 
  * Support os.PathLike arguments to Repo.stage().

+ 1 - 1
PKG-INFO

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.20.26
+Version: 0.20.27
 Summary: Python Git Library
 Home-page: https://www.dulwich.io/
 Author: Jelmer Vernooij

+ 1 - 1
dulwich.egg-info/PKG-INFO

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.20.26
+Version: 0.20.27
 Summary: Python Git Library
 Home-page: https://www.dulwich.io/
 Author: Jelmer Vernooij

+ 11 - 0
dulwich.egg-info/SOURCES.txt

@@ -3,6 +3,7 @@
 .flake8
 .gitignore
 .mailmap
+.stestr.conf
 .testr.conf
 AUTHORS
 CODE_OF_CONDUCT.md
@@ -96,6 +97,16 @@ dulwich.egg-info/entry_points.txt
 dulwich.egg-info/not-zip-safe
 dulwich.egg-info/requires.txt
 dulwich.egg-info/top_level.txt
+dulwich/../docs/tutorial/conclusion.txt
+dulwich/../docs/tutorial/encoding.txt
+dulwich/../docs/tutorial/file-format.txt
+dulwich/../docs/tutorial/index.txt
+dulwich/../docs/tutorial/introduction.txt
+dulwich/../docs/tutorial/object-store.txt
+dulwich/../docs/tutorial/porcelain.txt
+dulwich/../docs/tutorial/remote.txt
+dulwich/../docs/tutorial/repo.txt
+dulwich/../docs/tutorial/tag.txt
 dulwich/cloud/__init__.py
 dulwich/cloud/gcs.py
 dulwich/contrib/README.md

+ 1 - 1
dulwich/__init__.py

@@ -22,4 +22,4 @@
 
 """Python implementation of the Git file formats and protocols."""
 
-__version__ = (0, 20, 26)
+__version__ = (0, 20, 27)

+ 3 - 3
dulwich/hooks.py

@@ -117,10 +117,10 @@ class ShellHook(Hook):
 class PreCommitShellHook(ShellHook):
     """pre-commit shell hook"""
 
-    def __init__(self, controldir):
+    def __init__(self, cwd, controldir):
         filepath = os.path.join(controldir, "hooks", "pre-commit")
 
-        ShellHook.__init__(self, "pre-commit", filepath, 0, cwd=controldir)
+        ShellHook.__init__(self, "pre-commit", filepath, 0, cwd=cwd)
 
 
 class PostCommitShellHook(ShellHook):
@@ -173,7 +173,7 @@ class PostReceiveShellHook(ShellHook):
     def __init__(self, controldir):
         self.controldir = controldir
         filepath = os.path.join(controldir, "hooks", "post-receive")
-        ShellHook.__init__(self, "post-receive", filepath, 0)
+        ShellHook.__init__(self, "post-receive", path=filepath, numparam=0)
 
     def execute(self, client_refs):
         # do nothing if the script doesn't exist

+ 2 - 2
dulwich/object_store.py

@@ -331,8 +331,8 @@ class BaseObjectStore(object):
     def _collect_ancestors(
         self,
         heads,
-        common=set(),
-        shallow=set(),
+        common=frozenset(),
+        shallow=frozenset(),
         get_parents=lambda commit: commit.parents,
     ):
         """Collect all ancestors of heads up to (excluding) those in common.

+ 12 - 2
dulwich/objects.py

@@ -100,7 +100,7 @@ def _decompress(string):
 def sha_to_hex(sha):
     """Takes a string and returns the hex of the sha within"""
     hexsha = binascii.hexlify(sha)
-    assert len(hexsha) == 40, "Incorrect length of sha1 string: %d" % hexsha
+    assert len(hexsha) == 40, "Incorrect length of sha1 string: %s" % hexsha
     return hexsha
 
 
@@ -1032,6 +1032,14 @@ def pretty_format_tree_entry(name, mode, hexsha, encoding="utf-8"):
     )
 
 
+class SubmoduleEncountered(Exception):
+    """A submodule was encountered while resolving a path."""
+
+    def __init__(self, path, sha):
+        self.path = path
+        self.sha = sha
+
+
 class Tree(ShaFile):
     """A Git tree object"""
 
@@ -1182,9 +1190,11 @@ class Tree(ShaFile):
         parts = path.split(b"/")
         sha = self.id
         mode = None
-        for p in parts:
+        for i, p in enumerate(parts):
             if not p:
                 continue
+            if mode is not None and S_ISGITLINK(mode):
+                raise SubmoduleEncountered(b'/'.join(parts[:i]), sha)
             obj = lookup_obj(sha)
             if not isinstance(obj, Tree):
                 raise NotTreeError(sha)

+ 9 - 10
dulwich/repo.py

@@ -895,6 +895,14 @@ class BaseRepo(object):
           New commit SHA1
         """
 
+        try:
+            if not no_verify:
+                self.hooks["pre-commit"].execute()
+        except HookError as e:
+            raise CommitError(e)
+        except KeyError:  # no hook defined, silent fallthrough
+            pass
+
         c = Commit()
         if tree is None:
             index = self.open_index()
@@ -904,14 +912,6 @@ class BaseRepo(object):
                 raise ValueError("tree must be a 40-byte hex sha string")
             c.tree = tree
 
-        try:
-            if not no_verify:
-                self.hooks["pre-commit"].execute()
-        except HookError as e:
-            raise CommitError(e)
-        except KeyError:  # no hook defined, silent fallthrough
-            pass
-
         config = self.get_config_stack()
         if merge_heads is None:
             merge_heads = self._read_heads("MERGE_HEAD")
@@ -1057,7 +1057,6 @@ class Repo(BaseRepo):
             if os.path.isfile(hidden_path):
                 with open(hidden_path, "r") as f:
                     path = read_gitfile(f)
-                self.bare = False
                 self._controldir = os.path.join(root, path)
             else:
                 self._controldir = hidden_path
@@ -1101,7 +1100,7 @@ class Repo(BaseRepo):
             with graft_file:
                 self._graftpoints.update(parse_graftpoints(graft_file))
 
-        self.hooks["pre-commit"] = PreCommitShellHook(self.controldir())
+        self.hooks["pre-commit"] = PreCommitShellHook(self.path, self.controldir())
         self.hooks["commit-msg"] = CommitMsgShellHook(self.controldir())
         self.hooks["post-commit"] = PostCommitShellHook(self.controldir())
         self.hooks["post-receive"] = PostReceiveShellHook(self.controldir())

+ 1 - 1
dulwich/tests/test_hooks.py

@@ -71,7 +71,7 @@ exit 0
         )
 
         pre_commit = os.path.join(repo_dir, "hooks", "pre-commit")
-        hook = PreCommitShellHook(repo_dir)
+        hook = PreCommitShellHook(repo_dir, repo_dir)
 
         with open(pre_commit, "w") as f:
             f.write(pre_commit_fail)

+ 9 - 0
dulwich/tests/test_object_store.py

@@ -42,6 +42,8 @@ from dulwich.objects import (
     Tree,
     TreeEntry,
     EmptyFileException,
+    SubmoduleEncountered,
+    S_IFGITLINK,
 )
 from dulwich.object_store import (
     DiskObjectStore,
@@ -582,6 +584,7 @@ class TreeLookupPathTests(TestCase):
             (b"ad/bd/c", blob_c.id, 0o100755),
             (b"ad/c", blob_c.id, 0o100644),
             (b"c", blob_c.id, 0o100644),
+            (b"d", blob_c.id, S_IFGITLINK),
         ]
         self.tree_id = commit_tree(self.store, blobs)
 
@@ -600,6 +603,12 @@ class TreeLookupPathTests(TestCase):
         o_id = tree_lookup_path(self.get_object, self.tree_id, b"ad/bd/")[1]
         self.assertTrue(isinstance(self.store[o_id], Tree))
 
+    def test_lookup_submodule(self):
+        tree_lookup_path(self.get_object, self.tree_id, b"d")[1]
+        self.assertRaises(
+            SubmoduleEncountered, tree_lookup_path, self.get_object,
+            self.tree_id, b"d/a")
+
     def test_lookup_nonexistent(self):
         self.assertRaises(
             KeyError, tree_lookup_path, self.get_object, self.tree_id, b"j"

+ 49 - 3
dulwich/tests/test_repository.py

@@ -568,9 +568,9 @@ exit 0
         self.assertRaises(
             errors.CommitError,
             r.do_commit,
-            "failed commit",
-            committer="Test Committer <test@nodomain.com>",
-            author="Test Author <test@nodomain.com>",
+            b"failed commit",
+            committer=b"Test Committer <test@nodomain.com>",
+            author=b"Test Author <test@nodomain.com>",
             commit_timestamp=12345,
             commit_timezone=0,
             author_timestamp=12345,
@@ -642,6 +642,52 @@ exit 0
         )
         self.assertEqual([], r[commit_sha].parents)
 
+    def test_shell_hook_pre_commit_add_files(self):
+        if os.name != "posix":
+            self.skipTest("shell hook tests requires POSIX shell")
+
+        pre_commit_contents = """#!%(executable)s
+import sys
+sys.path.extend(':'.join(%(path)s))
+from dulwich.repo import Repo
+
+with open('foo', 'w') as f:
+    f.write('newfile')
+
+r = Repo('.')
+r.stage(['foo'])
+""" % {'executable': sys.executable, 'path': repr(sys.path)}
+
+        repo_dir = os.path.join(self.mkdtemp())
+        self.addCleanup(shutil.rmtree, repo_dir)
+        r = Repo.init(repo_dir)
+        self.addCleanup(r.close)
+
+        with open(os.path.join(repo_dir, 'blah'), 'w') as f:
+            f.write('blah')
+
+        r.stage(['blah'])
+
+        pre_commit = os.path.join(r.controldir(), "hooks", "pre-commit")
+
+        with open(pre_commit, "w") as f:
+            f.write(pre_commit_contents)
+        os.chmod(pre_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
+
+        commit_sha = r.do_commit(
+            b"new commit",
+            committer=b"Test Committer <test@nodomain.com>",
+            author=b"Test Author <test@nodomain.com>",
+            commit_timestamp=12395,
+            commit_timezone=0,
+            author_timestamp=12395,
+            author_timezone=0,
+        )
+        self.assertEqual([], r[commit_sha].parents)
+
+        tree = r[r[commit_sha].tree]
+        self.assertEqual(set([b'blah', b'foo']), set(tree))
+
     def test_shell_hook_post_commit(self):
         if os.name != "posix":
             self.skipTest("shell hook tests requires POSIX shell")

+ 1 - 1
setup.py

@@ -23,7 +23,7 @@ if sys.version_info < (3, 6):
         'For 2.7 support, please install a version prior to 0.20')
 
 
-dulwich_version_string = '0.20.26'
+dulwich_version_string = '0.20.27'
 
 
 class DulwichDistribution(Distribution):