Skip to content

Group submodule functions under Repository.submodules #1250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 14 additions & 167 deletions pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,15 @@
from ._pygit2 import Reference, Tree, Commit, Blob, Signature
from ._pygit2 import InvalidSpecError

from .callbacks import git_checkout_options, git_fetch_options, git_stash_apply_options, RemoteCallbacks
from .callbacks import git_checkout_options, git_stash_apply_options
from .config import Config
from .errors import check_error
from .enums import SubmoduleIgnore, SubmoduleStatus
from .ffi import ffi, C
from .index import Index
from .remote import RemoteCollection
from .blame import Blame
from .utils import to_bytes, StrArray
from .submodule import Submodule
from .submodule import Submodule, SubmoduleCollection
from .packbuilder import PackBuilder


Expand All @@ -65,6 +64,7 @@ def _common_init(self):
self.branches = Branches(self)
self.references = References(self)
self.remotes = RemoteCollection(self)
self.submodules = SubmoduleCollection(self)

# Get the pointer as the contents of a buffer and store it for
# later access
Expand Down Expand Up @@ -128,174 +128,21 @@ def __iter__(self):
# Submodules
#

def add_submodule(
self,
url: str,
path: str,
link: bool = True,
callbacks: typing.Optional[RemoteCallbacks] = None
) -> Submodule:
"""
Add a submodule to the index.
The submodule is automatically cloned.

Returns: the submodule that was added.

Parameters:

url
The URL of the submdoule.

path
The path within the parent repository to add the submodule

link
Should workdir contain a gitlink to the repo in `.git/modules` vs. repo directly in workdir.

callbacks
Optional RemoteCallbacks to clone the submodule.
"""
csub = ffi.new('git_submodule **')
curl = ffi.new('char[]', to_bytes(url))
cpath = ffi.new('char[]', to_bytes(path))
gitlink = 1 if link else 0

err = C.git_submodule_add_setup(csub, self._repo, curl, cpath, gitlink)
check_error(err)

submodule_instance = Submodule._from_c(self, csub[0])

# prepare options
opts = ffi.new('git_submodule_update_options *')
C.git_submodule_update_options_init(opts, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)

with git_fetch_options(callbacks, opts=opts.fetch_opts) as payload:
crepo = ffi.new('git_repository **')
err = C.git_submodule_clone(crepo, submodule_instance._subm, opts)
payload.check_error(err)

# clean-up the submodule repository
Repository._from_c(crepo[0], True)

err = C.git_submodule_add_finalize(submodule_instance._subm)
check_error(err)
return submodule_instance
def add_submodule(self, *args, **kwargs):
warnings.warn("Use repo.submodules.add(...)", DeprecationWarning)
return self.submodules.add(*args, **kwargs)

def lookup_submodule(self, path: str) -> Submodule:
"""
Look up submodule information by name or path.
"""
csub = ffi.new('git_submodule **')
cpath = ffi.new('char[]', to_bytes(path))

err = C.git_submodule_lookup(csub, self._repo, cpath)
check_error(err)
return Submodule._from_c(self, csub[0])

def init_submodules(
self,
submodules: typing.Optional[typing.Iterable[str]] = None,
overwrite: bool = False):
"""
Initialize submodules in the repository. Just like "git submodule init",
this copies information about the submodules into ".git/config".

Parameters:

submodules
Optional list of submodule paths or names to initialize
(the submodule is ultimately resolved with Repository.lookup_submodule()).
Default argument initializes all submodules.

overwrite
Flag indicating if initialization should overwrite submodule entries.
"""
if submodules is None:
submodules = self.listall_submodules()
warnings.warn("Use repo.submodules[...]", DeprecationWarning)
return self.submodules[path]

instances = [self.lookup_submodule(s) for s in submodules]
def init_submodules(self, *args, **kwargs):
warnings.warn("Use repo.submodules.init(...)", DeprecationWarning)
return self.submodules.init(*args, **kwargs)

for submodule in instances:
submodule.init(overwrite)

def update_submodules(
self,
submodules: typing.Optional[typing.Iterable[str]] = None,
init: bool = False,
callbacks: typing.Optional[RemoteCallbacks] = None):
"""
Update submodules. This will clone a missing submodule and checkout
the subrepository to the commit specified in the index of the
containing repository. If the submodule repository doesn't contain the
target commit (e.g. because fetchRecurseSubmodules isn't set), then the
submodule is fetched using the fetch options supplied in options.

Parameters:

submodules
Optional list of submodule paths or names (the submodule is ultimately
resolved with Repository.lookup_submodule()). If you omit this parameter
or pass None, all submodules will be updated.

init
If the submodule is not initialized, setting this flag to True will
initialize the submodule before updating. Otherwise, this will raise
an error if attempting to update an uninitialized repository.

callbacks
Optional RemoteCallbacks to clone or fetch the submodule.
"""
if submodules is None:
submodules = self.listall_submodules()

instances = [self.lookup_submodule(s) for s in submodules]

for submodule in instances:
submodule.update(init, callbacks)

def submodule_status(self, name: str, ignore: SubmoduleIgnore = SubmoduleIgnore.UNSPECIFIED) -> SubmoduleStatus:
"""
Get the status of a submodule.

Returns: A combination of SubmoduleStatus flags.

Parameters:

name
Submodule name or path (the submodule is ultimately resolved with lookup_submodule()).

ignore
A SubmoduleIgnore value indicating how deeply to examine the working directory.
"""
cstatus = ffi.new('unsigned int *')
err = C.git_submodule_status(cstatus, self._repo, to_bytes(name), ignore)
check_error(err)
return SubmoduleStatus(cstatus[0])

def submodule_cache_all(self):
"""
Load and cache all submodules in the repository.

Because the `.gitmodules` file is unstructured, loading submodules is an
O(N) operation. Any operation that requires accessing all submodules is O(N^2)
in the number of submodules, if it has to look each one up individually.
This function loads all submodules and caches them so that subsequent calls to
`lookup_submodule` are O(1).
"""
err = C.git_repository_submodule_cache_all(self._repo)
check_error(err)

def submodule_cache_clear(self):
"""
Clear the submodule cache populated by `submodule_cache_all`.
If there is no cache, do nothing.

The cache incorporates data from the repository's configuration, as well
as the state of the working tree, the index, and HEAD. So any time any
of these has changed, the cache might become invalid.
"""
err = C.git_repository_submodule_cache_clear(self._repo)
check_error(err)
def update_submodules(self, *args, **kwargs):
warnings.warn("Use repo.submodules.update(...)", DeprecationWarning)
return self.submodules.update(*args, **kwargs)

#
# Mapping interface
Expand Down
Loading