Ver Fonte

Add support for format argument to Repo.init() and Repo.init_bare() (#1584)

This allows creating repositories with different format versions by
specifying the repository format version (0 or 1) which sets the
core.repositoryformatversion configuration value.
Jelmer Vernooij há 1 mês atrás
pai
commit
475b3ec780
3 ficheiros alterados com 67 adições e 6 exclusões
  1. 5 0
      NEWS
  2. 26 6
      dulwich/repo.py
  3. 36 0
      tests/test_repository.py

+ 5 - 0
NEWS

@@ -1,5 +1,10 @@
 0.22.9	UNRELEASED
 0.22.9	UNRELEASED
 
 
+ * Add support for ``format`` argument to ``Repo.init()`` and ``Repo.init_bare()``
+   to specify repository format version (0 or 1). This allows creating repositories
+   with different format versions by setting the ``core.repositoryformatversion``
+   configuration value. (Jelmer Vernooij)
+
  * Fix ``porcelain.add()`` to stage both untracked and modified files when no
  * Fix ``porcelain.add()`` to stage both untracked and modified files when no
    paths are specified. Previously, only untracked files were staged, inconsistent
    paths are specified. Previously, only untracked files were staged, inconsistent
    with Git's behavior. Now behaves like ``git add -A`` when called without paths.
    with Git's behavior. Now behaves like ``git add -A`` when called without paths.

+ 26 - 6
dulwich/repo.py

@@ -401,14 +401,20 @@ class BaseRepo:
         # For now, just mimic the old behaviour
         # For now, just mimic the old behaviour
         return sys.platform != "win32"
         return sys.platform != "win32"
 
 
-    def _init_files(self, bare: bool, symlinks: Optional[bool] = None) -> None:
+    def _init_files(
+        self, bare: bool, symlinks: Optional[bool] = None, format: Optional[int] = None
+    ) -> None:
         """Initialize a default set of named files."""
         """Initialize a default set of named files."""
         from .config import ConfigFile
         from .config import ConfigFile
 
 
         self._put_named_file("description", b"Unnamed repository")
         self._put_named_file("description", b"Unnamed repository")
         f = BytesIO()
         f = BytesIO()
         cf = ConfigFile()
         cf = ConfigFile()
-        cf.set("core", "repositoryformatversion", "0")
+        if format is None:
+            format = 0
+        if format not in (0, 1):
+            raise ValueError(f"Unsupported repository format version: {format}")
+        cf.set("core", "repositoryformatversion", str(format))
         if self._determine_file_mode():
         if self._determine_file_mode():
             cf.set("core", "filemode", True)
             cf.set("core", "filemode", True)
         else:
         else:
@@ -1737,6 +1743,7 @@ class Repo(BaseRepo):
         config=None,
         config=None,
         default_branch=None,
         default_branch=None,
         symlinks: Optional[bool] = None,
         symlinks: Optional[bool] = None,
+        format: Optional[int] = None,
     ):
     ):
         for d in BASE_DIRECTORIES:
         for d in BASE_DIRECTORIES:
             os.mkdir(os.path.join(controldir, *d))
             os.mkdir(os.path.join(controldir, *d))
@@ -1753,7 +1760,7 @@ class Repo(BaseRepo):
             except KeyError:
             except KeyError:
                 default_branch = DEFAULT_BRANCH
                 default_branch = DEFAULT_BRANCH
         ret.refs.set_symbolic_ref(b"HEAD", LOCAL_BRANCH_PREFIX + default_branch)
         ret.refs.set_symbolic_ref(b"HEAD", LOCAL_BRANCH_PREFIX + default_branch)
-        ret._init_files(bare=bare, symlinks=symlinks)
+        ret._init_files(bare=bare, symlinks=symlinks, format=format)
         return ret
         return ret
 
 
     @classmethod
     @classmethod
@@ -1765,12 +1772,14 @@ class Repo(BaseRepo):
         config=None,
         config=None,
         default_branch=None,
         default_branch=None,
         symlinks: Optional[bool] = None,
         symlinks: Optional[bool] = None,
+        format: Optional[int] = None,
     ) -> "Repo":
     ) -> "Repo":
         """Create a new repository.
         """Create a new repository.
 
 
         Args:
         Args:
           path: Path in which to create the repository
           path: Path in which to create the repository
           mkdir: Whether to create the directory
           mkdir: Whether to create the directory
+          format: Repository format version (defaults to 0)
         Returns: `Repo` instance
         Returns: `Repo` instance
         """
         """
         if mkdir:
         if mkdir:
@@ -1785,6 +1794,7 @@ class Repo(BaseRepo):
             config=config,
             config=config,
             default_branch=default_branch,
             default_branch=default_branch,
             symlinks=symlinks,
             symlinks=symlinks,
