Commit 1aad6137 by Russell Belfer

Submodule status improvements

This fixes the way that submodule status is checked to bypass just
about all of the caching in the submodule object.  Based on the
ignore value, it will try to do the minimum work necessary to find
the current status of the submodule - but it will actually go to
disk to get all of the current values.

This also removes the custom refcounting stuff in favor of the
common git_refcount style.  Right now, it is still for internal
purposes only, but it should make it easier to add true submodule
refcounting in the future with a public git_submodule_free call
that will allow bindings not to worry about the submodule object
getting freed from underneath them.
parent 3fe046cf
......@@ -119,19 +119,9 @@ typedef enum {
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
} git_submodule_status_t;
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
(GIT_SUBMODULE_STATUS_IN_HEAD | \
GIT_SUBMODULE_STATUS_IN_INDEX | \
GIT_SUBMODULE_STATUS_IN_CONFIG | \
GIT_SUBMODULE_STATUS_IN_WD)
#define GIT_SUBMODULE_STATUS__INDEX_FLAGS \
(GIT_SUBMODULE_STATUS_INDEX_ADDED | \
GIT_SUBMODULE_STATUS_INDEX_DELETED | \
GIT_SUBMODULE_STATUS_INDEX_MODIFIED)
#define GIT_SUBMODULE_STATUS__WD_FLAGS \
~(GIT_SUBMODULE_STATUS__IN_FLAGS | GIT_SUBMODULE_STATUS__INDEX_FLAGS)
#define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu
#define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u
#define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u
#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
(((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
......
......@@ -71,15 +71,11 @@ static int load_submodule_config(git_repository *repo);
static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
static int lookup_head_remote(git_buf *url, git_repository *repo);
static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
static void submodule_release(git_submodule *sm, int decr);
static int submodule_load_from_index(git_repository *, const git_index_entry *);
static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
static int submodule_load_from_config(const git_config_entry *, void *);
static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
static int submodule_index_status(unsigned int *status, git_submodule *sm);
static int submodule_wd_status(unsigned int *status, git_submodule *sm);
static void submodule_get_index_status(unsigned int *, git_submodule *);
static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
static int submodule_cmp(const void *a, const void *b)
{
......@@ -161,7 +157,7 @@ int git_submodule_foreach(
* us from issuing a callback twice for a submodule where the name
* and path are not the same.
*/
if (sm->refcount > 1) {
if (GIT_REFCOUNT_VAL(sm) > 1) {
if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND)
continue;
if ((error = git_vector_insert(&seen, sm)) < 0)
......@@ -193,9 +189,7 @@ void git_submodule_config_free(git_repository *repo)
if (smcfg == NULL)
return;
git_strmap_foreach_value(smcfg, sm, {
submodule_release(sm,1);
});
git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); });
git_strmap_free(smcfg);
}
......@@ -336,7 +330,7 @@ int git_submodule_add_finalize(git_submodule *sm)
assert(sm);
if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
return error;
......@@ -346,7 +340,7 @@ int git_submodule_add_finalize(git_submodule *sm)
int git_submodule_add_to_index(git_submodule *sm, int write_index)
{
int error;
git_repository *repo, *sm_repo = NULL;
git_repository *sm_repo = NULL;
git_index *index;
git_buf path = GIT_BUF_INIT;
git_commit *head;
......@@ -355,14 +349,12 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
assert(sm);
repo = sm->owner;
/* force reload of wd OID by git_submodule_open */
sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
(error = git_buf_joinpath(
&path, git_repository_workdir(repo), sm->path)) < 0 ||
&path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
(error = git_submodule_open(&sm_repo, sm)) < 0)
goto cleanup;
......@@ -422,7 +414,7 @@ int git_submodule_save(git_submodule *submodule)
assert(submodule);
mods = open_gitmodules(submodule->owner, true, NULL);
mods = open_gitmodules(submodule->repo, true, NULL);
if (!mods) {
giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported (for now)");
......@@ -485,7 +477,7 @@ cleanup:
git_repository *git_submodule_owner(git_submodule *submodule)
{
assert(submodule);
return submodule->owner;
return submodule->repo;
}
const char *git_submodule_name(git_submodule *submodule)
......@@ -542,20 +534,12 @@ const git_oid *git_submodule_wd_id(git_submodule *submodule)
{
assert(submodule);
/* if we know the submodule index timestamp and it has moved, then
* let's reload the working directory information for the submodule
*/
if (submodule->wd_head_path != NULL &&
git_futils_filestamp_check(
&submodule->wd_stamp, submodule->wd_head_path))
submodule->flags &= ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
/* load unless we think we have a valid oid */
if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
git_repository *subrepo;
/* calling submodule open grabs the HEAD OID if possible */
if (!git_submodule_open(&subrepo, submodule))
if (!git_submodule_open_bare(&subrepo, submodule))
git_repository_free(subrepo);
else
giterr_clear();
......@@ -674,74 +658,68 @@ int git_submodule_sync(git_submodule *submodule)
submodule, "url", submodule->url, true, true);
}
int git_submodule_open(
git_repository **subrepo,
git_submodule *submodule)
static int git_submodule__open(
git_repository **subrepo, git_submodule *sm, bool bare)
{
int error;
git_buf path = GIT_BUF_INIT;
git_repository *repo;
const char *workdir;
unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
const char *wd;
assert(submodule && subrepo);
assert(sm && subrepo);
repo = submodule->owner;
workdir = git_repository_workdir(repo);
if (git_repository__ensure_not_bare(
sm->repo, "open submodule repository") < 0)
return GIT_EBAREREPO;
if (!workdir) {
giterr_set(GITERR_REPOSITORY,
"Cannot open submodule repository in a bare repo");
return GIT_ENOTFOUND;
}
if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
giterr_set(GITERR_REPOSITORY,
"Cannot open submodule repository that is not checked out");
return GIT_ENOTFOUND;
}
wd = git_repository_workdir(sm->repo);
if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
return -1;
error = git_repository_open(subrepo, path.ptr);
sm->flags = sm->flags &
~(GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS__WD_OID_VALID |
GIT_SUBMODULE_STATUS__WD_SCANNED);
git_buf_free(&path);
if (bare)
flags |= GIT_REPOSITORY_OPEN_BARE;
/* if we have opened the submodule successfully, let's grab the HEAD OID */
if (!error) {
git_buf buf = GIT_BUF_INIT;
error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
/* For now, let's just the index timestamp...
*
* git_buf_joinpath(&buf, git_repository_path(*subrepo), GIT_HEAD_FILE);
* if (!git_path_exists(buf.ptr)) {
*/
git_index *index;
if (!git_repository_index__weakptr(&index, *subrepo))
git_buf_sets(&buf, git_index_path(index));
else
git_buf_free(&buf);
/* } */
/* if we opened the submodule successfully, grab HEAD OID, etc. */
if (!error) {
sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS__WD_SCANNED;
if (git_buf_len(&buf) > 0) {
git__free(submodule->wd_head_path);
submodule->wd_head_path = git_buf_detach(&buf);
}
if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
else
giterr_clear();
} else if (git_path_exists(path.ptr)) {
sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
GIT_SUBMODULE_STATUS_IN_WD;
} else {
git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
if (!git_reference_name_to_id(
&submodule->wd_oid, *subrepo, GIT_HEAD_FILE)) {
if (git_path_isdir(path.ptr))
sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
}
submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
git_buf_free(&path);
if (submodule->wd_head_path)
git_futils_filestamp_check(
&submodule->wd_stamp, submodule->wd_head_path);
}
return error;
}
giterr_clear();
}
int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
{
return git_submodule__open(subrepo, sm, true);
}
return error;
int git_submodule_open(git_repository **subrepo, git_submodule *sm)
{
return git_submodule__open(subrepo, sm, false);
}
int git_submodule_reload_all(git_repository *repo)
......@@ -751,74 +729,99 @@ int git_submodule_reload_all(git_repository *repo)
return load_submodule_config(repo);
}
int git_submodule_reload(git_submodule *submodule)
static void submodule_update_from_index_entry(
git_submodule *sm, const git_index_entry *ie)
{
git_repository *repo;
git_index *index;
int error;
size_t pos;
git_tree *head;
git_config_backend *mods;
bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
assert(submodule);
if (!S_ISGITLINK(ie->mode)) {
if (!already_found)
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
} else {
if (already_found)
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
else
git_oid_cpy(&sm->index_oid, &ie->oid);
/* refresh index data */
sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
}
}
static int submodule_update_index(git_submodule *sm)
{
git_index *index;
const git_index_entry *ie;
repo = submodule->owner;
if (git_repository_index__weakptr(&index, repo) < 0)
if (git_repository_index__weakptr(&index, sm->repo) < 0)
return -1;
submodule->flags = submodule->flags &
sm->flags = sm->flags &
~(GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
if (!git_index_find(&pos, index, submodule->path)) {
const git_index_entry *entry = git_index_get_byindex(index, pos);
if (!(ie = git_index_get_bypath(index, sm->path, 0)))
return 0;
if (S_ISGITLINK(entry->mode)) {
if ((error = submodule_load_from_index(repo, entry)) < 0)
return error;
} else {
submodule_mode_mismatch(
repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
}
submodule_update_from_index_entry(sm, ie);
return 0;
}
static void submodule_update_from_head_data(
git_submodule *sm, mode_t mode, const git_oid *id)
{
if (!S_ISGITLINK(mode))
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
else {
git_oid_cpy(&sm->head_oid, id);
sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
}
}
/* refresh HEAD tree data */
static int submodule_update_head(git_submodule *submodule)
{
git_tree *head = NULL;
git_tree_entry *te;
if (!(error = git_repository_head_tree(&head, repo))) {
git_tree_entry *te;
submodule->flags = submodule->flags &
~(GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
submodule->flags = submodule->flags &
~(GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
/* if we can't look up file in current head, then done */
if (git_repository_head_tree(&head, submodule->repo) < 0 ||
git_tree_entry_bypath(&te, head, submodule->path) < 0)
giterr_clear();
else
submodule_update_from_head_data(submodule, te->attr, &te->oid);
if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
git_tree_free(head);
return 0;
}
if (S_ISGITLINK(te->attr)) {
error = submodule_load_from_head(repo, submodule->path, &te->oid);
} else {
submodule_mode_mismatch(
repo, submodule->path,
GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
}
int git_submodule_reload(git_submodule *submodule)
{
int error = 0;
git_config_backend *mods;
git_tree_entry_free(te);
}
else if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
assert(submodule);
git_tree_free(head);
}
/* refresh index data */
if (error < 0)
return error;
if (submodule_update_index(submodule) < 0)
return -1;
/* refresh HEAD tree data */
if (submodule_update_head(submodule) < 0)
return -1;
/* refresh config data */
if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
mods = open_gitmodules(submodule->repo, false, NULL);
if (mods != NULL) {
git_buf path = GIT_BUF_INIT;
git_buf_sets(&path, "submodule\\.");
......@@ -829,7 +832,7 @@ int git_submodule_reload(git_submodule *submodule)
error = -1;
else
error = git_config_file_foreach_match(
mods, path.ptr, submodule_load_from_config, repo);
mods, path.ptr, submodule_load_from_config, submodule->repo);
git_buf_free(&path);
git_config_file_free(mods);
......@@ -848,38 +851,91 @@ int git_submodule_reload(git_submodule *submodule)
return error;
}
int git_submodule_status(
unsigned int *status,
git_submodule *submodule)
static void submodule_copy_oid_maybe(
git_oid *tgt, const git_oid *src, bool valid)
{
int error = 0;
unsigned int status_val;
if (tgt) {
if (valid)
memcpy(tgt, src, sizeof(*tgt));
else
memset(tgt, 0, sizeof(*tgt));
}
}
assert(status && submodule);
int git_submodule__status(
unsigned int *out_status,
git_oid *out_head_id,
git_oid *out_index_id,
git_oid *out_wd_id,
git_submodule *sm,
git_submodule_ignore_t ign)
{
unsigned int status;
git_repository *smrepo = NULL;
status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
if (ign == GIT_SUBMODULE_IGNORE_DEFAULT)
ign = sm->ignore;
if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
if (!(error = submodule_index_status(&status_val, submodule)))
error = submodule_wd_status(&status_val, submodule);
/* only return location info if ignore == all */
if (ign == GIT_SUBMODULE_IGNORE_ALL) {
*out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
return 0;
}
*status = status_val;
/* refresh the index OID */
if (submodule_update_index(sm) < 0)
return -1;
/* refresh the HEAD OID */
if (submodule_update_head(sm) < 0)
return -1;
return error;
/* for ignore == dirty, don't scan the working directory */
if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
/* git_submodule_open_bare will load WD OID data */
if (git_submodule_open_bare(&smrepo, sm) < 0)
giterr_clear();
else
git_repository_free(smrepo);
smrepo = NULL;
} else if (git_submodule_open(&smrepo, sm) < 0) {
giterr_clear();
smrepo = NULL;
}
status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
submodule_get_index_status(&status, sm);
submodule_get_wd_status(&status, sm, smrepo, ign);
git_repository_free(smrepo);
*out_status = status;
submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
(sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
(sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
return 0;
}
int git_submodule_location(
unsigned int *location_status,
git_submodule *submodule)
int git_submodule_status(unsigned int *status, git_submodule *sm)
{
assert(location_status && submodule);
assert(status && sm);
*location_status = submodule->flags &
(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD);
return git_submodule__status(
status, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_DEFAULT);
}
return 0;
int git_submodule_location(unsigned int *location, git_submodule *sm)
{
assert(location && sm);
return git_submodule__status(
location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
}
......@@ -889,54 +945,47 @@ int git_submodule_location(
static git_submodule *submodule_alloc(git_repository *repo, const char *name)
{
size_t namelen;
git_submodule *sm;
if (!name || !strlen(name)) {
if (!name || !(namelen = strlen(name))) {
giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
return NULL;
}
sm = git__calloc(1, sizeof(git_submodule));
sm = git__calloc(1, sizeof(git_submodule) + namelen + 1);
if (sm == NULL)
goto fail;
sm->path = sm->name = git__strdup(name);
if (!sm->name)
goto fail;
return NULL;
sm->owner = repo;
sm->refcount = 1;
sm->name = sm->path = git__strdup(name);
if (!sm->name) {
git__free(sm);
return NULL;
}
GIT_REFCOUNT_INC(sm);
sm->repo = repo;
return sm;
fail:
submodule_release(sm, 0);
return NULL;
}
static void submodule_release(git_submodule *sm, int decr)
static void submodule_release(git_submodule *sm)
{
if (!sm)
return;
sm->refcount -= decr;
if (sm->refcount == 0) {
if (sm->name != sm->path) {
git__free(sm->path);
sm->path = NULL;
}
git__free(sm->name);
sm->name = NULL;
git__free(sm->url);
sm->url = NULL;
sm->owner = NULL;
if (sm->path != sm->name)
git__free(sm->path);
git__free(sm->name);
git__free(sm->url);
git__memzero(sm, sizeof(*sm));
git__free(sm);
}
git__free(sm);
}
void git_submodule_free(git_submodule *sm)
{
if (!sm)
return;
GIT_REFCOUNT_DEC(sm, submodule_release);
}
static int submodule_get(
......@@ -966,10 +1015,10 @@ static int submodule_get(
pos = kh_put(str, smcfg, sm->name, &error);
if (error < 0) {
submodule_release(sm, 1);
git_submodule_free(sm);
sm = NULL;
} else if (error == 0) {
submodule_release(sm, 1);
git_submodule_free(sm);
sm = git_strmap_value_at(smcfg, pos);
} else {
git_strmap_set_value_at(smcfg, pos, sm);
......@@ -983,43 +1032,6 @@ static int submodule_get(
return (sm != NULL) ? 0 : -1;
}
static int submodule_load_from_index(
git_repository *repo, const git_index_entry *entry)
{
git_submodule *sm;
if (submodule_get(&sm, repo, entry->path, NULL) < 0)
return -1;
if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
return 0;
}
sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
git_oid_cpy(&sm->index_oid, &entry->oid);
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
return 0;
}
static int submodule_load_from_head(
git_repository *repo, const char *path, const git_oid *oid)
{
git_submodule *sm;
if (submodule_get(&sm, repo, path, NULL) < 0)
return -1;
sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
git_oid_cpy(&sm->head_oid, oid);
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
return 0;
}
static int submodule_config_error(const char *property, const char *value)
{
giterr_set(GITERR_INVALID,
......@@ -1079,11 +1091,11 @@ static int submodule_load_from_config(
git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
if (error >= 0)
sm->refcount++; /* inserted under a new key */
GIT_REFCOUNT_INC(sm); /* inserted under a new key */
/* if we replaced an old module under this key, release the old one */
if (old_sm && ((git_submodule *)old_sm) != sm) {
submodule_release(old_sm, 1);
git_submodule_free(old_sm);
/* TODO: log warning about multiple submodules with same path */
}
}
......@@ -1133,13 +1145,15 @@ static int submodule_load_from_config(
static int submodule_load_from_wd_lite(
git_submodule *sm, const char *name, void *payload)
{
git_repository *repo = git_submodule_owner(sm);
git_buf path = GIT_BUF_INIT;
GIT_UNUSED(name);
GIT_UNUSED(payload);
if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
if (git_repository_is_bare(sm->repo))
return 0;
if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
return -1;
if (git_path_isdir(path.ptr))
......@@ -1153,18 +1167,6 @@ static int submodule_load_from_wd_lite(
return 0;
}
static void submodule_mode_mismatch(
git_repository *repo, const char *path, unsigned int flag)
{
khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
if (git_strmap_valid_index(repo->submodules, pos)) {
git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
sm->flags |= flag;
}
}
static int load_submodule_config_from_index(
git_repository *repo, git_oid *gitmodules_oid)
{
......@@ -1178,18 +1180,21 @@ static int load_submodule_config_from_index(
return error;
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
git_submodule *sm;
if (S_ISGITLINK(entry->mode)) {
error = submodule_load_from_index(repo, entry);
if (error < 0)
break;
} else {
submodule_mode_mismatch(
repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
if (git_strmap_valid_index(repo->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos);
if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
git_oid_cpy(gitmodules_oid, &entry->oid);
}
if (S_ISGITLINK(entry->mode))
submodule_update_from_index_entry(sm, entry);
else
sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, repo, entry->path, NULL))
submodule_update_from_index_entry(sm, entry);
} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
git_oid_cpy(gitmodules_oid, &entry->oid);
}
if (error == GIT_ITEROVER)
......@@ -1220,18 +1225,24 @@ static int load_submodule_config_from_head(
}
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
git_submodule *sm;
if (S_ISGITLINK(entry->mode)) {
error = submodule_load_from_head(repo, entry->path, &entry->oid);
if (error < 0)
break;
} else {
submodule_mode_mismatch(
repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
if (git_strmap_valid_index(repo->submodules, pos)) {
sm = git_strmap_value_at(repo->submodules, pos);
if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
git_oid_iszero(gitmodules_oid))
git_oid_cpy(gitmodules_oid, &entry->oid);
if (S_ISGITLINK(entry->mode))
submodule_update_from_head_data(
sm, entry->mode, &entry->oid);
else
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, repo, entry->path, NULL))
submodule_update_from_head_data(
sm, entry->mode, &entry->oid);
} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
git_oid_iszero(gitmodules_oid)) {
git_oid_cpy(gitmodules_oid, &entry->oid);
}
}
......@@ -1416,7 +1427,7 @@ static int submodule_update_config(
assert(submodule);
error = git_repository_config__weakptr(&config, submodule->owner);
error = git_repository_config__weakptr(&config, submodule->repo);
if (error < 0)
return error;
......@@ -1444,11 +1455,13 @@ cleanup:
return error;
}
static int submodule_index_status(unsigned int *status, git_submodule *sm)
static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
{
const git_oid *head_oid = git_submodule_head_id(sm);
const git_oid *index_oid = git_submodule_index_id(sm);
*status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
if (!head_oid) {
if (index_oid)
*status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
......@@ -1457,27 +1470,22 @@ static int submodule_index_status(unsigned int *status, git_submodule *sm)
*status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
else if (!git_oid_equal(head_oid, index_oid))
*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
return 0;
}
static int submodule_wd_status(unsigned int *status, git_submodule *sm)
static void submodule_get_wd_status(
unsigned int *status,
git_submodule *sm,
git_repository *sm_repo,
git_submodule_ignore_t ign)
{
int error = 0;
const git_oid *wd_oid, *index_oid;
git_repository *sm_repo = NULL;
/* open repo now if we need it (so wd_id() call won't reopen) */
if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
{
if ((error = git_submodule_open(&sm_repo, sm)) < 0)
return error;
}
const git_oid *index_oid = git_submodule_index_id(sm);
const git_oid *wd_oid =
(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
git_tree *sm_head = NULL;
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
index_oid = git_submodule_index_id(sm);
wd_oid = git_submodule_wd_id(sm);
*status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
if (!index_oid) {
if (wd_oid)
......@@ -1493,59 +1501,49 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm)
else if (!git_oid_equal(index_oid, wd_oid))
*status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
if (sm_repo != NULL) {
git_tree *sm_head;
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff;
/* the diffs below could be optimized with an early termination
* option to the git_diff functions, but for now this is sufficient
* (and certainly no worse that what core git does).
*/
/* perform head-to-index diff on submodule */
if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
return error;
/* if we have no repo, then we're done */
if (!sm_repo)
return;
if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* the diffs below could be optimized with an early termination
* option to the git_diff functions, but for now this is sufficient
* (and certainly no worse that what core git does).
*/
error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
if (ign == GIT_SUBMODULE_IGNORE_NONE)
opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
if (!error) {
/* if we don't have an orphaned head, check diff with index */
if (git_repository_head_tree(&sm_head, sm_repo) < 0)
giterr_clear();
else {
/* perform head to index diff on submodule */
if (git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt) < 0)
giterr_clear();
else {
if (git_diff_num_deltas(diff) > 0)
*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
git_diff_list_free(diff);
diff = NULL;
}
git_tree_free(sm_head);
}
if (error < 0)
return error;
/* perform index-to-workdir diff on submodule */
error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
if (!error) {
size_t untracked =
git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
if (untracked > 0)
*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
/* perform index-to-workdir diff on submodule */
if (git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt) < 0)
giterr_clear();
else {
size_t untracked =
git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
if (git_diff_num_deltas(diff) != untracked)
*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
if (untracked > 0)
*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
git_diff_list_free(diff);
diff = NULL;
}
if (git_diff_num_deltas(diff) != untracked)
*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
git_repository_free(sm_repo);
git_diff_list_free(diff);
diff = NULL;
}
return error;
}
......@@ -48,6 +48,10 @@
* an entry for every submodule found in the HEAD and index, and for every
* submodule described in .gitmodules. The fields are as follows:
*
* - `rc` tracks the refcount of how many hash table entries in the
* git_submodule_cache there are for this submodule. It only comes into
* play if the name and path of the submodule differ.
*
* - `name` is the name of the submodule from .gitmodules.
* - `path` is the path to the submodule from the repo root. It is almost
* always the same as `name`.
......@@ -58,18 +62,12 @@
* - `ignore_default` is the ignore value from the config
* - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
*
* - `owner` is the git_repository containing this submodule
* - `repo` is the parent repository that contains this submodule.
* - `flags` after for internal use, tracking where this submodule has been
* found (head, index, config, workdir) and known status info, etc.
* - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
* - `index_oid` is the SHA1 for the submodule recorded in the index.
* - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
* - `wd_index_path` is the path to the index of the checked out submodule
* - `wd_last_index` is a timestamp of that submodule index so we can
* quickly check if the `wd_oid` should be rechecked
* - `refcount` tracks how many hash table entries in the
* git_submodule_cache there are for this submodule. It only comes into
* play if the name and path of the submodule differ.
*
* If the submodule has been added to .gitmodules but not yet git added,
* then the `index_oid` will be zero but still marked valid. If the
......@@ -77,9 +75,11 @@
* then the `index_oid` will be set, but the `url` will be NULL.
*/
struct git_submodule {
git_refcount rc;
/* information from config */
char *name;
char *path; /* important: may point to same string data as "name" */
char *path; /* important: may just point to "name" string */
char *url;
git_submodule_update_t update;
git_submodule_update_t update_default;
......@@ -88,14 +88,11 @@ struct git_submodule {
int fetch_recurse;
/* internal information */
git_repository *owner;
git_repository *repo;
uint32_t flags;
git_oid head_oid;
git_oid index_oid;
git_oid wd_oid;
char *wd_head_path;
git_futils_filestamp wd_stamp;
int refcount;
};
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
......@@ -113,4 +110,21 @@ enum {
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
/* Internal status fn returns status and optionally the various OIDs */
extern int git_submodule__status(
unsigned int *out_status,
git_oid *out_head_id,
git_oid *out_index_id,
git_oid *out_wd_id,
git_submodule *sm,
git_submodule_ignore_t ign);
/* Open submodule repository as bare repo for quick HEAD check, etc. */
extern int git_submodule_open_bare(
git_repository **repo,
git_submodule *submodule);
/* Release reference to submodule object - not currently for external use */
extern void git_submodule_free(git_submodule *sm);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment