Skip to content
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

Add support for several more submodule functions #1012

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
33 changes: 33 additions & 0 deletions pygit2/decl/submodule.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION ...

typedef enum {
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
} git_submodule_status_t;

typedef struct git_submodule_update_options {
unsigned int version;
git_checkout_options checkout_opts;
Expand Down Expand Up @@ -34,8 +51,24 @@ int git_submodule_open(
git_repository **repo,
git_submodule *submodule);

int git_submodule_init(git_submodule *submodule, int overwrite);
int git_submodule_sync(git_submodule *submodule);
int git_submodule_reload(git_submodule *submodule, int force);
int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore);

int git_submodule_add_to_index(git_submodule *submodule, int write_index);

const char * git_submodule_name(git_submodule *submodule);
const char * git_submodule_path(git_submodule *submodule);
const char * git_submodule_url(git_submodule *submodule);
const char * git_submodule_branch(git_submodule *submodule);
git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule);
git_submodule_recurse_t git_submodule_fetch_recurse_submodules(git_submodule *submodule);
const git_oid * git_submodule_head_id(git_submodule *submodule);
const git_oid * git_submodule_index_id(git_submodule *submodule);
const git_oid * git_submodule_wd_id(git_submodule *submodule);

int git_submodule_set_url(git_repository *repo, const char *name, const char *url);
int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch);
int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore);
int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t fetch_recurse_submodules);
15 changes: 15 additions & 0 deletions pygit2/decl/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ typedef struct {
typedef int (*git_transport_message_cb)(const char *str, int len, void *payload);
typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);

typedef enum {
GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
GIT_SUBMODULE_UPDATE_REBASE = 2,
GIT_SUBMODULE_UPDATE_MERGE = 3,
GIT_SUBMODULE_UPDATE_NONE = 4,

GIT_SUBMODULE_UPDATE_DEFAULT = 0
} git_submodule_update_t;