+            format=format,
         )
         )
 
 
     @classmethod
     @classmethod
@@ -1827,7 +1837,14 @@ class Repo(BaseRepo):
 
 
     @classmethod
     @classmethod
     def init_bare(
     def init_bare(
-        cls, path, *, mkdir=False, object_store=None, config=None, default_branch=None
+        cls,
+        path,
+        *,
+        mkdir=False,
+        object_store=None,
+        config=None,
+        default_branch=None,
+        format: Optional[int] = None,
     ):
     ):
         """Create a new bare repository.
         """Create a new bare repository.
 
 
@@ -1835,6 +1852,7 @@ class Repo(BaseRepo):
 
 
         Args:
         Args:
           path: Path to create bare repository in
           path: Path to create bare repository in
+          format: Repository format version (defaults to 0)
         Returns: a `Repo` instance
         Returns: a `Repo` instance
         """
         """
         if mkdir:
         if mkdir:
@@ -1846,6 +1864,7 @@ class Repo(BaseRepo):
             object_store=object_store,
             object_store=object_store,
             config=config,
             config=config,
             default_branch=default_branch,
             default_branch=default_branch,
+            format=format,
         )
         )
 
 
     create = init_bare
     create = init_bare
@@ -2032,7 +2051,7 @@ class MemoryRepo(BaseRepo):
         return self._config
         return self._config
 
 
     @classmethod
     @classmethod
-    def init_bare(cls, objects, refs):
+    def init_bare(cls, objects, refs, format: Optional[int] = None):
         """Create a new bare repository in memory.
         """Create a new bare repository in memory.
 
 
         Args:
         Args:
@@ -2040,11 +2059,12 @@ class MemoryRepo(BaseRepo):
             as iterable
             as iterable
           refs: Refs as dictionary, mapping names
           refs: Refs as dictionary, mapping names
             to object SHA1s
             to object SHA1s
+          format: Repository format version (defaults to 0)
         """
         """
         ret = cls()
         ret = cls()
         for obj in objects:
         for obj in objects:
             ret.object_store.add_object(obj)
             ret.object_store.add_object(obj)
         for refname, sha in refs.items():
         for refname, sha in refs.items():
             ret.refs.add_if_new(refname, sha)
             ret.refs.add_if_new(refname, sha)
-        ret._init_files(bare=True)
+        ret._init_files(bare=True, format=format)
         return ret
         return ret

+ 36 - 0
tests/test_repository.py

@@ -342,6 +342,42 @@ class RepositoryRootTests(TestCase):
         self.assertEqual(os.listdir(repo_dir), [".git"])
         self.assertEqual(os.listdir(repo_dir), [".git"])
         self.assertFilesystemHidden(os.path.join(repo_dir, ".git"))
         self.assertFilesystemHidden(os.path.join(repo_dir, ".git"))
 
 
+    def test_init_format(self) -> None:
+        tmp_dir = self.mkdtemp()
+        self.addCleanup(shutil.rmtree, tmp_dir)
+
+        # Test format 0
+        t0 = Repo.init(tmp_dir + "0", mkdir=True, format=0)
+        self.addCleanup(t0.close)
+        self.assertEqual(t0.get_config().get("core", "repositoryformatversion"), b"0")
+
+        # Test format 1
+        t1 = Repo.init(tmp_dir + "1", mkdir=True, format=1)
+        self.addCleanup(t1.close)
+        self.assertEqual(t1.get_config().get("core", "repositoryformatversion"), b"1")
+
+        # Test default format
+        td = Repo.init(tmp_dir + "d", mkdir=True)
+        self.addCleanup(td.close)
+        self.assertEqual(td.get_config().get("core", "repositoryformatversion"), b"0")
+
+        # Test invalid format
+        with self.assertRaises(ValueError):
+            Repo.init(tmp_dir + "bad", mkdir=True, format=99)
+
+    def test_init_bare_format(self) -> None:
+        tmp_dir = self.mkdtemp()
+        self.addCleanup(shutil.rmtree, tmp_dir)
+
+        # Test format 1 for bare repo
+        t = Repo.init_bare(tmp_dir + "bare", mkdir=True, format=1)
+        self.addCleanup(t.close)
+        self.assertEqual(t.get_config().get("core", "repositoryformatversion"), b"1")
+
+        # Test invalid format for bare repo
+        with self.assertRaises(ValueError):
+            Repo.init_bare(tmp_dir + "badbr", mkdir=True, format=2)
+
     @skipIf(sys.platform == "win32", "fails on Windows")
     @skipIf(sys.platform == "win32", "fails on Windows")
     def test_fetch(self) -> None:
     def test_fetch(self) -> None:
         r = self.open_repo("a.git")
         r = self.open_repo("a.git")