Merge branch 'hv/submodule-config'
The gitmodules API accessed from the C code learned to cache stuff lazily. * hv/submodule-config: submodule: allow erroneous values for the fetchRecurseSubmodules option submodule: use new config API for worktree configurations submodule: extract functions for config set and lookup submodule: implement a config API for lookup of .gitmodules values
This commit is contained in:
commit
5a4f07b322
1
.gitignore
vendored
1
.gitignore
vendored
@ -205,6 +205,7 @@
|
|||||||
/test-sha1-array
|
/test-sha1-array
|
||||||
/test-sigchain
|
/test-sigchain
|
||||||
/test-string-list
|
/test-string-list
|
||||||
|
/test-submodule-config
|
||||||
/test-subprocess
|
/test-subprocess
|
||||||
/test-svn-fe
|
/test-svn-fe
|
||||||
/test-urlmatch-normalization
|
/test-urlmatch-normalization
|
||||||
|
62
Documentation/technical/api-submodule-config.txt
Normal file
62
Documentation/technical/api-submodule-config.txt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
submodule config cache API
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The submodule config cache API allows to read submodule
|
||||||
|
configurations/information from specified revisions. Internally
|
||||||
|
information is lazily read into a cache that is used to avoid
|
||||||
|
unnecessary parsing of the same .gitmodule files. Lookups can be done by
|
||||||
|
submodule path or name.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To initialize the cache with configurations from the worktree the caller
|
||||||
|
typically first calls `gitmodules_config()` to read values from the
|
||||||
|
worktree .gitmodules and then to overlay the local git config values
|
||||||
|
`parse_submodule_config_option()` from the config parsing
|
||||||
|
infrastructure.
|
||||||
|
|
||||||
|
The caller can look up information about submodules by using the
|
||||||
|
`submodule_from_path()` or `submodule_from_name()` functions. They return
|
||||||
|
a `struct submodule` which contains the values. The API automatically
|
||||||
|
initializes and allocates the needed infrastructure on-demand. If the
|
||||||
|
caller does only want to lookup values from revisions the initialization
|
||||||
|
can be skipped.
|
||||||
|
|
||||||
|
If the internal cache might grow too big or when the caller is done with
|
||||||
|
the API, all internally cached values can be freed with submodule_free().
|
||||||
|
|
||||||
|
Data Structures
|
||||||
|
---------------
|
||||||
|
|
||||||
|
`struct submodule`::
|
||||||
|
|
||||||
|
This structure is used to return the information about one
|
||||||
|
submodule for a certain revision. It is returned by the lookup
|
||||||
|
functions.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
---------
|
||||||
|
|
||||||
|
`void submodule_free()`::
|
||||||
|
|
||||||
|
Use these to free the internally cached values.
|
||||||
|
|
||||||
|
`int parse_submodule_config_option(const char *var, const char *value)`::
|
||||||
|
|
||||||
|
Can be passed to the config parsing infrastructure to parse
|
||||||
|
local (worktree) submodule configurations.
|
||||||
|
|
||||||
|
`const struct submodule *submodule_from_path(const unsigned char *commit_sha1, const char *path)`::
|
||||||
|
|
||||||
|
Lookup values for one submodule by its commit_sha1 and path.
|
||||||
|
|
||||||
|
`const struct submodule *submodule_from_name(const unsigned char *commit_sha1, const char *name)`::
|
||||||
|
|
||||||
|
The same as above but lookup by name.
|
||||||
|
|
||||||
|
If given the null_sha1 as commit_sha1 the local configuration of a
|
||||||
|
submodule will be returned (e.g. consolidated values from local git
|
||||||
|
configuration and the .gitmodules file in the worktree).
|
||||||
|
|
||||||
|
For an example usage see test-submodule-config.c.
|
2
Makefile
2
Makefile
@ -593,6 +593,7 @@ TEST_PROGRAMS_NEED_X += test-sha1
|
|||||||
TEST_PROGRAMS_NEED_X += test-sha1-array
|
TEST_PROGRAMS_NEED_X += test-sha1-array
|
||||||
TEST_PROGRAMS_NEED_X += test-sigchain
|
TEST_PROGRAMS_NEED_X += test-sigchain
|
||||||
TEST_PROGRAMS_NEED_X += test-string-list
|
TEST_PROGRAMS_NEED_X += test-string-list
|
||||||
|
TEST_PROGRAMS_NEED_X += test-submodule-config
|
||||||
TEST_PROGRAMS_NEED_X += test-subprocess
|
TEST_PROGRAMS_NEED_X += test-subprocess
|
||||||
TEST_PROGRAMS_NEED_X += test-svn-fe
|
TEST_PROGRAMS_NEED_X += test-svn-fe
|
||||||
TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
|
TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
|
||||||
@ -784,6 +785,7 @@ LIB_OBJS += strbuf.o
|
|||||||
LIB_OBJS += streaming.o
|
LIB_OBJS += streaming.o
|
||||||
LIB_OBJS += string-list.o
|
LIB_OBJS += string-list.o
|
||||||
LIB_OBJS += submodule.o
|
LIB_OBJS += submodule.o
|
||||||
|
LIB_OBJS += submodule-config.o
|
||||||
LIB_OBJS += symlinks.o
|
LIB_OBJS += symlinks.o
|
||||||
LIB_OBJS += tag.o
|
LIB_OBJS += tag.o
|
||||||
LIB_OBJS += tempfile.o
|
LIB_OBJS += tempfile.o
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "xdiff-interface.h"
|
#include "xdiff-interface.h"
|
||||||
#include "ll-merge.h"
|
#include "ll-merge.h"
|
||||||
#include "resolve-undo.h"
|
#include "resolve-undo.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
#include "submodule.h"
|
#include "submodule.h"
|
||||||
|
|
||||||
static const char * const checkout_usage[] = {
|
static const char * const checkout_usage[] = {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
#include "sigchain.h"
|
#include "sigchain.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
#include "submodule.h"
|
#include "submodule.h"
|
||||||
#include "connected.h"
|
#include "connected.h"
|
||||||
#include "argv-array.h"
|
#include "argv-array.h"
|
||||||
|
1
diff.c
1
diff.c
@ -14,6 +14,7 @@
|
|||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
#include "userdiff.h"
|
#include "userdiff.h"
|
||||||
#include "sigchain.h"
|
#include "sigchain.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
#include "submodule.h"
|
#include "submodule.h"
|
||||||
#include "ll-merge.h"
|
#include "ll-merge.h"
|
||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
|
482
submodule-config.c
Normal file
482
submodule-config.c
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
|
#include "submodule.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* submodule cache lookup structure
|
||||||
|
* There is one shared set of 'struct submodule' entries which can be
|
||||||
|
* looked up by their sha1 blob id of the .gitmodule file and either
|
||||||
|
* using path or name as key.
|
||||||
|
* for_path stores submodule entries with path as key
|
||||||
|
* for_name stores submodule entries with name as key
|
||||||
|
*/
|
||||||
|
struct submodule_cache {
|
||||||
|
struct hashmap for_path;
|
||||||
|
struct hashmap for_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* thin wrapper struct needed to insert 'struct submodule' entries to
|
||||||
|
* the hashmap
|
||||||
|
*/
|
||||||
|
struct submodule_entry {
|
||||||
|
struct hashmap_entry ent;
|
||||||
|
struct submodule *config;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum lookup_type {
|
||||||
|
lookup_name,
|
||||||
|
lookup_path
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct submodule_cache cache;
|
||||||
|
static int is_cache_init;
|
||||||
|
|
||||||
|
static int config_path_cmp(const struct submodule_entry *a,
|
||||||
|
const struct submodule_entry *b,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
return strcmp(a->config->path, b->config->path) ||
|
||||||
|
hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_name_cmp(const struct submodule_entry *a,
|
||||||
|
const struct submodule_entry *b,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
return strcmp(a->config->name, b->config->name) ||
|
||||||
|
hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_init(struct submodule_cache *cache)
|
||||||
|
{
|
||||||
|
hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0);
|
||||||
|
hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_one_config(struct submodule_entry *entry)
|
||||||
|
{
|
||||||
|
free((void *) entry->config->path);
|
||||||
|
free((void *) entry->config->name);
|
||||||
|
free(entry->config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_free(struct submodule_cache *cache)
|
||||||
|
{
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct submodule_entry *entry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We iterate over the name hash here to be symmetric with the
|
||||||
|
* allocation of struct submodule entries. Each is allocated by
|
||||||
|
* their .gitmodule blob sha1 and submodule name.
|
||||||
|
*/
|
||||||
|
hashmap_iter_init(&cache->for_name, &iter);
|
||||||
|
while ((entry = hashmap_iter_next(&iter)))
|
||||||
|
free_one_config(entry);
|
||||||
|
|
||||||
|
hashmap_free(&cache->for_path, 1);
|
||||||
|
hashmap_free(&cache->for_name, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int hash_sha1_string(const unsigned char *sha1,
|
||||||
|
const char *string)
|
||||||
|
{
|
||||||
|
return memhash(sha1, 20) + strhash(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_put_path(struct submodule_cache *cache,
|
||||||
|
struct submodule *submodule)
|
||||||
|
{
|
||||||
|
unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
|
||||||
|
submodule->path);
|
||||||
|
struct submodule_entry *e = xmalloc(sizeof(*e));
|
||||||
|
hashmap_entry_init(e, hash);
|
||||||
|
e->config = submodule;
|
||||||
|
hashmap_put(&cache->for_path, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_remove_path(struct submodule_cache *cache,
|
||||||
|
struct submodule *submodule)
|
||||||
|
{
|
||||||
|
unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
|
||||||
|
submodule->path);
|
||||||
|
struct submodule_entry e;
|
||||||
|
struct submodule_entry *removed;
|
||||||
|
hashmap_entry_init(&e, hash);
|
||||||
|
e.config = submodule;
|
||||||
|
removed = hashmap_remove(&cache->for_path, &e, NULL);
|
||||||
|
free(removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_add(struct submodule_cache *cache,
|
||||||
|
struct submodule *submodule)
|
||||||
|
{
|
||||||
|
unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
|
||||||
|
submodule->name);
|
||||||
|
struct submodule_entry *e = xmalloc(sizeof(*e));
|
||||||
|
hashmap_entry_init(e, hash);
|
||||||
|
e->config = submodule;
|
||||||
|
hashmap_add(&cache->for_name, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct submodule *cache_lookup_path(struct submodule_cache *cache,
|
||||||
|
const unsigned char *gitmodules_sha1, const char *path)
|
||||||
|
{
|
||||||
|
struct submodule_entry *entry;
|
||||||
|
unsigned int hash = hash_sha1_string(gitmodules_sha1, path);
|
||||||
|
struct submodule_entry key;
|
||||||
|
struct submodule key_config;
|
||||||
|
|
||||||
|
hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
|
||||||
|
key_config.path = path;
|
||||||
|
|
||||||
|
hashmap_entry_init(&key, hash);
|
||||||
|
key.config = &key_config;
|
||||||
|
|
||||||
|
entry = hashmap_get(&cache->for_path, &key, NULL);
|
||||||
|
if (entry)
|
||||||
|
return entry->config;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct submodule *cache_lookup_name(struct submodule_cache *cache,
|
||||||
|
const unsigned char *gitmodules_sha1, const char *name)
|
||||||
|
{
|
||||||
|
struct submodule_entry *entry;
|
||||||
|
unsigned int hash = hash_sha1_string(gitmodules_sha1, name);
|
||||||
|
struct submodule_entry key;
|
||||||
|
struct submodule key_config;
|
||||||
|
|
||||||
|
hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
|
||||||
|
key_config.name = name;
|
||||||
|
|
||||||
|
hashmap_entry_init(&key, hash);
|
||||||
|
key.config = &key_config;
|
||||||
|
|
||||||
|
entry = hashmap_get(&cache->for_name, &key, NULL);
|
||||||
|
if (entry)
|
||||||
|
return entry->config;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int name_and_item_from_var(const char *var, struct strbuf *name,
|
||||||
|
struct strbuf *item)
|
||||||
|
{
|
||||||
|
const char *subsection, *key;
|
||||||
|
int subsection_len, parse;
|
||||||
|
parse = parse_config_key(var, "submodule", &subsection,
|
||||||
|
&subsection_len, &key);
|
||||||
|
if (parse < 0 || !subsection)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
strbuf_add(name, subsection, subsection_len);
|
||||||
|
strbuf_addstr(item, key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
|
||||||
|
const unsigned char *gitmodules_sha1, const char *name)
|
||||||
|
{
|
||||||
|
struct submodule *submodule;
|
||||||
|
struct strbuf name_buf = STRBUF_INIT;
|
||||||
|
|
||||||
|
submodule = cache_lookup_name(cache, gitmodules_sha1, name);
|
||||||
|
if (submodule)
|
||||||
|
return submodule;
|
||||||
|
|
||||||
|
submodule = xmalloc(sizeof(*submodule));
|
||||||
|
|
||||||
|
strbuf_addstr(&name_buf, name);
|
||||||
|
submodule->name = strbuf_detach(&name_buf, NULL);
|
||||||
|
|
||||||
|
submodule->path = NULL;
|
||||||
|
submodule->url = NULL;
|
||||||
|
submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
|
||||||
|
submodule->ignore = NULL;
|
||||||
|
|
||||||
|
hashcpy(submodule->gitmodules_sha1, gitmodules_sha1);
|
||||||
|
|
||||||
|
cache_add(cache, submodule);
|
||||||
|
|
||||||
|
return submodule;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_fetch_recurse(const char *opt, const char *arg,
|
||||||
|
int die_on_error)
|
||||||
|
{
|
||||||
|
switch (git_config_maybe_bool(opt, arg)) {
|
||||||
|
case 1:
|
||||||
|
return RECURSE_SUBMODULES_ON;
|
||||||
|
case 0:
|
||||||
|
return RECURSE_SUBMODULES_OFF;
|
||||||
|
default:
|
||||||
|
if (!strcmp(arg, "on-demand"))
|
||||||
|
return RECURSE_SUBMODULES_ON_DEMAND;
|
||||||
|
|
||||||
|
if (die_on_error)
|
||||||
|
die("bad %s argument: %s", opt, arg);
|
||||||
|
else
|
||||||
|
return RECURSE_SUBMODULES_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
|
||||||
|
{
|
||||||
|
return parse_fetch_recurse(opt, arg, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warn_multiple_config(const unsigned char *commit_sha1,
|
||||||
|
const char *name, const char *option)
|
||||||
|
{
|
||||||
|
const char *commit_string = "WORKTREE";
|
||||||
|
if (commit_sha1)
|
||||||
|
commit_string = sha1_to_hex(commit_sha1);
|
||||||
|
warning("%s:.gitmodules, multiple configurations found for "
|
||||||
|
"'submodule.%s.%s'. Skipping second one!",
|
||||||
|
commit_string, name, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct parse_config_parameter {
|
||||||
|
struct submodule_cache *cache;
|
||||||
|
const unsigned char *commit_sha1;
|
||||||
|
const unsigned char *gitmodules_sha1;
|
||||||
|
int overwrite;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_config(const char *var, const char *value, void *data)
|
||||||
|
{
|
||||||
|
struct parse_config_parameter *me = data;
|
||||||
|
struct submodule *submodule;
|
||||||
|
struct strbuf name = STRBUF_INIT, item = STRBUF_INIT;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* this also ensures that we only parse submodule entries */
|
||||||
|
if (!name_and_item_from_var(var, &name, &item))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
submodule = lookup_or_create_by_name(me->cache, me->gitmodules_sha1,
|
||||||
|
name.buf);
|
||||||
|
|
||||||
|
if (!strcmp(item.buf, "path")) {
|
||||||
|
struct strbuf path = STRBUF_INIT;
|
||||||
|
if (!value) {
|
||||||
|
ret = config_error_nonbool(var);
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
if (!me->overwrite && submodule->path != NULL) {
|
||||||
|
warn_multiple_config(me->commit_sha1, submodule->name,
|
||||||
|
"path");
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submodule->path)
|
||||||
|
cache_remove_path(me->cache, submodule);
|
||||||
|
free((void *) submodule->path);
|
||||||
|
strbuf_addstr(&path, value);
|
||||||
|
submodule->path = strbuf_detach(&path, NULL);
|
||||||
|
cache_put_path(me->cache, submodule);
|
||||||
|
} else if (!strcmp(item.buf, "fetchrecursesubmodules")) {
|
||||||
|
/* when parsing worktree configurations we can die early */
|
||||||
|
int die_on_error = is_null_sha1(me->gitmodules_sha1);
|
||||||
|
if (!me->overwrite &&
|
||||||
|
submodule->fetch_recurse != RECURSE_SUBMODULES_NONE) {
|
||||||
|
warn_multiple_config(me->commit_sha1, submodule->name,
|
||||||
|
"fetchrecursesubmodules");
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submodule->fetch_recurse = parse_fetch_recurse(var, value,
|
||||||
|
die_on_error);
|
||||||
|
} else if (!strcmp(item.buf, "ignore")) {
|
||||||
|
struct strbuf ignore = STRBUF_INIT;
|
||||||
|
if (!me->overwrite && submodule->ignore != NULL) {
|
||||||
|
warn_multiple_config(me->commit_sha1, submodule->name,
|
||||||
|
"ignore");
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
ret = config_error_nonbool(var);
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
|
||||||
|
strcmp(value, "all") && strcmp(value, "none")) {
|
||||||
|
warning("Invalid parameter '%s' for config option "
|
||||||
|
"'submodule.%s.ignore'", value, var);
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free((void *) submodule->ignore);
|
||||||
|
strbuf_addstr(&ignore, value);
|
||||||
|
submodule->ignore = strbuf_detach(&ignore, NULL);
|
||||||
|
} else if (!strcmp(item.buf, "url")) {
|
||||||
|
struct strbuf url = STRBUF_INIT;
|
||||||
|
if (!value) {
|
||||||
|
ret = config_error_nonbool(var);
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
if (!me->overwrite && submodule->url != NULL) {
|
||||||
|
warn_multiple_config(me->commit_sha1, submodule->name,
|
||||||
|
"url");
|
||||||
|
goto release_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free((void *) submodule->url);
|
||||||
|
strbuf_addstr(&url, value);
|
||||||
|
submodule->url = strbuf_detach(&url, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
release_return:
|
||||||
|
strbuf_release(&name);
|
||||||
|
strbuf_release(&item);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
|
||||||
|
unsigned char *gitmodules_sha1)
|
||||||
|
{
|
||||||
|
struct strbuf rev = STRBUF_INIT;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (is_null_sha1(commit_sha1)) {
|
||||||
|
hashcpy(gitmodules_sha1, null_sha1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_addf(&rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
|
||||||
|
if (get_sha1(rev.buf, gitmodules_sha1) >= 0)
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
strbuf_release(&rev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This does a lookup of a submodule configuration by name or by path
|
||||||
|
* (key) with on-demand reading of the appropriate .gitmodules from
|
||||||
|
* revisions.
|
||||||
|
*/
|
||||||
|
static const struct submodule *config_from(struct submodule_cache *cache,
|
||||||
|
const unsigned char *commit_sha1, const char *key,
|
||||||
|
enum lookup_type lookup_type)
|
||||||
|
{
|
||||||
|
struct strbuf rev = STRBUF_INIT;
|
||||||
|
unsigned long config_size;
|
||||||
|
char *config;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
enum object_type type;
|
||||||
|
const struct submodule *submodule = NULL;
|
||||||
|
struct parse_config_parameter parameter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If any parameter except the cache is a NULL pointer just
|
||||||
|
* return the first submodule. Can be used to check whether
|
||||||
|
* there are any submodules parsed.
|
||||||
|
*/
|
||||||
|
if (!commit_sha1 || !key) {
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct submodule_entry *entry;
|
||||||
|
|
||||||
|
hashmap_iter_init(&cache->for_name, &iter);
|
||||||
|
entry = hashmap_iter_next(&iter);
|
||||||
|
if (!entry)
|
||||||
|
return NULL;
|
||||||
|
return entry->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gitmodule_sha1_from_commit(commit_sha1, sha1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
switch (lookup_type) {
|
||||||
|
case lookup_name:
|
||||||
|
submodule = cache_lookup_name(cache, sha1, key);
|
||||||
|
break;
|
||||||
|
case lookup_path:
|
||||||
|
submodule = cache_lookup_path(cache, sha1, key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (submodule)
|
||||||
|
return submodule;
|
||||||
|
|
||||||
|
config = read_sha1_file(sha1, &type, &config_size);
|
||||||
|
if (!config)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (type != OBJ_BLOB) {
|
||||||
|
free(config);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill the submodule config into the cache */
|
||||||
|
parameter.cache = cache;
|
||||||
|
parameter.commit_sha1 = commit_sha1;
|
||||||
|
parameter.gitmodules_sha1 = sha1;
|
||||||
|
parameter.overwrite = 0;
|
||||||
|
git_config_from_buf(parse_config, rev.buf, config, config_size,
|
||||||
|
¶meter);
|
||||||
|
free(config);
|
||||||
|
|
||||||
|
switch (lookup_type) {
|
||||||
|
case lookup_name:
|
||||||
|
return cache_lookup_name(cache, sha1, key);
|
||||||
|
case lookup_path:
|
||||||
|
return cache_lookup_path(cache, sha1, key);
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct submodule *config_from_path(struct submodule_cache *cache,
|
||||||
|
const unsigned char *commit_sha1, const char *path)
|
||||||
|
{
|
||||||
|
return config_from(cache, commit_sha1, path, lookup_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct submodule *config_from_name(struct submodule_cache *cache,
|
||||||
|
const unsigned char *commit_sha1, const char *name)
|
||||||
|
{
|
||||||
|
return config_from(cache, commit_sha1, name, lookup_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_cache_init(void)
|
||||||
|
{
|
||||||
|
if (is_cache_init)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cache_init(&cache);
|
||||||
|
is_cache_init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_submodule_config_option(const char *var, const char *value)
|
||||||
|
{
|
||||||
|
struct parse_config_parameter parameter;
|
||||||
|
parameter.cache = &cache;
|
||||||
|
parameter.commit_sha1 = NULL;
|
||||||
|
parameter.gitmodules_sha1 = null_sha1;
|
||||||
|
parameter.overwrite = 1;
|
||||||
|
|
||||||
|
ensure_cache_init();
|
||||||
|
return parse_config(var, value, ¶meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
ensure_cache_init();
|
||||||
|
return config_from_name(&cache, commit_sha1, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
|
||||||
|
const char *path)
|
||||||
|
{
|
||||||
|
ensure_cache_init();
|
||||||
|
return config_from_path(&cache, commit_sha1, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void submodule_free(void)
|
||||||
|
{
|
||||||
|
cache_free(&cache);
|
||||||
|
is_cache_init = 0;
|
||||||
|
}
|
29
submodule-config.h
Normal file
29
submodule-config.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef SUBMODULE_CONFIG_CACHE_H
|
||||||
|
#define SUBMODULE_CONFIG_CACHE_H
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Submodule entry containing the information about a certain submodule
|
||||||
|
* in a certain revision.
|
||||||
|
*/
|
||||||
|
struct submodule {
|
||||||
|
const char *path;
|
||||||
|
const char *name;
|
||||||
|
const char *url;
|
||||||
|
int fetch_recurse;
|
||||||
|
const char *ignore;
|
||||||
|
/* the sha1 blob id of the responsible .gitmodules file */
|
||||||
|
unsigned char gitmodules_sha1[20];
|
||||||
|
};
|
||||||
|
|
||||||
|
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
|
||||||
|
int parse_submodule_config_option(const char *var, const char *value);
|
||||||
|
const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
|
||||||
|
const char *name);
|
||||||
|
const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
|
||||||
|
const char *path);
|
||||||
|
void submodule_free(void);
|
||||||
|
|
||||||
|
#endif /* SUBMODULE_CONFIG_H */
|
122
submodule.c
122
submodule.c
@ -1,4 +1,5 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
#include "submodule.h"
|
#include "submodule.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
@ -12,9 +13,6 @@
|
|||||||
#include "argv-array.h"
|
#include "argv-array.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
|
|
||||||
static struct string_list config_name_for_path;
|
|
||||||
static struct string_list config_fetch_recurse_submodules_for_name;
|
|
||||||
static struct string_list config_ignore_for_name;
|
|
||||||
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
|
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
|
||||||
static struct string_list changed_submodule_paths;
|
static struct string_list changed_submodule_paths;
|
||||||
static int initialized_fetch_ref_tips;
|
static int initialized_fetch_ref_tips;
|
||||||
@ -41,7 +39,6 @@ static int gitmodules_is_unmerged;
|
|||||||
*/
|
*/
|
||||||
static int gitmodules_is_modified;
|
static int gitmodules_is_modified;
|
||||||
|
|
||||||
|
|
||||||
int is_staging_gitmodules_ok(void)
|
int is_staging_gitmodules_ok(void)
|
||||||
{
|
{
|
||||||
return !gitmodules_is_modified;
|
return !gitmodules_is_modified;
|
||||||
@ -55,7 +52,7 @@ int is_staging_gitmodules_ok(void)
|
|||||||
int update_path_in_gitmodules(const char *oldpath, const char *newpath)
|
int update_path_in_gitmodules(const char *oldpath, const char *newpath)
|
||||||
{
|
{
|
||||||
struct strbuf entry = STRBUF_INIT;
|
struct strbuf entry = STRBUF_INIT;
|
||||||
struct string_list_item *path_option;
|
const struct submodule *submodule;
|
||||||
|
|
||||||
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
||||||
return -1;
|
return -1;
|
||||||
@ -63,13 +60,13 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
|
|||||||
if (gitmodules_is_unmerged)
|
if (gitmodules_is_unmerged)
|
||||||
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
||||||
|
|
||||||
path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
|
submodule = submodule_from_path(null_sha1, oldpath);
|
||||||
if (!path_option) {
|
if (!submodule || !submodule->name) {
|
||||||
warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
|
warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
strbuf_addstr(&entry, "submodule.");
|
strbuf_addstr(&entry, "submodule.");
|
||||||
strbuf_addstr(&entry, path_option->util);
|
strbuf_addstr(&entry, submodule->name);
|
||||||
strbuf_addstr(&entry, ".path");
|
strbuf_addstr(&entry, ".path");
|
||||||
if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
|
if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
|
||||||
/* Maybe the user already did that, don't error out here */
|
/* Maybe the user already did that, don't error out here */
|
||||||
@ -89,7 +86,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
|
|||||||
int remove_path_from_gitmodules(const char *path)
|
int remove_path_from_gitmodules(const char *path)
|
||||||
{
|
{
|
||||||
struct strbuf sect = STRBUF_INIT;
|
struct strbuf sect = STRBUF_INIT;
|
||||||
struct string_list_item *path_option;
|
const struct submodule *submodule;
|
||||||
|
|
||||||
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
|
||||||
return -1;
|
return -1;
|
||||||
@ -97,13 +94,13 @@ int remove_path_from_gitmodules(const char *path)
|
|||||||
if (gitmodules_is_unmerged)
|
if (gitmodules_is_unmerged)
|
||||||
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
|
||||||
|
|
||||||
path_option = unsorted_string_list_lookup(&config_name_for_path, path);
|
submodule = submodule_from_path(null_sha1, path);
|
||||||
if (!path_option) {
|
if (!submodule || !submodule->name) {
|
||||||
warning(_("Could not find section in .gitmodules where path=%s"), path);
|
warning(_("Could not find section in .gitmodules where path=%s"), path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
strbuf_addstr(§, "submodule.");
|
strbuf_addstr(§, "submodule.");
|
||||||
strbuf_addstr(§, path_option->util);
|
strbuf_addstr(§, submodule->name);
|
||||||
if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
|
if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
|
||||||
/* Maybe the user already did that, don't error out here */
|
/* Maybe the user already did that, don't error out here */
|
||||||
warning(_("Could not remove .gitmodules entry for %s"), path);
|
warning(_("Could not remove .gitmodules entry for %s"), path);
|
||||||
@ -165,12 +162,10 @@ done:
|
|||||||
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
|
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
|
||||||
const char *path)
|
const char *path)
|
||||||
{
|
{
|
||||||
struct string_list_item *path_option, *ignore_option;
|
const struct submodule *submodule = submodule_from_path(null_sha1, path);
|
||||||
path_option = unsorted_string_list_lookup(&config_name_for_path, path);
|
if (submodule) {
|
||||||
if (path_option) {
|
if (submodule->ignore)
|
||||||
ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
|
handle_ignore_submodules_arg(diffopt, submodule->ignore);
|
||||||
if (ignore_option)
|
|
||||||
handle_ignore_submodules_arg(diffopt, ignore_option->util);
|
|
||||||
else if (gitmodules_is_unmerged)
|
else if (gitmodules_is_unmerged)
|
||||||
DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
|
DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
|
||||||
}
|
}
|
||||||
@ -219,58 +214,6 @@ void gitmodules_config(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_submodule_config_option(const char *var, const char *value)
|
|
||||||
{
|
|
||||||
struct string_list_item *config;
|
|
||||||
const char *name, *key;
|
|
||||||
int namelen;
|
|
||||||
|
|
||||||
if (parse_config_key(var, "submodule", &name, &namelen, &key) < 0 || !name)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!strcmp(key, "path")) {
|
|
||||||
if (!value)
|
|
||||||
return config_error_nonbool(var);
|
|
||||||
|
|
||||||
config = unsorted_string_list_lookup(&config_name_for_path, value);
|
|
||||||
if (config)
|
|
||||||
free(config->util);
|
|
||||||
else
|
|
||||||
config = string_list_append(&config_name_for_path, xstrdup(value));
|
|
||||||
config->util = xmemdupz(name, namelen);
|
|
||||||
} else if (!strcmp(key, "fetchrecursesubmodules")) {
|
|
||||||
char *name_cstr = xmemdupz(name, namelen);
|
|
||||||
config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name_cstr);
|
|
||||||
if (!config)
|
|
||||||
config = string_list_append(&config_fetch_recurse_submodules_for_name, name_cstr);
|
|
||||||
else
|
|
||||||
free(name_cstr);
|
|
||||||
config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
|
|
||||||
} else if (!strcmp(key, "ignore")) {
|
|
||||||
char *name_cstr;
|
|
||||||
|
|
||||||
if (!value)
|
|
||||||
return config_error_nonbool(var);
|
|
||||||
|
|
||||||
if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
|
|
||||||
strcmp(value, "all") && strcmp(value, "none")) {
|
|
||||||
warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_cstr = xmemdupz(name, namelen);
|
|
||||||
config = unsorted_string_list_lookup(&config_ignore_for_name, name_cstr);
|
|
||||||
if (config) {
|
|
||||||
free(config->util);
|
|
||||||
free(name_cstr);
|
|
||||||
} else
|
|
||||||
config = string_list_append(&config_ignore_for_name, name_cstr);
|
|
||||||
config->util = xstrdup(value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_ignore_submodules_arg(struct diff_options *diffopt,
|
void handle_ignore_submodules_arg(struct diff_options *diffopt,
|
||||||
const char *arg)
|
const char *arg)
|
||||||
{
|
{
|
||||||
@ -345,20 +288,6 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
|
|||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
|
|
||||||
{
|
|
||||||
switch (git_config_maybe_bool(opt, arg)) {
|
|
||||||
case 1:
|
|
||||||
return RECURSE_SUBMODULES_ON;
|
|
||||||
case 0:
|
|
||||||
return RECURSE_SUBMODULES_OFF;
|
|
||||||
default:
|
|
||||||
if (!strcmp(arg, "on-demand"))
|
|
||||||
return RECURSE_SUBMODULES_ON_DEMAND;
|
|
||||||
die("bad %s argument: %s", opt, arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_submodule_summary(FILE *f, const char *path,
|
void show_submodule_summary(FILE *f, const char *path,
|
||||||
const char *line_prefix,
|
const char *line_prefix,
|
||||||
unsigned char one[20], unsigned char two[20],
|
unsigned char one[20], unsigned char two[20],
|
||||||
@ -646,7 +575,7 @@ static void calculate_changed_submodule_paths(void)
|
|||||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||||
|
|
||||||
/* No need to check if there are no submodules configured */
|
/* No need to check if there are no submodules configured */
|
||||||
if (!config_name_for_path.nr)
|
if (!submodule_from_path(NULL, NULL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
init_revisions(&rev, NULL);
|
init_revisions(&rev, NULL);
|
||||||
@ -693,7 +622,6 @@ int fetch_populated_submodules(const struct argv_array *options,
|
|||||||
int i, result = 0;
|
int i, result = 0;
|
||||||
struct child_process cp = CHILD_PROCESS_INIT;
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||||
struct string_list_item *name_for_path;
|
|
||||||
const char *work_tree = get_git_work_tree();
|
const char *work_tree = get_git_work_tree();
|
||||||
if (!work_tree)
|
if (!work_tree)
|
||||||
goto out;
|
goto out;
|
||||||
@ -718,24 +646,26 @@ int fetch_populated_submodules(const struct argv_array *options,
|
|||||||
struct strbuf submodule_git_dir = STRBUF_INIT;
|
struct strbuf submodule_git_dir = STRBUF_INIT;
|
||||||
struct strbuf submodule_prefix = STRBUF_INIT;
|
struct strbuf submodule_prefix = STRBUF_INIT;
|
||||||
const struct cache_entry *ce = active_cache[i];
|
const struct cache_entry *ce = active_cache[i];
|
||||||
const char *git_dir, *name, *default_argv;
|
const char *git_dir, *default_argv;
|
||||||
|
const struct submodule *submodule;
|
||||||
|
|
||||||
if (!S_ISGITLINK(ce->ce_mode))
|
if (!S_ISGITLINK(ce->ce_mode))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
name = ce->name;
|
submodule = submodule_from_path(null_sha1, ce->name);
|
||||||
name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name);
|
if (!submodule)
|
||||||
if (name_for_path)
|
submodule = submodule_from_name(null_sha1, ce->name);
|
||||||
name = name_for_path->util;
|
|
||||||
|
|
||||||
default_argv = "yes";
|
default_argv = "yes";
|
||||||
if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
|
if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
|
||||||
struct string_list_item *fetch_recurse_submodules_option;
|
if (submodule &&
|
||||||
fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
|
submodule->fetch_recurse !=
|
||||||
if (fetch_recurse_submodules_option) {
|
RECURSE_SUBMODULES_NONE) {
|
||||||
if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF)
|
if (submodule->fetch_recurse ==
|
||||||
|
RECURSE_SUBMODULES_OFF)
|
||||||
continue;
|
continue;
|
||||||
if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) {
|
if (submodule->fetch_recurse ==
|
||||||
|
RECURSE_SUBMODULES_ON_DEMAND) {
|
||||||
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
|
if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
|
||||||
continue;
|
continue;
|
||||||
default_argv = "on-demand";
|
default_argv = "on-demand";
|
||||||
|
@ -5,6 +5,8 @@ struct diff_options;
|
|||||||
struct argv_array;
|
struct argv_array;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
RECURSE_SUBMODULES_ERROR = -3,
|
||||||
|
RECURSE_SUBMODULES_NONE = -2,
|
||||||
RECURSE_SUBMODULES_ON_DEMAND = -1,
|
RECURSE_SUBMODULES_ON_DEMAND = -1,
|
||||||
RECURSE_SUBMODULES_OFF = 0,
|
RECURSE_SUBMODULES_OFF = 0,
|
||||||
RECURSE_SUBMODULES_DEFAULT = 1,
|
RECURSE_SUBMODULES_DEFAULT = 1,
|
||||||
@ -19,9 +21,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
|
|||||||
const char *path);
|
const char *path);
|
||||||
int submodule_config(const char *var, const char *value, void *cb);
|
int submodule_config(const char *var, const char *value, void *cb);
|
||||||
void gitmodules_config(void);
|
void gitmodules_config(void);
|
||||||
int parse_submodule_config_option(const char *var, const char *value);
|
|
||||||
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
|
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
|
||||||
int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
|
|
||||||
void show_submodule_summary(FILE *f, const char *path,
|
void show_submodule_summary(FILE *f, const char *path,
|
||||||
const char *line_prefix,
|
const char *line_prefix,
|
||||||
unsigned char one[20], unsigned char two[20],
|
unsigned char one[20], unsigned char two[20],
|
||||||
|
153
t/t7411-submodule-config.sh
Executable file
153
t/t7411-submodule-config.sh
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 Heiko Voigt
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='Test submodules config cache infrastructure
|
||||||
|
|
||||||
|
This test verifies that parsing .gitmodules configurations directly
|
||||||
|
from the database and from the worktree works.
|
||||||
|
'
|
||||||
|
|
||||||
|
TEST_NO_CREATE_REPO=1
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'submodule config cache setup' '
|
||||||
|
mkdir submodule &&
|
||||||
|
(cd submodule &&
|
||||||
|
git init &&
|
||||||
|
echo a >a &&
|
||||||
|
git add . &&
|
||||||
|
git commit -ma
|
||||||
|
) &&
|
||||||
|
mkdir super &&
|
||||||
|
(cd super &&
|
||||||
|
git init &&
|
||||||
|
git submodule add ../submodule &&
|
||||||
|
git submodule add ../submodule a &&
|
||||||
|
git commit -m "add as submodule and as a" &&
|
||||||
|
git mv a b &&
|
||||||
|
git commit -m "move a to b"
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >super/expect <<EOF
|
||||||
|
Submodule name: 'a' for path 'a'
|
||||||
|
Submodule name: 'a' for path 'b'
|
||||||
|
Submodule name: 'submodule' for path 'submodule'
|
||||||
|
Submodule name: 'submodule' for path 'submodule'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'test parsing and lookup of submodule config by path' '
|
||||||
|
(cd super &&
|
||||||
|
test-submodule-config \
|
||||||
|
HEAD^ a \
|
||||||
|
HEAD b \
|
||||||
|
HEAD^ submodule \
|
||||||
|
HEAD submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test parsing and lookup of submodule config by name' '
|
||||||
|
(cd super &&
|
||||||
|
test-submodule-config --name \
|
||||||
|
HEAD^ a \
|
||||||
|
HEAD a \
|
||||||
|
HEAD^ submodule \
|
||||||
|
HEAD submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >super/expect_error <<EOF
|
||||||
|
Submodule name: 'a' for path 'b'
|
||||||
|
Submodule name: 'submodule' for path 'submodule'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'error in one submodule config lets continue' '
|
||||||
|
(cd super &&
|
||||||
|
cp .gitmodules .gitmodules.bak &&
|
||||||
|
echo " value = \"" >>.gitmodules &&
|
||||||
|
git add .gitmodules &&
|
||||||
|
mv .gitmodules.bak .gitmodules &&
|
||||||
|
git commit -m "add error" &&
|
||||||
|
test-submodule-config \
|
||||||
|
HEAD b \
|
||||||
|
HEAD submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect_error actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >super/expect_url <<EOF
|
||||||
|
Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
|
||||||
|
Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >super/expect_local_path <<EOF
|
||||||
|
Submodule name: 'a' for path 'c'
|
||||||
|
Submodule name: 'submodule' for path 'submodule'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'reading of local configuration' '
|
||||||
|
(cd super &&
|
||||||
|
old_a=$(git config submodule.a.url) &&
|
||||||
|
old_submodule=$(git config submodule.submodule.url) &&
|
||||||
|
git config submodule.a.url git@somewhere.else.net:a.git &&
|
||||||
|
git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
|
||||||
|
test-submodule-config --url \
|
||||||
|
"" b \
|
||||||
|
"" submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect_url actual &&
|
||||||
|
git config submodule.a.path c &&
|
||||||
|
test-submodule-config \
|
||||||
|
"" c \
|
||||||
|
"" submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect_local_path actual &&
|
||||||
|
git config submodule.a.url $old_a &&
|
||||||
|
git config submodule.submodule.url $old_submodule &&
|
||||||
|
git config --unset submodule.a.path c
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >super/expect_fetchrecurse_die.err <<EOF
|
||||||
|
fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'local error in fetchrecursesubmodule dies early' '
|
||||||
|
(cd super &&
|
||||||
|
git config submodule.submodule.fetchrecursesubmodules blabla &&
|
||||||
|
test_must_fail test-submodule-config \
|
||||||
|
"" b \
|
||||||
|
"" submodule \
|
||||||
|
>actual.out 2>actual.err &&
|
||||||
|
touch expect_fetchrecurse_die.out &&
|
||||||
|
test_cmp expect_fetchrecurse_die.out actual.out &&
|
||||||
|
test_cmp expect_fetchrecurse_die.err actual.err &&
|
||||||
|
git config --unset submodule.submodule.fetchrecursesubmodules
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'error in history in fetchrecursesubmodule lets continue' '
|
||||||
|
(cd super &&
|
||||||
|
git config -f .gitmodules \
|
||||||
|
submodule.submodule.fetchrecursesubmodules blabla &&
|
||||||
|
git add .gitmodules &&
|
||||||
|
git config --unset -f .gitmodules \
|
||||||
|
submodule.submodule.fetchrecursesubmodules &&
|
||||||
|
git commit -m "add error in fetchrecursesubmodules" &&
|
||||||
|
test-submodule-config \
|
||||||
|
HEAD b \
|
||||||
|
HEAD submodule \
|
||||||
|
>actual &&
|
||||||
|
test_cmp expect_error actual &&
|
||||||
|
git reset --hard HEAD^
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
76
test-submodule-config.c
Normal file
76
test-submodule-config.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "submodule-config.h"
|
||||||
|
#include "submodule.h"
|
||||||
|
|
||||||
|
static void die_usage(int argc, char **argv, const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", msg);
|
||||||
|
fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int git_test_config(const char *var, const char *value, void *cb)
|
||||||
|
{
|
||||||
|
return parse_submodule_config_option(var, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char **arg = argv;
|
||||||
|
int my_argc = argc;
|
||||||
|
int output_url = 0;
|
||||||
|
int lookup_name = 0;
|
||||||
|
|
||||||
|
arg++;
|
||||||
|
my_argc--;
|
||||||
|
while (starts_with(arg[0], "--")) {
|
||||||
|
if (!strcmp(arg[0], "--url"))
|
||||||
|
output_url = 1;
|
||||||
|
if (!strcmp(arg[0], "--name"))
|
||||||
|
lookup_name = 1;
|
||||||
|
arg++;
|
||||||
|
my_argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_argc % 2 != 0)
|
||||||
|
die_usage(argc, argv, "Wrong number of arguments.");
|
||||||
|
|
||||||
|
setup_git_directory();
|
||||||
|
gitmodules_config();
|
||||||
|
git_config(git_test_config, NULL);
|
||||||
|
|
||||||
|
while (*arg) {
|
||||||
|
unsigned char commit_sha1[20];
|
||||||
|
const struct submodule *submodule;
|
||||||
|
const char *commit;
|
||||||
|
const char *path_or_name;
|
||||||
|
|
||||||
|
commit = arg[0];
|
||||||
|
path_or_name = arg[1];
|
||||||
|
|
||||||
|
if (commit[0] == '\0')
|
||||||
|
hashcpy(commit_sha1, null_sha1);
|
||||||
|
else if (get_sha1(commit, commit_sha1) < 0)
|
||||||
|
die_usage(argc, argv, "Commit not found.");
|
||||||
|
|
||||||
|
if (lookup_name) {
|
||||||
|
submodule = submodule_from_name(commit_sha1, path_or_name);
|
||||||
|
} else
|
||||||
|
submodule = submodule_from_path(commit_sha1, path_or_name);
|
||||||
|
if (!submodule)
|
||||||
|
die_usage(argc, argv, "Submodule not found.");
|
||||||
|
|
||||||
|
if (output_url)
|
||||||
|
printf("Submodule url: '%s' for path '%s'\n",
|
||||||
|
submodule->url, submodule->path);
|
||||||
|
else
|
||||||
|
printf("Submodule name: '%s' for path '%s'\n",
|
||||||
|
submodule->name, submodule->path);
|
||||||
|
|
||||||
|
arg += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
submodule_free();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user