Commit daacf96d by Carlos Martín Nieto

Merge pull request #3097 from libgit2/cmn/submodule-config-state

Remove run-time configuration settings from submodules
parents e1f434f8 783672fa
......@@ -384,25 +384,19 @@ static void print_short(git_repository *repo, git_status_list *status)
if (s->index_to_workdir &&
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
{
git_submodule *sm = NULL;
unsigned int smstatus = 0;
if (!git_submodule_lookup(
&sm, repo, s->index_to_workdir->new_file.path)) {
if (!git_submodule_status(&smstatus, sm)) {
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
extra = " (new commits)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
extra = " (untracked content)";
}
if (!git_submodule_status(&smstatus, repo, s->index_to_workdir->new_file.path,
GIT_SUBMODULE_IGNORE_FALLBACK)) {
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
extra = " (new commits)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
extra = " (untracked content)";
}
git_submodule_free(sm);
}
/**
......
......@@ -399,7 +399,7 @@ typedef struct {
* `git_diff_options_init` programmatic initialization.
*/
#define GIT_DIFF_OPTIONS_INIT \
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_FALLBACK, {NULL,0}, NULL, NULL, 3}
/**
* Initializes a `git_diff_options` with default values. Equivalent to
......
......@@ -301,20 +301,6 @@ GIT_EXTERN(int) git_submodule_add_to_index(
int write_index);
/**
* Write submodule settings to .gitmodules file.
*
* This commits any in-memory changes to the submodule to the gitmodules
* file on disk. You may also be interested in `git_submodule_init()` which
* writes submodule info to ".git/config" (which is better for local changes
* to submodule settings) and/or `git_submodule_sync()` which writes
* settings about remotes to the actual submodule repository.
*
* @param submodule The submodule to write.
* @return 0 on success, <0 on failure.
*/
GIT_EXTERN(int) git_submodule_save(git_submodule *submodule);
/**
* Get the containing repository for a submodule.
*
* This returns a pointer to the repository that contains the submodule.
......@@ -373,36 +359,31 @@ GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, co
GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule);
/**
* Set the branch for the submodule.
* Set the branch for the submodule in the configuration
*
* This sets the branch in memory for the submodule. This will be used for
* any following submodule actions while this submodule data is in memory.
*
* After calling this, you may wish to call `git_submodule_save()` to write
* the changes back to the ".gitmodules" file and `git_submodule_sync()` to
* After calling this, you may wish to call `git_submodule_sync()` to
* write the changes to the checked out submodule repository.
*
* @param submodule Pointer to the submodule object
* @param repo the repository to affect
* @param name the name of the submodule to configure
* @param branch Branch that should be used for the submodule
* @return 0 on success, <0 on failure
*/
GIT_EXTERN(int) git_submodule_set_branch(git_submodule *submodule, const char *branch);
GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch);
/**
* Set the URL for the submodule.
* Set the URL for the submodule in the configuration
*
* This sets the URL in memory for the submodule. This will be used for
* any following submodule actions while this submodule data is in memory.
*
* After calling this, you may wish to call `git_submodule_save()` to write
* the changes back to the ".gitmodules" file and `git_submodule_sync()` to
* After calling this, you may wish to call `git_submodule_sync()` to
* write the changes to the checked out submodule repository.
*
* @param submodule Pointer to the submodule object
* @param repo the repository to affect
* @param name the name of the submodule to configure
* @param url URL that should be used for the submodule
* @return 0 on success, <0 on failure
*/
GIT_EXTERN(int) git_submodule_set_url(git_submodule *submodule, const char *url);
GIT_EXTERN(int) git_submodule_set_url(git_repository *repo, const char *name, const char *url);
/**
* Get the OID for the submodule in the index.
......@@ -452,9 +433,6 @@ GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
* The working directory will be consider clean so long as there is a
* checked out version present.
*
* plus the special **GIT_SUBMODULE_IGNORE_RESET** which can be used with
* `git_submodule_set_ignore()` to revert to the on-disk setting.
*
* @param submodule The submodule to check
* @return The current git_submodule_ignore_t valyue what will be used for
* this submodule.
......@@ -463,32 +441,25 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
git_submodule *submodule);
/**
* Set the ignore rule for the submodule.
*
* This sets the in-memory ignore rule for the submodule which will
* control the behavior of `git_submodule_status()`.
* Set the ignore rule for the submodule in the configuration
*
* To make changes persistent, call `git_submodule_save()` to write the
* value to disk (in the ".gitmodules" and ".git/config" files).
* This does not affect any currently-loaded instances.
*
* Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()`
* to revert the in-memory rule to the value that is on disk.
*
* @param submodule The submodule to update
* @param repo the repository to affect
* @param name the name of the submdule
* @param ignore The new value for the ignore rule
* @return old value for ignore
* @return 0 or an error code
*/
GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
git_submodule *submodule,
GIT_EXTERN(int) git_submodule_set_ignore(
git_repository *repo,
const char *name,
git_submodule_ignore_t ignore);
/**
* Get the update rule that will be used for the submodule.
*
* This value controls the behavior of the `git submodule update` command.
* There are four useful values documented with `git_submodule_update_t`
* plus the `GIT_SUBMODULE_UPDATE_RESET` which can be used to revert to
* the on-disk setting.
* There are four useful values documented with `git_submodule_update_t`.
*
* @param submodule The submodule to check
* @return The current git_submodule_update_t value that will be used
......@@ -498,23 +469,18 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy(
git_submodule *submodule);
/**
* Set the update rule for the submodule.
*
* The initial value comes from the ".git/config" setting of
* `submodule.$name.update` for this submodule (which is initialized from
* the ".gitmodules" file). Using this function sets the update rule in
* memory for the submodule. Call `git_submodule_save()` to write out the
* new update rule.
* Set the update rule for the submodule in the configuration
*
* Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling
* `git_submodule_reload()` will revert the rule to the on disk value.
* This setting won't affect any existing instances.
*
* @param submodule The submodule to update
* @param repo the repository to affect
* @param name the name of the submodule to configure
* @param update The new value to use
* @return old value for update
* @return 0 or an error code
*/
GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
git_submodule *submodule,
GIT_EXTERN(int) git_submodule_set_update(
git_repository *repo,
const char *name,
git_submodule_update_t update);
/**
......@@ -532,18 +498,18 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules(
git_submodule *submodule);
/**
* Set the fetchRecurseSubmodules rule for a submodule.
* Set the fetchRecurseSubmodules rule for a submodule in the configuration
*
* This sets the submodule.<name>.fetchRecurseSubmodules value for
* the submodule. You should call `git_submodule_save()` if you want
* to persist the new value.
* This setting won't affect any existing instances.
*
* @param submodule The submodule to modify
* @param repo the repository to affect
* @param name the submodule to configure
* @param fetch_recurse_submodules Boolean value
* @return old value for fetchRecurseSubmodules
*/
GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules(
git_submodule *submodule,
GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
git_repository *repo,
const char *name,
git_submodule_recurse_t fetch_recurse_submodules);
/**
......@@ -634,16 +600,19 @@ GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force);
* This looks at a submodule and tries to determine the status. It
* will return a combination of the `GIT_SUBMODULE_STATUS` values above.
* How deeply it examines the working directory to do this will depend
* on the `git_submodule_ignore_t` value for the submodule - which can be
* set either temporarily or permanently with `git_submodule_set_ignore()`.
* on the `git_submodule_ignore_t` value for the submodule.
*
* @param status Combination of `GIT_SUBMODULE_STATUS` flags
* @param submodule Submodule for which to get status
* @param repo the repository in which to look
* @param name name of the submodule
* @param ignore the ignore rules to follow
* @return 0 on success, <0 on error
*/
GIT_EXTERN(int) git_submodule_status(
unsigned int *status,
git_submodule *submodule);
git_repository *repo,
const char *name,
git_submodule_ignore_t ignore);
/**
* Get the locations of submodule information.
......
......@@ -349,7 +349,6 @@ typedef struct git_submodule git_submodule;
*
* The values are:
*
* - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value.
* - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
* updated, checkout the new detached HEAD to the submodule directory.
* - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
......@@ -362,8 +361,6 @@ typedef struct git_submodule git_submodule;
* when we don't want any particular update rule to be specified.
*/
typedef enum {
GIT_SUBMODULE_UPDATE_RESET = -1,
GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
GIT_SUBMODULE_UPDATE_REBASE = 2,
GIT_SUBMODULE_UPDATE_MERGE = 3,
......@@ -386,7 +383,7 @@ typedef enum {
*
* The values are:
*
* - GIT_SUBMODULE_IGNORE_RESET: reset to the on-disk value.
* - GIT_SUBMODULE_IGNORE_FALLBACK: use the submodule's configuration
* - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
* untracked file, will mark the submodule as dirty. Ignored files are
* still ignored, of course.
......@@ -400,14 +397,12 @@ typedef enum {
* when we don't want any particular ignore rule to be specified.
*/
typedef enum {
GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */
GIT_SUBMODULE_IGNORE_FALLBACK = -1, /**< use the submodule's configuration */
GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t;
/**
......@@ -415,15 +410,12 @@ typedef enum {
*
* Represent the value of `submodule.$name.fetchRecurseSubmodules`
*
* * GIT_SUBMODULE_RECURSE_RESET - reset to the on-disk value
* * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules
* * GIT_SUBMODULE_RECURSE_YES - recurse into submodules
* * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when
* commit not already in local clone
*/
typedef enum {
GIT_SUBMODULE_RECURSE_RESET = -1,
GIT_SUBMODULE_RECURSE_NO = 0,
GIT_SUBMODULE_RECURSE_YES = 1,
GIT_SUBMODULE_RECURSE_ONDEMAND = 2,
......
......@@ -180,7 +180,7 @@ static bool checkout_is_workdir_modified(
return true;
}
if (git_submodule_status(&sm_status, sm) < 0 ||
if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_FALLBACK) < 0 ||
GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
rval = true;
else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
......@@ -1860,11 +1860,6 @@ static int checkout_create_submodules(
git_diff_delta *delta;
size_t i;
/* initial reload of submodules if .gitmodules was changed */
if (data->reload_submodules &&
(error = git_submodule_reload_all(data->repo, 1)) < 0)
return error;
git_vector_foreach(&data->diff->deltas, i, delta) {
if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
/* this has a blocker directory that should only be removed iff
......@@ -1885,8 +1880,7 @@ static int checkout_create_submodules(
}
}
/* final reload once submodules have been updated */
return git_submodule_reload_all(data->repo, 1);
return 0;
}
static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
......
......@@ -1194,6 +1194,26 @@ fail_parse:
return -1;
}
int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
const git_cvar_map *maps, size_t map_n, int enum_val)
{
size_t i;
for (i = 0; i < map_n; i++) {
const git_cvar_map *m = &maps[i];
if (m->map_value != enum_val)
continue;
*type_out = m->cvar_type;
*str_out = m->str_match;
return 0;
}
giterr_set(GITERR_CONFIG, "invalid enum value");
return GIT_ENOTFOUND;
}
int git_config_parse_bool(int *out, const char *value)
{
if (git__parse_bool(out, value) == 0)
......
......@@ -82,4 +82,10 @@ extern int git_config__get_int_force(
extern int git_config__cvar(
int *out, git_config *config, git_cvar_cached cvar);
/**
* The opposite of git_config_lookup_map_value, we take an enum value
* and map it to the string or bool value on the config.
*/
int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
const git_cvar_map *maps, size_t map_n, int enum_val);
#endif
......@@ -186,7 +186,7 @@ static int diff_file_content_commit_to_str(
return error;
}
if ((error = git_submodule_status(&sm_status, sm)) < 0) {
if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0) {
git_submodule_free(sm);
return error;
}
......
......@@ -111,7 +111,6 @@ void git_repository__cleanup(git_repository *repo)
git_cache_clear(&repo->objects);
git_attr_cache_flush(repo);
git_submodule_cache_free(repo);
set_config(repo, NULL);
set_index(repo, NULL);
......
......@@ -121,7 +121,6 @@ struct git_repository {
git_refdb *_refdb;
git_config *_config;
git_index *_index;
git_submodule_cache *_submodules;
git_cache objects;
git_attr_cache *attrcache;
......
......@@ -87,17 +87,16 @@ __KHASH_IMPL(
str, static kh_inline, const char *, void *, 1,
str_hash_no_trailing_slash, str_equal_no_trailing_slash)
static int submodule_cache_init(git_repository *repo, int refresh);
static void submodule_cache_free(git_submodule_cache *cache);
static git_config_backend *open_gitmodules(git_submodule_cache *, int gitmod);
static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
static int get_url_base(git_buf *url, git_repository *repo);
static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
static int submodule_get(git_submodule **, git_submodule_cache *, const char *, const char *);
static int submodule_load_from_config(const git_config_entry *, void *);
static int submodule_load_from_wd_lite(git_submodule *);
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 void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie);
static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id);
static int submodule_cmp(const void *a, const void *b)
{
......@@ -111,69 +110,10 @@ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
return git_buf_puts(key, suffix);
}
/* lookup submodule or return ENOTFOUND if it doesn't exist */
static int submodule_lookup(
git_submodule **out,
git_submodule_cache *cache,
const char *name,
const char *alternate)
{
khiter_t pos;
/* lock cache */
pos = git_strmap_lookup_index(cache->submodules, name);
if (!git_strmap_valid_index(cache->submodules, pos) && alternate)
pos = git_strmap_lookup_index(cache->submodules, alternate);
if (!git_strmap_valid_index(cache->submodules, pos)) {
/* unlock cache */
return GIT_ENOTFOUND; /* don't set error - caller will cope */
}
if (out != NULL) {
git_submodule *sm = git_strmap_value_at(cache->submodules, pos);
GIT_REFCOUNT_INC(sm);
*out = sm;
}
/* unlock cache */
return 0;
}
/* clear a set of flags on all submodules */
static void submodule_cache_clear_flags(
git_submodule_cache *cache, uint32_t mask)
{
git_submodule *sm;
uint32_t inverted_mask = ~mask;
git_strmap_foreach_value(cache->submodules, sm, {
sm->flags &= inverted_mask;
});
}
/*
* PUBLIC APIS
*/
bool git_submodule__is_submodule(git_repository *repo, const char *name)
{
git_strmap *map;
if (submodule_cache_init(repo, CACHE_OK) < 0) {
giterr_clear();
return false;
}
if (!repo->_submodules || !(map = repo->_submodules->submodules))
return false;
return git_strmap_valid_index(map, git_strmap_lookup_index(map, name));
}
static void submodule_set_lookup_error(int error, const char *name)
{
if (!error)
......@@ -184,22 +124,24 @@ static void submodule_set_lookup_error(int error, const char *name)
"Submodule '%s' has not been added yet", name);
}
int git_submodule__lookup(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
const char *name) /* trailing slash is allowed */
{
int error;
assert(repo && name);
typedef struct {
const char *path;
char *name;
} fbp_data;
if ((error = submodule_cache_init(repo, CACHE_OK)) < 0)
return error;
static int find_by_path(const git_config_entry *entry, void *payload)
{
fbp_data *data = payload;
if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0)
submodule_set_lookup_error(error, name);
if (!strcmp(entry->value, data->path)) {
const char *fdot, *ldot;
fdot = strchr(entry->name, '.');
ldot = strrchr(entry->name, '.');
data->name = git__strndup(fdot + 1, ldot - fdot - 1);
GITERR_CHECK_ALLOC(data->name);
}
return error;
return 0;
}
int git_submodule_lookup(
......@@ -208,20 +150,71 @@ int git_submodule_lookup(
const char *name) /* trailing slash is allowed */
{
int error;
unsigned int location;
git_submodule *sm;
assert(repo && name);
if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0)
if ((error = submodule_alloc(&sm, repo, name)) < 0)
return error;
if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) {
if ((error = git_submodule_reload(sm, false)) < 0) {
git_submodule_free(sm);
return error;
}
if ((error = git_submodule_location(&location, sm)) < 0) {
git_submodule_free(sm);
return error;
}
/* If it's not configured, we need to check for the path */
if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_config_backend *mods;
const char *pattern = "submodule\\..*\\.path";
fbp_data data = { name, NULL };
mods = open_gitmodules(repo, GITMODULES_EXISTING);
if (mods)
error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
/* check if a plausible submodule exists at path */
git_config_file_free(mods);
if (error < 0) {
git_submodule_free(sm);
return error;
}
if (data.name) {
git__free(sm->name);
sm->name = data.name;
sm->path = git__strdup(name);
GITERR_CHECK_ALLOC(sm->path);
/* Try to load again with the right name */
if ((error = git_submodule_reload(sm, false)) < 0) {
git_submodule_free(sm);
return error;
}
}
}
if ((error = git_submodule_location(&location, sm)) < 0) {
git_submodule_free(sm);
return error;
}
/* If we still haven't found it, do the WD check */
if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_submodule_free(sm);
error = GIT_ENOTFOUND;
/* If it's not configured, we still check if there's a repo at the path */
if (git_repository_workdir(repo)) {
git_buf path = GIT_BUF_INIT;
if (git_buf_join3(&path,
'/', git_repository_workdir(repo), name, DOT_GIT) < 0)
'/', git_repository_workdir(repo), name, DOT_GIT) < 0)
return -1;
if (git_path_exists(path.ptr))
......@@ -231,9 +224,15 @@ int git_submodule_lookup(
}
submodule_set_lookup_error(error, name);
return error;
}
return error;
if (out)
*out = sm;
else
git_submodule_free(sm);
return 0;
}
static void submodule_free_dup(void *sm)
......@@ -241,41 +240,221 @@ static void submodule_free_dup(void *sm)
git_submodule_free(sm);
}
static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
{
int error = 0;
khiter_t pos;
git_submodule *sm = NULL;
pos = git_strmap_lookup_index(map, name);
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
goto done;
}
/* if the submodule doesn't exist yet in the map, create it */
if ((error = submodule_alloc(&sm, repo, name)) < 0)
return error;
pos = kh_put(str, map, sm->name, &error);
/* nobody can beat us to adding it */
assert(error != 0);
if (error < 0) {
git_submodule_free(sm);
return error;
}
git_strmap_set_value_at(map, pos, sm);
done:
GIT_REFCOUNT_INC(sm);
*out = sm;
return 0;
}
static int submodules_from_index(git_strmap *map, git_index *idx)
{
int error;
git_iterator *i;
const git_index_entry *entry;
if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0)
return error;
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(map, entry->path);
git_submodule *sm;
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
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_or_create(&sm, git_index_owner(idx), map, entry->path)) {
submodule_update_from_index_entry(sm, entry);
git_submodule_free(sm);
}
}
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
return error;
}
static int submodules_from_head(git_strmap *map, git_tree *head)
{
int error;
git_iterator *i;
const git_index_entry *entry;
if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0)
return error;
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(map, entry->path);
git_submodule *sm;
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
if (S_ISGITLINK(entry->mode))
submodule_update_from_head_data(sm, entry->mode, &entry->id);
else
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get_or_create(&sm, git_tree_owner(head), map, entry->path)) {
submodule_update_from_head_data(
sm, entry->mode, &entry->id);
git_submodule_free(sm);
}
}
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
return error;
}
/* If have_sm is true, sm is populated, otherwise map an repo are. */
typedef struct {
int have_sm;
git_submodule *sm;
git_strmap *map;
git_repository *repo;
} lfc_data;
static int all_submodules(git_repository *repo, git_strmap *map)
{
int error = 0;
git_index *idx = NULL;
git_tree *head = NULL;
const char *wd = NULL;
git_buf path = GIT_BUF_INIT;
git_submodule *sm;
git_config_backend *mods = NULL;
uint32_t mask;
assert(repo && map);
/* get sources that we will need to check */
if (git_repository_index(&idx, repo) < 0)
giterr_clear();
if (git_repository_head_tree(&head, repo) < 0)
giterr_clear();
wd = git_repository_workdir(repo);
if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
goto cleanup;
/* clear submodule flags that are to be refreshed */
mask = 0;
mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_FLAGS |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
mask |= GIT_SUBMODULE_STATUS_IN_CONFIG;
if (mask != 0)
mask |= GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS__WD_SCANNED |
GIT_SUBMODULE_STATUS__WD_FLAGS |
GIT_SUBMODULE_STATUS__WD_OID_VALID;
/* add back submodule information from index */
if (idx) {
if ((error = submodules_from_index(map, idx)) < 0)
goto cleanup;
}
/* add submodule information from HEAD */
if (head) {
if ((error = submodules_from_head(map, head)) < 0)
goto cleanup;
}
/* add submodule information from .gitmodules */
if (wd) {
lfc_data data = { 0 };
data.map = map;
data.repo = repo;
if ((mods = open_gitmodules(repo, false)) != NULL &&
(error = git_config_file_foreach(
mods, submodule_load_from_config, &data)) < 0)
goto cleanup;
}
/* shallow scan submodules in work tree as needed */
if (wd && mask != 0) {
git_strmap_foreach_value(map, sm, {
submodule_load_from_wd_lite(sm);
});
}
cleanup:
git_config_file_free(mods);
/* TODO: if we got an error, mark submodule config as invalid? */
git_index_free(idx);
git_tree_free(head);
git_buf_free(&path);
return error;
}
int git_submodule_foreach(
git_repository *repo,
int (*callback)(git_submodule *sm, const char *name, void *payload),
void *payload)
{
git_vector snapshot = GIT_VECTOR_INIT;
git_strmap *submodules;
git_submodule *sm;
int error;
size_t i;
git_submodule *sm;
git_submodule_cache *cache;
git_vector snapshot = GIT_VECTOR_INIT;
assert(repo && callback);
if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0)
if ((error = git_strmap_alloc(&submodules)) < 0)
return error;
cache = repo->_submodules;
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
return -1;
}
if ((error = all_submodules(repo, submodules)) < 0)
goto done;
if (!(error = git_vector_init(
&snapshot, kh_size(cache->submodules), submodule_cmp))) {
&snapshot, kh_size(submodules), submodule_cmp))) {
git_strmap_foreach_value(cache->submodules, sm, {
git_strmap_foreach_value(submodules, sm, {
if ((error = git_vector_insert(&snapshot, sm)) < 0)
break;
GIT_REFCOUNT_INC(sm);
});
}
git_mutex_unlock(&cache->lock);
if (error < 0)
goto done;
......@@ -293,17 +472,12 @@ done:
git_submodule_free(sm);
git_vector_free(&snapshot);
return error;
}
void git_submodule_cache_free(git_repository *repo)
{
git_submodule_cache *cache;
assert(repo);
git_strmap_foreach_value(submodules, sm, {
git_submodule_free(sm);
});
git_strmap_free(submodules);
if ((cache = git__swap(repo->_submodules, NULL)) != NULL)
submodule_cache_free(cache);
return error;
}
static int submodule_repo_init(
......@@ -394,7 +568,7 @@ int git_submodule_add_setup(
/* update .gitmodules */
if (!(mods = open_gitmodules(repo->_submodules, GITMODULES_CREATE))) {
if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported");
return -1;
......@@ -430,19 +604,10 @@ int git_submodule_add_setup(
goto cleanup;
}
/* add submodule to hash and "reload" it */
if (git_mutex_lock(&repo->_submodules->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
error = -1;
if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
goto cleanup;
}
if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) &&
!(error = git_submodule_reload(sm, false)))
error = git_submodule_init(sm, false);
git_mutex_unlock(&repo->_submodules->lock);
error = git_submodule_init(sm, false);
cleanup:
if (error && sm) {
......@@ -572,15 +737,6 @@ cleanup:
return error;
}
const char *git_submodule_ignore_to_str(git_submodule_ignore_t ignore)
{
int i;
for (i = 0; i < (int)ARRAY_SIZE(_sm_ignore_map); ++i)
if (_sm_ignore_map[i].map_value == ignore)
return _sm_ignore_map[i].str_match;
return NULL;
}
const char *git_submodule_update_to_str(git_submodule_update_t update)
{
int i;
......@@ -590,132 +746,90 @@ const char *git_submodule_update_to_str(git_submodule_update_t update)
return NULL;
}
const char *git_submodule_recurse_to_str(git_submodule_recurse_t recurse)
git_repository *git_submodule_owner(git_submodule *submodule)
{
int i;
for (i = 0; i < (int)ARRAY_SIZE(_sm_recurse_map); ++i)
if (_sm_recurse_map[i].map_value == recurse)
return _sm_recurse_map[i].str_match;
return NULL;
assert(submodule);
return submodule->repo;
}
int git_submodule_save(git_submodule *submodule)
const char *git_submodule_name(git_submodule *submodule)
{
int error = 0;
git_config_backend *mods;
git_buf key = GIT_BUF_INIT;
const char *val;
assert(submodule);
return submodule->name;
}
mods = open_gitmodules(submodule->repo->_submodules, GITMODULES_CREATE);
if (!mods) {
giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported");
return -1;
}
const char *git_submodule_path(git_submodule *submodule)
{
assert(submodule);
return submodule->path;
}
if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0)
goto cleanup;
const char *git_submodule_url(git_submodule *submodule)
{
assert(submodule);
return submodule->url;
}
/* save values for path, url, update, ignore, fetchRecurseSubmodules */
int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
{
int error = 0;
if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 ||
(error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0)
goto cleanup;
assert(out && repo && url);
if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 ||
(error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0)
goto cleanup;
git_buf_sanitize(out);
if ((error = submodule_config_key_trunc_puts(&key, "branch")) < 0)
goto cleanup;
if (submodule->branch == NULL)
error = git_config_file_delete(mods, key.ptr);
else
error = git_config_file_set_string(mods, key.ptr, submodule->branch);
if (error == GIT_ENOTFOUND) {
error = 0;
giterr_clear();
if (git_path_is_relative(url)) {
if (!(error = get_url_base(out, repo)))
error = git_path_apply_relative(out, url);
} else if (strchr(url, ':') != NULL || url[0] == '/') {
error = git_buf_sets(out, url);
} else {
giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
error = -1;
}
if (error < 0)
goto cleanup;
if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
(val = git_submodule_update_to_str(submodule->update)) != NULL)
error = git_config_file_set_string(mods, key.ptr, val);
if (error < 0)
goto cleanup;
if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
(val = git_submodule_ignore_to_str(submodule->ignore)) != NULL)
error = git_config_file_set_string(mods, key.ptr, val);
if (error < 0)
goto cleanup;
if (!(error = submodule_config_key_trunc_puts(&key, "fetchRecurseSubmodules")) &&
(val = git_submodule_recurse_to_str(submodule->fetch_recurse)) != NULL)
error = git_config_file_set_string(mods, key.ptr, val);
if (error < 0)
goto cleanup;
/* update internal defaults */
submodule->ignore_default = submodule->ignore;
submodule->update_default = submodule->update;
submodule->fetch_recurse_default = submodule->fetch_recurse;
submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
cleanup:
git_config_file_free(mods);
git_buf_free(&key);
return error;
}
git_repository *git_submodule_owner(git_submodule *submodule)
static int write_var(git_repository *repo, const char *name, const char *var, const char *val)
{
assert(submodule);
return submodule->repo;
}
git_buf key = GIT_BUF_INIT;
git_config_backend *mods;
int error;
const char *git_submodule_name(git_submodule *submodule)
{
assert(submodule);
return submodule->name;
}
mods = open_gitmodules(repo, GITMODULES_CREATE);
if (!mods)
return -1;
const char *git_submodule_path(git_submodule *submodule)
{
assert(submodule);
return submodule->path;
}
if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0)
goto cleanup;
if (val)
error = git_config_file_set_string(mods, key.ptr, val);
else
error = git_config_file_delete(mods, key.ptr);
git_buf_free(&key);
const char *git_submodule_url(git_submodule *submodule)
{
assert(submodule);
return submodule->url;
cleanup:
git_config_file_free(mods);
return error;
}
int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival)
{
int error = 0;
assert(out && repo && url);
git_buf_sanitize(out);
git_cvar_t type;
const char *val;
if (git_path_is_relative(url)) {
if (!(error = get_url_base(out, repo)))
error = git_path_apply_relative(out, url);
} else if (strchr(url, ':') != NULL || url[0] == '/') {
error = git_buf_sets(out, url);
} else {
giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
error = -1;
if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
return -1;
}
return error;
if (type == GIT_CVAR_TRUE)
val = "true";
return write_var(repo, name, var, val);
}
const char *git_submodule_branch(git_submodule *submodule)
......@@ -724,31 +838,19 @@ const char *git_submodule_branch(git_submodule *submodule)
return submodule->branch;
}
int git_submodule_set_branch(git_submodule *submodule, const char *branch)
int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
{
assert(submodule);
git__free(submodule->branch);
submodule->branch = NULL;
if (branch != NULL) {
submodule->branch = git__strdup(branch);
GITERR_CHECK_ALLOC(submodule->branch);
}
assert(repo && name);
return 0;
return write_var(repo, name, "branch", branch);
}
int git_submodule_set_url(git_submodule *submodule, const char *url)
int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
{
assert(submodule && url);
git__free(submodule->url);
submodule->url = git__strdup(url);
GITERR_CHECK_ALLOC(submodule->url);
assert(repo && name && url);
return 0;
return write_var(repo, name, "url", url);
}
const git_oid *git_submodule_index_id(git_submodule *submodule)
......@@ -799,19 +901,11 @@ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
}
git_submodule_ignore_t git_submodule_set_ignore(
git_submodule *submodule, git_submodule_ignore_t ignore)
int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
{
git_submodule_ignore_t old;
assert(submodule);
if (ignore == GIT_SUBMODULE_IGNORE_RESET)
ignore = submodule->ignore_default;
assert(repo && name);
old = submodule->ignore;
submodule->ignore = ignore;
return old;
return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
}
git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
......@@ -821,19 +915,11 @@ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
}
git_submodule_update_t git_submodule_set_update(
git_submodule *submodule, git_submodule_update_t update)
int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
{
git_submodule_update_t old;
assert(submodule);
if (update == GIT_SUBMODULE_UPDATE_RESET)
update = submodule->update_default;
assert(repo && name);
old = submodule->update;
submodule->update = update;
return old;
return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
}
git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
......@@ -843,20 +929,11 @@ git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
return submodule->fetch_recurse;
}
git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules(
git_submodule *submodule,
git_submodule_recurse_t fetch_recurse_submodules)
int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
{
git_submodule_recurse_t old;
assert(submodule);
if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET)
fetch_recurse_submodules = submodule->fetch_recurse_default;
assert(repo && name);
old = submodule->fetch_recurse;
submodule->fetch_recurse = fetch_recurse_submodules;
return old;
return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
}
static int submodule_repo_create(
......@@ -953,7 +1030,7 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
/* Get the status of the submodule to determine if it is already initialized */
if ((error = git_submodule_status(&submodule_status, sm)) < 0)
if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_FALLBACK)) < 0)
goto done;
/*
......@@ -1197,11 +1274,6 @@ 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, int force)
{
return submodule_cache_init(repo, force ? CACHE_FLUSH : CACHE_REFRESH);
}
static void submodule_update_from_index_entry(
git_submodule *sm, const git_index_entry *ie)
{
......@@ -1280,14 +1352,12 @@ int git_submodule_reload(git_submodule *sm, int force)
{
int error = 0;
git_config_backend *mods;
git_submodule_cache *cache;
lfc_data data = { 0 };
GIT_UNUSED(force);
assert(sm);
cache = sm->repo->_submodules;
/* refresh index data */
if ((error = submodule_update_index(sm)) < 0)
return error;
......@@ -1301,7 +1371,7 @@ int git_submodule_reload(git_submodule *sm, int force)
return error;
/* refresh config data */
mods = open_gitmodules(cache, GITMODULES_EXISTING);
mods = open_gitmodules(sm->repo, GITMODULES_EXISTING);
if (mods != NULL) {
git_buf path = GIT_BUF_INIT;
......@@ -1309,11 +1379,14 @@ int git_submodule_reload(git_submodule *sm, int force)
git_buf_text_puts_escape_regex(&path, sm->name);
git_buf_puts(&path, ".*");
if (git_buf_oom(&path))
if (git_buf_oom(&path)) {
error = -1;
else
} else {
data.have_sm = 1;
data.sm = sm;
error = git_config_file_foreach_match(
mods, path.ptr, submodule_load_from_config, cache);
mods, path.ptr, submodule_load_from_config, &data);
}
git_buf_free(&path);
git_config_file_free(mods);
......@@ -1352,7 +1425,7 @@ int git_submodule__status(
unsigned int status;
git_repository *smrepo = NULL;
if (ign < GIT_SUBMODULE_IGNORE_NONE)
if (ign == GIT_SUBMODULE_IGNORE_FALLBACK)
ign = sm->ignore;
/* only return location info if ignore == all */
......@@ -1401,11 +1474,20 @@ int git_submodule__status(
return 0;
}
int git_submodule_status(unsigned int *status, git_submodule *sm)
int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore)
{
assert(status && sm);
git_submodule *sm;
int error;
assert(status && repo && name);
if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
return error;
error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore);
git_submodule_free(sm);
return git_submodule__status(status, NULL, NULL, NULL, sm, 0);
return error;
}
int git_submodule_location(unsigned int *location, git_submodule *sm)
......@@ -1422,7 +1504,7 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
*/
static int submodule_alloc(
git_submodule **out, git_submodule_cache *cache, const char *name)
git_submodule **out, git_repository *repo, const char *name)
{
size_t namelen;
git_submodule *sm;
......@@ -1445,56 +1527,20 @@ static int submodule_alloc(
sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
sm->repo = cache->repo;
sm->repo = repo;
sm->branch = NULL;
*out = sm;
return 0;
}
static void submodule_cache_remove_item(
git_submodule_cache *cache,
git_submodule *item,
bool free_after_remove)
{
git_strmap *map;
const char *name, *alt;
if (!cache || !(map = cache->submodules) || !item)
return;
name = item->name;
alt = (item->path != item->name) ? item->path : NULL;
for (; name; name = alt, alt = NULL) {
khiter_t pos = git_strmap_lookup_index(map, name);
git_submodule *found;
if (!git_strmap_valid_index(map, pos))
continue;
found = git_strmap_value_at(map, pos);
if (found != item)
continue;
git_strmap_set_value_at(map, pos, NULL);
git_strmap_delete_at(map, pos);
if (free_after_remove)
git_submodule_free(found);
}
}
static void submodule_release(git_submodule *sm)
{
if (!sm)
return;
if (sm->repo) {
git_submodule_cache *cache = sm->repo->_submodules;
sm->repo = NULL;
submodule_cache_remove_item(cache, sm, false);
}
if (sm->path != sm->name)
......@@ -1513,54 +1559,6 @@ void git_submodule_free(git_submodule *sm)
GIT_REFCOUNT_DEC(sm, submodule_release);
}
static int submodule_get(
git_submodule **out,
git_submodule_cache *cache,
const char *name,
const char *alternate)
{
int error = 0;
khiter_t pos;
git_submodule *sm;
pos = git_strmap_lookup_index(cache->submodules, name);
if (!git_strmap_valid_index(cache->submodules, pos) && alternate)
pos = git_strmap_lookup_index(cache->submodules, alternate);
if (!git_strmap_valid_index(cache->submodules, pos)) {
if ((error = submodule_alloc(&sm, cache, name)) < 0)
return error;
/* insert value at name - if another thread beats us to it, then use
* their record and release our own.
*/
pos = kh_put(str, cache->submodules, sm->name, &error);
if (error < 0)
goto done;
else if (error == 0) {
git_submodule_free(sm);
sm = git_strmap_value_at(cache->submodules, pos);
} else {
error = 0;
git_strmap_set_value_at(cache->submodules, pos, sm);
}
} else {
sm = git_strmap_value_at(cache->submodules, pos);
}
done:
if (error < 0)
git_submodule_free(sm);
else if (out) {
GIT_REFCOUNT_INC(sm);
*out = sm;
}
return error;
}
static int submodule_config_error(const char *property, const char *value)
{
giterr_set(GITERR_INVALID,
......@@ -1613,12 +1611,12 @@ int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
static int submodule_load_from_config(
const git_config_entry *entry, void *payload)
{
git_submodule_cache *cache = payload;
const char *namestart, *property;
const char *key = entry->name, *value = entry->value, *path;
char *alternate = NULL, *replaced = NULL;
git_buf name = GIT_BUF_INIT;
git_submodule *sm = NULL;
lfc_data *data = payload;
git_submodule *sm;
int error = 0;
if (git__prefixcmp(key, "submodule.") != 0)
......@@ -1633,10 +1631,29 @@ static int submodule_load_from_config(
property++;
path = !strcasecmp(property, "path") ? value : NULL;
if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 ||
(error = submodule_get(&sm, cache, name.ptr, path)) < 0)
if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
goto done;
if (data->have_sm) {
sm = data->sm;
} else {
khiter_t pos;
git_strmap *map = data->map;
pos = git_strmap_lookup_index(map, name.ptr);
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
} else {
if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
goto done;
git_strmap_insert(map, sm->name, sm, error);
assert(error != 0);
if (error < 0)
goto done;
error = 0;
}
}
sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
/* Only from config might we get differing names & paths. If so, then
......@@ -1648,7 +1665,7 @@ static int submodule_load_from_config(
*/
if (strcmp(sm->name, name.ptr) != 0) { /* name changed */
if (!strcmp(sm->path, name.ptr)) { /* already set as path */
if (sm->path && !strcmp(sm->path, name.ptr)) { /* already set as path */
replaced = sm->name;
sm->name = sm->path;
} else {
......@@ -1674,7 +1691,6 @@ static int submodule_load_from_config(
/* Deregister under name being replaced */
if (replaced) {
git_strmap_delete(cache->submodules, replaced);
git_submodule_free(sm);
git__free(replaced);
}
......@@ -1682,7 +1698,6 @@ static int submodule_load_from_config(
/* Insert under alternate key */
if (alternate) {
void *old_sm = NULL;
git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error);
if (error < 0)
goto done;
......@@ -1742,7 +1757,6 @@ static int submodule_load_from_config(
/* ignore other unknown submodule properties */
done:
git_submodule_free(sm); /* offset refcount inc from submodule_get() */
git_buf_free(&name);
return error;
}
......@@ -1764,86 +1778,11 @@ static int submodule_load_from_wd_lite(git_submodule *sm)
return 0;
}
static int submodule_cache_refresh_from_index(
git_submodule_cache *cache, git_index *idx)
{
int error;
git_iterator *i;
const git_index_entry *entry;
if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0)
return error;
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path);
git_submodule *sm;
if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(cache->submodules, pos);
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, cache, entry->path, NULL)) {
submodule_update_from_index_entry(sm, entry);
git_submodule_free(sm);
}
}
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
return error;
}
static int submodule_cache_refresh_from_head(
git_submodule_cache *cache, git_tree *head)
{
int error;
git_iterator *i;
const git_index_entry *entry;
if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0)
return error;
while (!(error = git_iterator_advance(&entry, i))) {
khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path);
git_submodule *sm;
if (git_strmap_valid_index(cache->submodules, pos)) {
sm = git_strmap_value_at(cache->submodules, pos);
if (S_ISGITLINK(entry->mode))
submodule_update_from_head_data(sm, entry->mode, &entry->id);
else
sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
} else if (S_ISGITLINK(entry->mode)) {
if (!submodule_get(&sm, cache, entry->path, NULL)) {
submodule_update_from_head_data(
sm, entry->mode, &entry->id);
git_submodule_free(sm);
}
}
}
if (error == GIT_ITEROVER)
error = 0;
git_iterator_free(i);
return error;
}
static git_config_backend *open_gitmodules(
git_submodule_cache *cache,
git_repository *repo,
int okay_to_create)
{
const char *workdir = git_repository_workdir(cache->repo);
const char *workdir = git_repository_workdir(repo);
git_buf path = GIT_BUF_INIT;
git_config_backend *mods = NULL;
......@@ -1868,198 +1807,6 @@ static git_config_backend *open_gitmodules(
return mods;
}
static void submodule_cache_free(git_submodule_cache *cache)
{
git_submodule *sm;
if (!cache)
return;
git_strmap_foreach_value(cache->submodules, sm, {
sm->repo = NULL; /* disconnect from repo */
git_submodule_free(sm);
});
git_strmap_free(cache->submodules);
git_buf_free(&cache->gitmodules_path);
git_mutex_free(&cache->lock);
git__free(cache);
}
static int submodule_cache_alloc(
git_submodule_cache **out, git_repository *repo)
{
git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache));
GITERR_CHECK_ALLOC(cache);
if (git_mutex_init(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to initialize submodule cache lock");
git__free(cache);
return -1;
}
if (git_strmap_alloc(&cache->submodules) < 0) {
submodule_cache_free(cache);
return -1;
}
cache->repo = repo;
git_buf_init(&cache->gitmodules_path, 0);
*out = cache;
return 0;
}
static int submodule_cache_refresh(git_submodule_cache *cache, int refresh)
{
int error = 0, update_index, update_head, update_gitmod;
git_index *idx = NULL;
git_tree *head = NULL;
const char *wd = NULL;
git_buf path = GIT_BUF_INIT;
git_submodule *sm;
git_config_backend *mods = NULL;
uint32_t mask;
if (!cache || !cache->repo || !refresh)
return 0;
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache");
return -1;
}
/* get sources that we will need to check */
if (git_repository_index(&idx, cache->repo) < 0)
giterr_clear();
if (git_repository_head_tree(&head, cache->repo) < 0)
giterr_clear();
wd = git_repository_workdir(cache->repo);
if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
goto cleanup;
/* check for invalidation */
if (refresh == CACHE_FLUSH)
update_index = update_head = update_gitmod = true;
else {
update_index =
!idx || git_index__changed_relative_to(idx, &cache->index_checksum);
update_head =
!head || !git_oid_equal(&cache->head_id, git_tree_id(head));
update_gitmod = (wd != NULL) ?
git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) :
(cache->gitmodules_stamp.mtime != 0);
}
/* clear submodule flags that are to be refreshed */
mask = 0;
if (!idx || update_index)
mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_FLAGS |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
if (!head || update_head)
mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
if (update_gitmod)
mask |= GIT_SUBMODULE_STATUS_IN_CONFIG;
if (mask != 0)
mask |= GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS__WD_SCANNED |
GIT_SUBMODULE_STATUS__WD_FLAGS |
GIT_SUBMODULE_STATUS__WD_OID_VALID;
else
goto cleanup; /* nothing to do */
submodule_cache_clear_flags(cache, mask);
/* add back submodule information from index */
if (idx && update_index) {
if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0)
goto cleanup;
git_oid_cpy(&cache->index_checksum, git_index_checksum(idx));
}
/* add submodule information from HEAD */
if (head && update_head) {
if ((error = submodule_cache_refresh_from_head(cache, head)) < 0)
goto cleanup;
git_oid_cpy(&cache->head_id, git_tree_id(head));
}
/* add submodule information from .gitmodules */
if (wd && update_gitmod > 0) {
if ((mods = open_gitmodules(cache, false)) != NULL &&
(error = git_config_file_foreach(
mods, submodule_load_from_config, cache)) < 0)
goto cleanup;
}
/* shallow scan submodules in work tree as needed */
if (wd && mask != 0) {
git_strmap_foreach_value(cache->submodules, sm, {
submodule_load_from_wd_lite(sm);
});
}
/* remove submodules that no longer exist */
git_strmap_foreach_value(cache->submodules, sm, {
/* purge unless in HEAD, index, or .gitmodules; no sm for wd only */
if (sm != NULL &&
!(sm->flags &
(GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG)))
submodule_cache_remove_item(cache, sm, true);
});
cleanup:
git_config_file_free(mods);
/* TODO: if we got an error, mark submodule config as invalid? */
git_mutex_unlock(&cache->lock);
git_index_free(idx);
git_tree_free(head);
git_buf_free(&path);
return error;
}
static int submodule_cache_init(git_repository *repo, int cache_refresh)
{
int error = 0;
git_submodule_cache *cache = NULL;
/* if submodules already exist, just refresh as requested */
if (repo->_submodules)
return submodule_cache_refresh(repo->_submodules, cache_refresh);
/* otherwise create a new cache, load it, and atomically swap it in */
if (!(error = submodule_cache_alloc(&cache, repo)) &&
!(error = submodule_cache_refresh(cache, CACHE_FLUSH)))
cache = git__compare_and_swap(&repo->_submodules, NULL, cache);
/* might have raced with another thread to set cache, so free if needed */
if (cache)
submodule_cache_free(cache);
return error;
}
/* Lookup name of remote of the local tracking branch HEAD points to */
static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
{
......
......@@ -99,23 +99,6 @@ struct git_submodule {
git_oid wd_oid;
};
/**
* The git_submodule_cache stores known submodules along with timestamps,
* etc. about when they were loaded
*/
typedef struct {
git_repository *repo;
git_strmap *submodules;
git_mutex lock;
/* cache invalidation data */
git_oid head_id;
git_oid index_checksum;
git_buf gitmodules_path;
git_futils_filestamp gitmodules_stamp;
git_futils_filestamp config_stamp;
} git_submodule_cache;
/* Force revalidation of submodule data cache (alloc as needed) */
extern int git_submodule_cache_refresh(git_repository *repo);
......@@ -137,9 +120,6 @@ enum {
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
/* Internal submodule check does not attempt to refresh cached data */
extern bool git_submodule__is_submodule(git_repository *repo, const char *name);
/* Internal lookup does not attempt to refresh cached data */
extern int git_submodule__lookup(
git_submodule **out, git_repository *repo, const char *path);
......@@ -163,8 +143,4 @@ extern int git_submodule_parse_ignore(
extern int git_submodule_parse_update(
git_submodule_update_t *out, const char *value);
extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t);
extern const char *git_submodule_update_to_str(git_submodule_update_t);
extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t);
#endif
......@@ -273,13 +273,13 @@ void test_diff_submodules__invalid_cache(void)
/* create untracked file in submodule working directory */
cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello");
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
......@@ -301,7 +301,7 @@ void test_diff_submodules__invalid_cache(void)
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
......@@ -312,13 +312,13 @@ void test_diff_submodules__invalid_cache(void)
cl_git_pass(git_repository_index(&smindex, smrepo));
cl_git_pass(git_index_add_bypath(smindex, "file_to_modify"));
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_dirty);
git_diff_free(diff);
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
......@@ -327,19 +327,19 @@ void test_diff_submodules__invalid_cache(void)
/* commit changed index of submodule */
cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved);
git_diff_free(diff);
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_ALL);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_ALL);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_unchanged);
git_diff_free(diff);
git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
check_diff_patches(diff, expected_moved_dirty);
......
......@@ -89,7 +89,7 @@ void test_diff_tree__0(void)
}
#define DIFF_OPTS(FLAGS, CTXT) \
{GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_DEFAULT, \
{GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_FALLBACK, \
{NULL,0}, NULL, NULL, (CTXT), 1}
void test_diff_tree__options(void)
......
......@@ -23,10 +23,10 @@ void test_submodule_init__absolute_url(void)
cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* write the absolute url to the .gitmodules file*/
cl_git_pass(git_submodule_set_url(sm, absolute_url.ptr));
cl_git_pass(git_submodule_set_url(g_repo, "testrepo", absolute_url.ptr));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify that the .gitmodules is set with an absolute path*/
cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm));
......
......@@ -41,17 +41,15 @@ void test_submodule_modify__init(void)
git_config_free(cfg);
/* confirm no submodule data in config */
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
git_config_free(cfg);
/* call init and see that settings are copied */
cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
git_submodule_reload_all(g_repo, 1);
/* confirm submodule data in config */
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
......@@ -130,132 +128,85 @@ void test_submodule_modify__sync(void)
git_submodule_free(sm3);
}
void test_submodule_modify__edit_and_save(void)
void assert_ignore_change(git_submodule_ignore_t ignore)
{
git_submodule *sm1, *sm2;
char *old_url, *old_branch;
git_submodule_ignore_t old_ignore;
git_submodule_update_t old_update;
git_repository *r2;
git_submodule_recurse_t old_fetchrecurse;
cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head"));
old_url = git__strdup(git_submodule_url(sm1));
old_branch = NULL;
/* modify properties of submodule */
cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH));
old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
old_fetchrecurse = git_submodule_set_fetch_recurse_submodules(
sm1, GIT_SUBMODULE_RECURSE_YES);
cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1));
cl_assert_equal_i(
GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm1));
cl_assert_equal_i(
GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm1));
cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1));
/* revert without saving (and confirm setters return old value) */
cl_git_pass(git_submodule_set_url(sm1, old_url));
cl_git_pass(git_submodule_set_branch(sm1, old_branch));
cl_assert_equal_i(
GIT_SUBMODULE_IGNORE_UNTRACKED,
git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET));
cl_assert_equal_i(
GIT_SUBMODULE_UPDATE_REBASE,
git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET));
cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_YES, git_submodule_set_fetch_recurse_submodules(
sm1, GIT_SUBMODULE_RECURSE_RESET));
/* check that revert was successful */
cl_assert_equal_s(old_url, git_submodule_url(sm1));
cl_assert_equal_s(old_branch, git_submodule_branch(sm1));
cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1));
cl_assert_equal_i((int)old_update, (int)git_submodule_update_strategy(sm1));
cl_assert_equal_i(
old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1));
/* modify properties of submodule (again) */
cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
cl_git_pass(git_submodule_set_branch(sm1, SM_LIBGIT2_BRANCH));
git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_YES);
/* call save */
cl_git_pass(git_submodule_save(sm1));
/* attempt to "revert" values */
git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET);
git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET);
/* but ignore and update should NOT revert because the RESET
* should now be the newly saved value...
*/
cl_assert_equal_i(
(int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
cl_assert_equal_i(
(int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1));
cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1));
/* call reload and check that the new values are loaded */
cl_git_pass(git_submodule_reload(sm1, 0));
cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm1));
cl_assert_equal_i(
(int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
cl_assert_equal_i(
(int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1));
cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1));
/* unset branch again and verify that the property is deleted in config */
cl_git_pass(git_submodule_set_branch(sm1, NULL));
cl_git_pass(git_submodule_save(sm1));
cl_git_pass(git_submodule_reload(sm1, 0));
cl_assert_equal_s(NULL, git_submodule_branch(sm1));
/* open a second copy of the repo and compare submodule */
cl_git_pass(git_repository_open(&r2, "submod2"));
cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head"));
cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2));
cl_assert_equal_i(
GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm2));
cl_assert_equal_i(
GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm2));
cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2));
/* set fetchRecurseSubmodules on-demand */
cl_git_pass(git_submodule_reload(sm1, 0));
git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_ONDEMAND);
cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1));
/* call save */
cl_git_pass(git_submodule_save(sm1));
cl_git_pass(git_submodule_reload(sm1, 0));
cl_assert_equal_i(
GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1));
git_submodule *sm;
git_submodule_free(sm1);
git_submodule_free(sm2);
git_repository_free(r2);
git__free(old_url);
cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", ignore));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(ignore, git_submodule_ignore(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_ignore(void)
{
assert_ignore_change(GIT_SUBMODULE_IGNORE_UNTRACKED);
assert_ignore_change(GIT_SUBMODULE_IGNORE_NONE);
assert_ignore_change(GIT_SUBMODULE_IGNORE_ALL);
}
void assert_update_change(git_submodule_update_t update)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", update));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(update, git_submodule_update_strategy(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_update(void)
{
assert_update_change(GIT_SUBMODULE_UPDATE_REBASE);
assert_update_change(GIT_SUBMODULE_UPDATE_NONE);
assert_update_change(GIT_SUBMODULE_UPDATE_CHECKOUT);
}
void assert_recurse_change(git_submodule_recurse_t recurse)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", recurse));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(recurse, git_submodule_fetch_recurse_submodules(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_fetch_recurse_submodules(void)
{
assert_recurse_change(GIT_SUBMODULE_RECURSE_YES);
assert_recurse_change(GIT_SUBMODULE_RECURSE_NO);
assert_recurse_change(GIT_SUBMODULE_RECURSE_ONDEMAND);
}
void test_submodule_modify__set_branch(void)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert(git_submodule_branch(sm) == NULL);
git_submodule_free(sm);
cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", SM_LIBGIT2_BRANCH));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", NULL));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert(git_submodule_branch(sm) == NULL);
git_submodule_free(sm);
}
void test_submodule_modify__save_last(void)
void test_submodule_modify__set_url(void)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
cl_git_pass(git_submodule_save(sm));
cl_git_pass(git_submodule_set_url(g_repo, "sm_changed_head", SM_LIBGIT2_URL));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm));
git_submodule_free(sm);
}
......@@ -21,19 +21,11 @@ void test_submodule_nosubs__lookup(void)
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
cl_git_pass(git_submodule_reload_all(repo, 0));
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
}
void test_submodule_nosubs__immediate_reload(void)
{
git_repository *repo = cl_git_sandbox_init("status");
cl_git_pass(git_submodule_reload_all(repo, 0));
}
static int fake_submod_cb(git_submodule *sm, const char *n, void *p)
{
GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p);
......@@ -57,41 +49,7 @@ void test_submodule_nosubs__add(void)
git_submodule_free(sm2);
cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL));
cl_git_pass(git_submodule_reload_all(repo, 0));
git_submodule_free(sm);
}
void test_submodule_nosubs__reload_add_reload(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_submodule *sm;
cl_git_pass(git_submodule_reload_all(repo, 0));
/* try one add with a reload (to make sure no errors happen) */
cl_git_pass(git_submodule_add_setup(&sm, repo,
"https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1));
cl_git_pass(git_submodule_reload_all(repo, 0));
cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
git_submodule_free(sm);
/* try one add without a reload (to make sure cache inval works, too) */
cl_git_pass(git_submodule_add_setup(&sm, repo,
"https://github.com/libgit2/libgit2.git", "libgit2-again", 1));
cl_assert_equal_s("libgit2-again", git_submodule_name(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2-again"));
cl_assert_equal_s("libgit2-again", git_submodule_name(sm));
git_submodule_free(sm);
}
......@@ -100,10 +58,8 @@ void test_submodule_nosubs__bad_gitmodules(void)
git_repository *repo = cl_git_sandbox_init("status");
cl_git_mkfile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=flooble\n\n");
cl_git_fail(git_submodule_reload_all(repo, 0));
cl_git_rewritefile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=rebase\n\n");
cl_git_pass(git_submodule_reload_all(repo, 0));
cl_git_pass(git_submodule_lookup(NULL, repo, "foobar"));
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir"));
......
......@@ -107,56 +107,47 @@ void test_submodule_status__ignore_none(void)
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
}
static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
{
git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload;
GIT_UNUSED(name);
git_submodule_set_ignore(sm, ignore);
return 0;
}
void test_submodule_status__ignore_untracked(void)
{
unsigned int status;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
rm_submodule("sm_unchanged");
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
}
......@@ -166,42 +157,41 @@ void test_submodule_status__ignore_dirty(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
rm_submodule("sm_unchanged");
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
}
......@@ -211,42 +201,41 @@ void test_submodule_status__ignore_all(void)
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
rm_submodule("sm_unchanged");
cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
status = get_submodule_status(g_repo, "sm_changed_index");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_changed_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_missing_commits");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* removed sm_unchanged for deleted workdir */
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
status = get_submodule_status(g_repo, "sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
}
......
......@@ -156,21 +156,18 @@ void refute__submodule_exists(
git_repository *repo, const char *name, int expected_error,
const char *msg, const char *file, int line)
{
git_submodule *sm;
clar__assert_equal(
file, line, msg, 1, "%i",
expected_error, (int)(git_submodule_lookup(&sm, repo, name)));
expected_error, (int)(git_submodule_lookup(NULL, repo, name)));
}
unsigned int get_submodule_status(git_repository *repo, const char *name)
{
git_submodule *sm = NULL;
unsigned int status = 0;
cl_git_pass(git_submodule_lookup(&sm, repo, name));
cl_assert(sm);
cl_git_pass(git_submodule_status(&status, sm));
git_submodule_free(sm);
assert(repo && name);
cl_git_pass(git_submodule_status(&status, repo, name, GIT_SUBMODULE_IGNORE_FALLBACK));
return status;
}
......
......@@ -103,7 +103,7 @@ void test_submodule_update__update_submodule(void)
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify the initial state of the submodule */
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -114,7 +114,7 @@ void test_submodule_update__update_submodule(void)
cl_git_pass(git_submodule_update(sm, 0, &update_options));
/* verify state */
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -142,7 +142,7 @@ void test_submodule_update__update_and_init_submodule(void)
/* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -177,7 +177,7 @@ void test_submodule_update__update_already_checked_out_submodule(void)
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -203,7 +203,11 @@ void test_submodule_update__update_already_checked_out_submodule(void)
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -251,7 +255,7 @@ void test_submodule_update__update_blocks_on_dirty_wd(void)
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -277,7 +281,11 @@ void test_submodule_update__update_blocks_on_dirty_wd(void)
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -324,7 +332,7 @@ void test_submodule_update__can_force_update(void)
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......@@ -349,7 +357,11 @@ void test_submodule_update__can_force_update(void)
* Verify state after checkout of parent repository. The submodule ID in the
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, sm));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_FALLBACK));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
......
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