diff --git a/dulwich/cli.py b/dulwich/cli.py index 38b805f0c..4ccf991c6 100755 --- a/dulwich/cli.py +++ b/dulwich/cli.py @@ -34,7 +34,7 @@ import argparse import optparse import signal -from typing import Dict, Type +from typing import Dict, Type, Optional from dulwich import porcelain from dulwich.client import get_transport_and_path @@ -321,14 +321,6 @@ def run(self, args): porcelain.rev_list(".", args) -class cmd_submodule(Command): - def run(self, args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - for path, sha in porcelain.submodule_list("."): - sys.stdout.write(' %s %s\n' % (sha, path)) - - class cmd_tag(Command): def run(self, args): parser = optparse.OptionParser() @@ -581,10 +573,11 @@ def run(self, args): class SuperCommand(Command): - subcommands = {} # type: Dict[str, Type[Command]] + subcommands: Dict[str, Type[Command]] = {} + default_command: Optional[Type[Command]] = None def run(self, args): - if not args: + if not args and not self.default_command: print("Supported subcommands: %s" % ", ".join(self.subcommands.keys())) return False cmd = args[0] @@ -603,6 +596,30 @@ class cmd_remote(SuperCommand): } +class cmd_submodule_list(Command): + def run(self, argv): + parser = argparse.ArgumentParser() + parser.parse_args(argv) + for path, sha in porcelain.submodule_list("."): + sys.stdout.write(' %s %s\n' % (sha, path)) + + +class cmd_submodule_init(Command): + def run(self, argv): + parser = argparse.ArgumentParser() + parser.parse_args(argv) + porcelain.submodule_init(".") + + +class cmd_submodule(SuperCommand): + + subcommands = { + "init": cmd_submodule_init, + } + + default_command = cmd_submodule_init + + class cmd_check_ignore(Command): def run(self, args): parser = optparse.OptionParser() diff --git a/dulwich/config.py b/dulwich/config.py index 7f09ae57a..857ef520b 100644 --- a/dulwich/config.py +++ b/dulwich/config.py @@ -750,6 +750,12 @@ def sections(self) -> Iterator[Section]: yield section +def read_submodules(path: str) -> Iterator[Tuple[bytes, bytes, bytes]]: + """read a .gitmodules file.""" + cfg = ConfigFile.from_path(path) + return parse_submodules(cfg) + + def parse_submodules(config: ConfigFile) -> Iterator[Tuple[bytes, bytes, bytes]]: """Parse a gitmodules GitConfig file, returning submodules. diff --git a/dulwich/porcelain.py b/dulwich/porcelain.py index 0c2d00d26..fd6f2d4aa 100644 --- a/dulwich/porcelain.py +++ b/dulwich/porcelain.py @@ -43,6 +43,8 @@ * remote{_add} * receive-pack * reset + * submodule_add + * submodule_init * submodule_list * rev-list * tag{_create,_delete,_list} @@ -89,6 +91,7 @@ from dulwich.config import ( ConfigFile, StackedConfig, + read_submodules, ) from dulwich.diff_tree import ( CHANGE_ADD, @@ -989,6 +992,21 @@ def submodule_add(repo, url, path=None, name=None): config.write_to_path() +def submodule_init(repo): + """Initialize submodules. + + Args: + repo: Path to repository + """ + with open_repo_closing(repo) as r: + config = r.get_config() + gitmodules_path = os.path.join(r.path, '.gitmodules') + for path, url, name in read_submodules(gitmodules_path): + config.set((b'submodule', name), b'active', True) + config.set((b'submodule', name), b'url', url) + config.write_to_path() + + def submodule_list(repo): """List submodules. diff --git a/dulwich/tests/test_porcelain.py b/dulwich/tests/test_porcelain.py index c19cf0ed1..6c38a4ba8 100644 --- a/dulwich/tests/test_porcelain.py +++ b/dulwich/tests/test_porcelain.py @@ -1625,6 +1625,10 @@ def test_add(self): \tpath = bar """, f.read()) + def test_init(self): + porcelain.submodule_add(self.repo, "../bar.git", "bar") + porcelain.submodule_init(self.repo) + class PushTests(PorcelainTestCase): def test_simple(self):