typedef enum {
GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1,

Expand All @@ -56,3 +65,9 @@ typedef enum {
GIT_SUBMODULE_IGNORE_DIRTY = 3,
GIT_SUBMODULE_IGNORE_ALL = 4,
} git_submodule_ignore_t;

typedef enum {
GIT_SUBMODULE_RECURSE_NO = 0,
GIT_SUBMODULE_RECURSE_YES = 1,
GIT_SUBMODULE_RECURSE_ONDEMAND = 2,
} git_submodule_recurse_t;
15 changes: 4 additions & 11 deletions pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def add_submodule(self, url, path, link=True, callbacks=None):
Parameters:

url
The URL of the submdoule.
The URL of the submodule.

path
The path within the parent repository to add the submodule
Expand Down Expand Up @@ -184,16 +184,9 @@ def update_submodules(self, submodules=None, init=False, callbacks=None):
if submodules is None:
submodules = self.listall_submodules()

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

with git_fetch_options(callbacks, opts=opts.fetch_opts) as payload:
i = 1 if init else 0
for submodule in submodules:
submodule_instance = self.lookup_submodule(submodule)
err = C.git_submodule_update(submodule_instance._subm, i, opts)
payload.check_error(err)
for submodule in submodules:
submodule_instance = self.lookup_submodule(submodule)
submodule_instance.update(init, callbacks)

return None

Expand Down
198 changes: 198 additions & 0 deletions pygit2/submodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,46 @@
# Boston, MA 02110-1301, USA.

from ._pygit2 import Oid
from .callbacks import git_fetch_options
from .errors import check_error
from .ffi import ffi, C
from .utils import to_bytes


# GIT_SUBMODULE_UPDATE_*
GIT_SUBMODULE_UPDATE_CHECKOUT = C.GIT_SUBMODULE_UPDATE_CHECKOUT
GIT_SUBMODULE_UPDATE_REBASE = C.GIT_SUBMODULE_UPDATE_REBASE
GIT_SUBMODULE_UPDATE_MERGE = C.GIT_SUBMODULE_UPDATE_MERGE
GIT_SUBMODULE_UPDATE_NONE = C.GIT_SUBMODULE_UPDATE_NONE
GIT_SUBMODULE_UPDATE_DEFAULT = C.GIT_SUBMODULE_UPDATE_DEFAULT

# GIT_SUBMODULE_RECURSE_*
GIT_SUBMODULE_RECURSE_NO = C.GIT_SUBMODULE_RECURSE_NO
GIT_SUBMODULE_RECURSE_YES = C.GIT_SUBMODULE_RECURSE_YES
GIT_SUBMODULE_RECURSE_ONDEMAND = C.GIT_SUBMODULE_RECURSE_ONDEMAND

# GIT_SUBMODULE_IGNORE_*
GIT_SUBMODULE_IGNORE_UNSPECIFIED = C.GIT_SUBMODULE_IGNORE_UNSPECIFIED
GIT_SUBMODULE_IGNORE_NONE = C.GIT_SUBMODULE_IGNORE_NONE
GIT_SUBMODULE_IGNORE_UNTRACKED = C.GIT_SUBMODULE_IGNORE_UNTRACKED
GIT_SUBMODULE_IGNORE_DIRTY = C.GIT_SUBMODULE_IGNORE_DIRTY
GIT_SUBMODULE_IGNORE_ALL = C.GIT_SUBMODULE_IGNORE_ALL

# GIT_SUBMODULE_STATUS_*
GIT_SUBMODULE_STATUS_IN_HEAD = C.GIT_SUBMODULE_STATUS_IN_HEAD
GIT_SUBMODULE_STATUS_IN_INDEX = C.GIT_SUBMODULE_STATUS_IN_INDEX
GIT_SUBMODULE_STATUS_IN_CONFIG = C.GIT_SUBMODULE_STATUS_IN_CONFIG
GIT_SUBMODULE_STATUS_IN_WD = C.GIT_SUBMODULE_STATUS_IN_WD
GIT_SUBMODULE_STATUS_INDEX_ADDED = C.GIT_SUBMODULE_STATUS_INDEX_ADDED
GIT_SUBMODULE_STATUS_INDEX_DELETED = C.GIT_SUBMODULE_STATUS_INDEX_DELETED
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = C.GIT_SUBMODULE_STATUS_INDEX_MODIFIED
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = C.GIT_SUBMODULE_STATUS_WD_UNINITIALIZED
GIT_SUBMODULE_STATUS_WD_ADDED = C.GIT_SUBMODULE_STATUS_WD_ADDED
GIT_SUBMODULE_STATUS_WD_DELETED = C.GIT_SUBMODULE_STATUS_WD_DELETED
GIT_SUBMODULE_STATUS_WD_MODIFIED = C.GIT_SUBMODULE_STATUS_WD_MODIFIED
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = C.GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = C.GIT_SUBMODULE_STATUS_WD_WD_MODIFIED
GIT_SUBMODULE_STATUS_WD_UNTRACKED = C.GIT_SUBMODULE_STATUS_WD_UNTRACKED


class Submodule:
Expand All @@ -50,12 +88,95 @@ def open(self):

return self._repo._from_c(crepo[0], True)

def init(self, force=False):
"""Copy submodule info into ".git/config" file.

Parameters:

force
Force entry to be updated.
"""
cforce = 1 if force else 0
err = C.git_submodule_init(self._subm, cforce)
check_error(err)

def update(self, init=False, callbacks=None):
"""Update a submodule.

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:

init
If the submodule is not initialized, setting this flag to true will initialize
the submodule before updating. Otherwise, this will return an error if attempting
to update an uninitialzed repository. but setting this to true forces them to be
updated.

callbacks
Configuration options for the update."""
opts = ffi.new('git_submodule_update_options *')
C.git_submodule_update_init_options(opts, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)

with git_fetch_options(callbacks, opts=opts.fetch_opts) as payload:
i = 1 if init else 0
err = C.git_submodule_update(self._subm, i, opts)
payload.check_error(err)

def sync(self):
"""Copy submodule remote info into submodule repo."""
err = C.git_submodule_sync(self._subm)
check_error(err)

def reload(self, force=False):
"""Reread submodule info from config, index, and HEAD.

Parameters:

force
Force reload even if the data doesn't seem out of date.
"""
cforce = 1 if force else 0
err = C.git_submodule_reload(self._subm, cforce)
check_error(err)

def add_to_index(self, write_index=False):
"""Add current submodule HEAD commit to index of superproject.

Parameters:

write_index
Immediately write the index file.
"""
cwrite_index = 1 if write_index else 0
err = C.git_submodule_add_to_index(self._subm, cwrite_index)
check_error(err)

def status(self, ignore=GIT_SUBMODULE_IGNORE_UNSPECIFIED):
"""Get the status for a submodule."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))
cstatus = ffi.new('uint *')

err = C.git_submodule_status(cstatus, crepo, cname, ignore)
check_error(err)

return cstatus[0]

@property
def name(self):
"""Name of the submodule."""
name = C.git_submodule_name(self._subm)
return ffi.string(name).decode('utf-8')

@property
def owner(self):
"""The parent repository"""
return self._repo

@property
def path(self):
"""Path of the submodule."""
Expand All @@ -68,14 +189,91 @@ def url(self):
url = C.git_submodule_url(self._subm)
return ffi.string(url).decode('utf-8')

@url.setter
def url(self, u):
"""Set the URL for the submodule in the configuration."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))
curl = ffi.new('char[]', to_bytes(u))

err = C.git_submodule_set_url(crepo, cname, curl)
check_error(err)

@property
def branch(self):
"""Branch that is to be tracked by the submodule."""
branch = C.git_submodule_branch(self._subm)
return ffi.string(branch).decode('utf-8')

@branch.setter
def branch(self, b):
"""Set the branch for the submodule in the configuration."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))
cbranch = ffi.new('char[]', to_bytes(b))

err = C.git_submodule_set_branch(crepo, cname, cbranch)
check_error(err)

@property
def head_id(self):
"""Head of the submodule."""
head = C.git_submodule_head_id(self._subm)
return Oid(raw=bytes(ffi.buffer(head)[:]))

@property
def index_id(self):
"""Index OID of the submodule."""
oid = C.git_submodule_index_id(self._subm)
return Oid(raw=bytes(ffi.buffer(oid)[:]))

@property
def wd_id(self):
"""Current working directory OID of the submodule."""
oid = C.git_submodule_wd_id(self._subm)
return Oid(raw=bytes(ffi.buffer(oid)[:]))

@property
def ignore_rule(self):
"""Get the ignore rule that will be used for the submodule."""
res = C.git_submodule_ignore(self._subm)
return res

@ignore_rule.setter
def ignore_rule(self, i):
"""Set the ignore rule for the submodule in the configuration."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))

err = C.git_submodule_set_ignore(crepo, cname, i)
check_error(err)


@property
def fetch_recurse_rule(self):
"""Get the fetchRecurseSubmodules rule for a submodule."""
res = C.git_submodule_fetch_recurse_submodules(self._subm)
return res

@fetch_recurse_rule.setter
def fetch_recurse_rule(self, f):
"""Set the fetchRecurseSubmodules rule for a submodule in the configuration."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))

C.git_submodule_set_fetch_recurse_submodules(crepo, cname, f)

@property
def update_rule(self):
"""Get the update rule that will be used for the submodule."""
res = C.git_submodule_update_strategy(self._subm)
return res

@update_rule.setter
def update_rule(self, u):
"""Set the ignore rule for the submodule in the configuration."""
crepo = self._repo._repo
cname = ffi.new('char[]', to_bytes(self.name))

err = C.git_submodule_set_update(crepo, cname, u)
check_error(err)