git
/
submodule-config.c
1038 строк · 27.2 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "dir.h"5#include "environment.h"6#include "gettext.h"7#include "hex.h"8#include "path.h"9#include "repository.h"10#include "config.h"11#include "submodule-config.h"12#include "submodule.h"13#include "strbuf.h"14#include "object-name.h"15#include "object-store-ll.h"16#include "parse-options.h"17#include "thread-utils.h"18#include "tree-walk.h"19#include "url.h"20#include "urlmatch.h"21
22/*
23* submodule cache lookup structure
24* There is one shared set of 'struct submodule' entries which can be
25* looked up by their sha1 blob id of the .gitmodules file and either
26* using path or name as key.
27* for_path stores submodule entries with path as key
28* for_name stores submodule entries with name as key
29*/
30struct submodule_cache {31struct hashmap for_path;32struct hashmap for_name;33unsigned initialized:1;34unsigned gitmodules_read:1;35};36
37/*
38* thin wrapper struct needed to insert 'struct submodule' entries to
39* the hashmap
40*/
41struct submodule_entry {42struct hashmap_entry ent;43struct submodule *config;44};45
46enum lookup_type {47lookup_name,48lookup_path
49};50
51static int config_path_cmp(const void *cmp_data UNUSED,52const struct hashmap_entry *eptr,53const struct hashmap_entry *entry_or_key,54const void *keydata UNUSED)55{
56const struct submodule_entry *a, *b;57
58a = container_of(eptr, const struct submodule_entry, ent);59b = container_of(entry_or_key, const struct submodule_entry, ent);60
61return strcmp(a->config->path, b->config->path) ||62!oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);63}
64
65static int config_name_cmp(const void *cmp_data UNUSED,66const struct hashmap_entry *eptr,67const struct hashmap_entry *entry_or_key,68const void *keydata UNUSED)69{
70const struct submodule_entry *a, *b;71
72a = container_of(eptr, const struct submodule_entry, ent);73b = container_of(entry_or_key, const struct submodule_entry, ent);74
75return strcmp(a->config->name, b->config->name) ||76!oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);77}
78
79static struct submodule_cache *submodule_cache_alloc(void)80{
81return xcalloc(1, sizeof(struct submodule_cache));82}
83
84static void submodule_cache_init(struct submodule_cache *cache)85{
86hashmap_init(&cache->for_path, config_path_cmp, NULL, 0);87hashmap_init(&cache->for_name, config_name_cmp, NULL, 0);88cache->initialized = 1;89}
90
91static void free_one_config(struct submodule_entry *entry)92{
93free((void *) entry->config->path);94free((void *) entry->config->name);95free((void *) entry->config->branch);96free((void *) entry->config->url);97free((void *) entry->config->ignore);98free((void *) entry->config->update_strategy.command);99free(entry->config);100}
101
102static void submodule_cache_clear(struct submodule_cache *cache)103{
104struct hashmap_iter iter;105struct submodule_entry *entry;106
107if (!cache->initialized)108return;109
110/*111* We iterate over the name hash here to be symmetric with the
112* allocation of struct submodule entries. Each is allocated by
113* their .gitmodules blob sha1 and submodule name.
114*/
115hashmap_for_each_entry(&cache->for_name, &iter, entry,116ent /* member name */)117free_one_config(entry);118
119hashmap_clear_and_free(&cache->for_path, struct submodule_entry, ent);120hashmap_clear_and_free(&cache->for_name, struct submodule_entry, ent);121cache->initialized = 0;122cache->gitmodules_read = 0;123}
124
125void submodule_cache_free(struct submodule_cache *cache)126{
127submodule_cache_clear(cache);128free(cache);129}
130
131static unsigned int hash_oid_string(const struct object_id *oid,132const char *string)133{
134return memhash(oid->hash, the_hash_algo->rawsz) + strhash(string);135}
136
137static void cache_put_path(struct submodule_cache *cache,138struct submodule *submodule)139{
140unsigned int hash = hash_oid_string(&submodule->gitmodules_oid,141submodule->path);142struct submodule_entry *e = xmalloc(sizeof(*e));143hashmap_entry_init(&e->ent, hash);144e->config = submodule;145hashmap_put(&cache->for_path, &e->ent);146}
147
148static void cache_remove_path(struct submodule_cache *cache,149struct submodule *submodule)150{
151unsigned int hash = hash_oid_string(&submodule->gitmodules_oid,152submodule->path);153struct submodule_entry e;154struct submodule_entry *removed;155hashmap_entry_init(&e.ent, hash);156e.config = submodule;157removed = hashmap_remove_entry(&cache->for_path, &e, ent, NULL);158free(removed);159}
160
161static void cache_add(struct submodule_cache *cache,162struct submodule *submodule)163{
164unsigned int hash = hash_oid_string(&submodule->gitmodules_oid,165submodule->name);166struct submodule_entry *e = xmalloc(sizeof(*e));167hashmap_entry_init(&e->ent, hash);168e->config = submodule;169hashmap_add(&cache->for_name, &e->ent);170}
171
172static const struct submodule *cache_lookup_path(struct submodule_cache *cache,173const struct object_id *gitmodules_oid, const char *path)174{
175struct submodule_entry *entry;176unsigned int hash = hash_oid_string(gitmodules_oid, path);177struct submodule_entry key;178struct submodule key_config;179
180oidcpy(&key_config.gitmodules_oid, gitmodules_oid);181key_config.path = path;182
183hashmap_entry_init(&key.ent, hash);184key.config = &key_config;185
186entry = hashmap_get_entry(&cache->for_path, &key, ent, NULL);187if (entry)188return entry->config;189return NULL;190}
191
192static struct submodule *cache_lookup_name(struct submodule_cache *cache,193const struct object_id *gitmodules_oid, const char *name)194{
195struct submodule_entry *entry;196unsigned int hash = hash_oid_string(gitmodules_oid, name);197struct submodule_entry key;198struct submodule key_config;199
200oidcpy(&key_config.gitmodules_oid, gitmodules_oid);201key_config.name = name;202
203hashmap_entry_init(&key.ent, hash);204key.config = &key_config;205
206entry = hashmap_get_entry(&cache->for_name, &key, ent, NULL);207if (entry)208return entry->config;209return NULL;210}
211
212int check_submodule_name(const char *name)213{
214/* Disallow empty names */215if (!*name)216return -1;217
218/*219* Look for '..' as a path component. Check is_xplatform_dir_sep() as
220* separators rather than is_dir_sep(), because we want the name rules
221* to be consistent across platforms.
222*/
223goto in_component; /* always start inside component */224while (*name) {225char c = *name++;226if (is_xplatform_dir_sep(c)) {227in_component:228if (name[0] == '.' && name[1] == '.' &&229(!name[2] || is_xplatform_dir_sep(name[2])))230return -1;231}232}233
234return 0;235}
236
237static int starts_with_dot_slash(const char *const path)238{
239return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |240PATH_MATCH_XPLATFORM);241}
242
243static int starts_with_dot_dot_slash(const char *const path)244{
245return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |246PATH_MATCH_XPLATFORM);247}
248
249static int submodule_url_is_relative(const char *url)250{
251return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);252}
253
254/*
255* Count directory components that a relative submodule URL should chop
256* from the remote_url it is to be resolved against.
257*
258* In other words, this counts "../" components at the start of a
259* submodule URL.
260*
261* Returns the number of directory components to chop and writes a
262* pointer to the next character of url after all leading "./" and
263* "../" components to out.
264*/
265static int count_leading_dotdots(const char *url, const char **out)266{
267int result = 0;268while (1) {269if (starts_with_dot_dot_slash(url)) {270result++;271url += strlen("../");272continue;273}274if (starts_with_dot_slash(url)) {275url += strlen("./");276continue;277}278*out = url;279return result;280}281}
282/*
283* Check whether a transport is implemented by git-remote-curl.
284*
285* If it is, returns 1 and writes the URL that would be passed to
286* git-remote-curl to the "out" parameter.
287*
288* Otherwise, returns 0 and leaves "out" untouched.
289*
290* Examples:
291* http::https://example.com/repo.git -> 1, https://example.com/repo.git
292* https://example.com/repo.git -> 1, https://example.com/repo.git
293* git://example.com/repo.git -> 0
294*
295* This is for use in checking for previously exploitable bugs that
296* required a submodule URL to be passed to git-remote-curl.
297*/
298static int url_to_curl_url(const char *url, const char **out)299{
300/*301* We don't need to check for case-aliases, "http.exe", and so
302* on because in the default configuration, is_transport_allowed
303* prevents URLs with those schemes from being cloned
304* automatically.
305*/
306if (skip_prefix(url, "http::", out) ||307skip_prefix(url, "https::", out) ||308skip_prefix(url, "ftp::", out) ||309skip_prefix(url, "ftps::", out))310return 1;311if (starts_with(url, "http://") ||312starts_with(url, "https://") ||313starts_with(url, "ftp://") ||314starts_with(url, "ftps://")) {315*out = url;316return 1;317}318return 0;319}
320
321int check_submodule_url(const char *url)322{
323const char *curl_url;324
325if (looks_like_command_line_option(url))326return -1;327
328if (submodule_url_is_relative(url) || starts_with(url, "git://")) {329char *decoded;330const char *next;331int has_nl;332
333/*334* This could be appended to an http URL and url-decoded;
335* check for malicious characters.
336*/
337decoded = url_decode(url);338has_nl = !!strchr(decoded, '\n');339
340free(decoded);341if (has_nl)342return -1;343
344/*345* URLs which escape their root via "../" can overwrite
346* the host field and previous components, resolving to
347* URLs like https::example.com/submodule.git and
348* https:///example.com/submodule.git that were
349* susceptible to CVE-2020-11008.
350*/
351if (count_leading_dotdots(url, &next) > 0 &&352(*next == ':' || *next == '/'))353return -1;354}355
356else if (url_to_curl_url(url, &curl_url)) {357int ret = 0;358char *normalized = url_normalize(curl_url, NULL);359if (normalized) {360char *decoded = url_decode(normalized);361if (strchr(decoded, '\n'))362ret = -1;363free(normalized);364free(decoded);365} else {366ret = -1;367}368
369return ret;370}371
372return 0;373}
374
375static int name_and_item_from_var(const char *var, struct strbuf *name,376struct strbuf *item)377{
378const char *subsection, *key;379size_t subsection_len;380int parse;381parse = parse_config_key(var, "submodule", &subsection,382&subsection_len, &key);383if (parse < 0 || !subsection)384return 0;385
386strbuf_add(name, subsection, subsection_len);387if (check_submodule_name(name->buf) < 0) {388warning(_("ignoring suspicious submodule name: %s"), name->buf);389strbuf_release(name);390return 0;391}392
393strbuf_addstr(item, key);394
395return 1;396}
397
398static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,399const struct object_id *gitmodules_oid, const char *name)400{
401struct submodule *submodule;402struct strbuf name_buf = STRBUF_INIT;403
404submodule = cache_lookup_name(cache, gitmodules_oid, name);405if (submodule)406return submodule;407
408submodule = xmalloc(sizeof(*submodule));409
410strbuf_addstr(&name_buf, name);411submodule->name = strbuf_detach(&name_buf, NULL);412
413submodule->path = NULL;414submodule->url = NULL;415submodule->update_strategy.type = SM_UPDATE_UNSPECIFIED;416submodule->update_strategy.command = NULL;417submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;418submodule->ignore = NULL;419submodule->branch = NULL;420submodule->recommend_shallow = -1;421
422oidcpy(&submodule->gitmodules_oid, gitmodules_oid);423
424cache_add(cache, submodule);425
426return submodule;427}
428
429static int parse_fetch_recurse(const char *opt, const char *arg,430int die_on_error)431{
432switch (git_parse_maybe_bool(arg)) {433case 1:434return RECURSE_SUBMODULES_ON;435case 0:436return RECURSE_SUBMODULES_OFF;437default:438if (!strcmp(arg, "on-demand"))439return RECURSE_SUBMODULES_ON_DEMAND;440/*441* Please update $__git_fetch_recurse_submodules in
442* git-completion.bash when you add new options.
443*/
444if (die_on_error)445die("bad %s argument: %s", opt, arg);446else447return RECURSE_SUBMODULES_ERROR;448}449}
450
451int parse_submodule_fetchjobs(const char *var, const char *value,452const struct key_value_info *kvi)453{
454int fetchjobs = git_config_int(var, value, kvi);455if (fetchjobs < 0)456die(_("negative values not allowed for submodule.fetchJobs"));457if (!fetchjobs)458fetchjobs = online_cpus();459return fetchjobs;460}
461
462int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)463{
464return parse_fetch_recurse(opt, arg, 1);465}
466
467int option_fetch_parse_recurse_submodules(const struct option *opt,468const char *arg, int unset)469{
470int *v;471
472if (!opt->value)473return -1;474
475v = opt->value;476
477if (unset) {478*v = RECURSE_SUBMODULES_OFF;479} else {480if (arg)481*v = parse_fetch_recurse_submodules_arg(opt->long_name, arg);482else483*v = RECURSE_SUBMODULES_ON;484}485return 0;486}
487
488static int parse_update_recurse(const char *opt, const char *arg,489int die_on_error)490{
491switch (git_parse_maybe_bool(arg)) {492case 1:493return RECURSE_SUBMODULES_ON;494case 0:495return RECURSE_SUBMODULES_OFF;496default:497if (die_on_error)498die("bad %s argument: %s", opt, arg);499return RECURSE_SUBMODULES_ERROR;500}501}
502
503int parse_update_recurse_submodules_arg(const char *opt, const char *arg)504{
505return parse_update_recurse(opt, arg, 1);506}
507
508static int parse_push_recurse(const char *opt, const char *arg,509int die_on_error)510{
511switch (git_parse_maybe_bool(arg)) {512case 1:513/* There's no simple "on" value when pushing */514if (die_on_error)515die("bad %s argument: %s", opt, arg);516else517return RECURSE_SUBMODULES_ERROR;518case 0:519return RECURSE_SUBMODULES_OFF;520default:521if (!strcmp(arg, "on-demand"))522return RECURSE_SUBMODULES_ON_DEMAND;523else if (!strcmp(arg, "check"))524return RECURSE_SUBMODULES_CHECK;525else if (!strcmp(arg, "only"))526return RECURSE_SUBMODULES_ONLY;527/*528* Please update $__git_push_recurse_submodules in
529* git-completion.bash when you add new modes.
530*/
531else if (die_on_error)532die("bad %s argument: %s", opt, arg);533else534return RECURSE_SUBMODULES_ERROR;535}536}
537
538int parse_push_recurse_submodules_arg(const char *opt, const char *arg)539{
540return parse_push_recurse(opt, arg, 1);541}
542
543static void warn_multiple_config(const struct object_id *treeish_name,544const char *name, const char *option)545{
546const char *commit_string = "WORKTREE";547if (treeish_name)548commit_string = oid_to_hex(treeish_name);549warning("%s:.gitmodules, multiple configurations found for "550"'submodule.%s.%s'. Skipping second one!",551commit_string, name, option);552}
553
554static void warn_command_line_option(const char *var, const char *value)555{
556warning(_("ignoring '%s' which may be interpreted as"557" a command-line option: %s"), var, value);558}
559
560struct parse_config_parameter {561struct submodule_cache *cache;562const struct object_id *treeish_name;563const struct object_id *gitmodules_oid;564int overwrite;565};566
567/*
568* Parse a config item from .gitmodules.
569*
570* This does not handle submodule-related configuration from the main
571* config store (.git/config, etc). Callers are responsible for
572* checking for overrides in the main config store when appropriate.
573*/
574static int parse_config(const char *var, const char *value,575const struct config_context *ctx UNUSED, void *data)576{
577struct parse_config_parameter *me = data;578struct submodule *submodule;579struct strbuf name = STRBUF_INIT, item = STRBUF_INIT;580int ret = 0;581
582/* this also ensures that we only parse submodule entries */583if (!name_and_item_from_var(var, &name, &item))584return 0;585
586submodule = lookup_or_create_by_name(me->cache,587me->gitmodules_oid,588name.buf);589
590if (!strcmp(item.buf, "path")) {591if (!value)592ret = config_error_nonbool(var);593else if (looks_like_command_line_option(value))594warn_command_line_option(var, value);595else if (!me->overwrite && submodule->path)596warn_multiple_config(me->treeish_name, submodule->name,597"path");598else {599if (submodule->path)600cache_remove_path(me->cache, submodule);601free((void *) submodule->path);602submodule->path = xstrdup(value);603cache_put_path(me->cache, submodule);604}605} else if (!strcmp(item.buf, "fetchrecursesubmodules")) {606/* when parsing worktree configurations we can die early */607int die_on_error = is_null_oid(me->gitmodules_oid);608if (!me->overwrite &&609submodule->fetch_recurse != RECURSE_SUBMODULES_NONE)610warn_multiple_config(me->treeish_name, submodule->name,611"fetchrecursesubmodules");612else613submodule->fetch_recurse = parse_fetch_recurse(614var, value,615die_on_error);616} else if (!strcmp(item.buf, "ignore")) {617if (!value)618ret = config_error_nonbool(var);619else if (!me->overwrite && submodule->ignore)620warn_multiple_config(me->treeish_name, submodule->name,621"ignore");622else if (strcmp(value, "untracked") &&623strcmp(value, "dirty") &&624strcmp(value, "all") &&625strcmp(value, "none"))626warning("Invalid parameter '%s' for config option "627"'submodule.%s.ignore'", value, name.buf);628else {629free((void *) submodule->ignore);630submodule->ignore = xstrdup(value);631}632} else if (!strcmp(item.buf, "url")) {633if (!value) {634ret = config_error_nonbool(var);635} else if (looks_like_command_line_option(value)) {636warn_command_line_option(var, value);637} else if (!me->overwrite && submodule->url) {638warn_multiple_config(me->treeish_name, submodule->name,639"url");640} else {641free((void *) submodule->url);642submodule->url = xstrdup(value);643}644} else if (!strcmp(item.buf, "update")) {645if (!value)646ret = config_error_nonbool(var);647else if (!me->overwrite &&648submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)649warn_multiple_config(me->treeish_name, submodule->name,650"update");651else if (parse_submodule_update_strategy(value,652&submodule->update_strategy) < 0 ||653submodule->update_strategy.type == SM_UPDATE_COMMAND)654die(_("invalid value for '%s'"), var);655} else if (!strcmp(item.buf, "shallow")) {656if (!me->overwrite && submodule->recommend_shallow != -1)657warn_multiple_config(me->treeish_name, submodule->name,658"shallow");659else660submodule->recommend_shallow =661git_config_bool(var, value);662} else if (!strcmp(item.buf, "branch")) {663if (!value)664ret = config_error_nonbool(var);665else if (!me->overwrite && submodule->branch)666warn_multiple_config(me->treeish_name, submodule->name,667"branch");668else {669free((void *)submodule->branch);670submodule->branch = xstrdup(value);671}672}673
674strbuf_release(&name);675strbuf_release(&item);676
677return ret;678}
679
680static int gitmodule_oid_from_commit(const struct object_id *treeish_name,681struct object_id *gitmodules_oid,682struct strbuf *rev)683{
684int ret = 0;685
686if (is_null_oid(treeish_name)) {687oidclr(gitmodules_oid, the_repository->hash_algo);688return 1;689}690
691strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));692if (repo_get_oid(the_repository, rev->buf, gitmodules_oid) >= 0)693ret = 1;694
695return ret;696}
697
698/* This does a lookup of a submodule configuration by name or by path
699* (key) with on-demand reading of the appropriate .gitmodules from
700* revisions.
701*/
702static const struct submodule *config_from(struct submodule_cache *cache,703const struct object_id *treeish_name, const char *key,704enum lookup_type lookup_type)705{
706struct strbuf rev = STRBUF_INIT;707unsigned long config_size;708char *config = NULL;709struct object_id oid;710enum object_type type;711const struct submodule *submodule = NULL;712struct parse_config_parameter parameter;713
714/*715* If any parameter except the cache is a NULL pointer just
716* return the first submodule. Can be used to check whether
717* there are any submodules parsed.
718*/
719if (!treeish_name || !key) {720struct hashmap_iter iter;721struct submodule_entry *entry;722
723entry = hashmap_iter_first_entry(&cache->for_name, &iter,724struct submodule_entry,725ent /* member name */);726if (!entry)727return NULL;728return entry->config;729}730
731if (!gitmodule_oid_from_commit(treeish_name, &oid, &rev))732goto out;733
734switch (lookup_type) {735case lookup_name:736submodule = cache_lookup_name(cache, &oid, key);737break;738case lookup_path:739submodule = cache_lookup_path(cache, &oid, key);740break;741}742if (submodule)743goto out;744
745config = repo_read_object_file(the_repository, &oid, &type,746&config_size);747if (!config || type != OBJ_BLOB)748goto out;749
750/* fill the submodule config into the cache */751parameter.cache = cache;752parameter.treeish_name = treeish_name;753parameter.gitmodules_oid = &oid;754parameter.overwrite = 0;755git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,756config, config_size, ¶meter, CONFIG_SCOPE_UNKNOWN, NULL);757strbuf_release(&rev);758free(config);759
760switch (lookup_type) {761case lookup_name:762return cache_lookup_name(cache, &oid, key);763case lookup_path:764return cache_lookup_path(cache, &oid, key);765default:766return NULL;767}768
769out:770strbuf_release(&rev);771free(config);772return submodule;773}
774
775static void submodule_cache_check_init(struct repository *repo)776{
777if (repo->submodule_cache && repo->submodule_cache->initialized)778return;779
780if (!repo->submodule_cache)781repo->submodule_cache = submodule_cache_alloc();782
783submodule_cache_init(repo->submodule_cache);784}
785
786/*
787* Note: This function is private for a reason, the '.gitmodules' file should
788* not be used as a mechanism to retrieve arbitrary configuration stored in
789* the repository.
790*
791* Runs the provided config function on the '.gitmodules' file found in the
792* working directory.
793*/
794static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)795{
796if (repo->worktree) {797struct git_config_source config_source = {7980, .scope = CONFIG_SCOPE_SUBMODULE799};800const struct config_options opts = { 0 };801struct object_id oid;802char *file;803char *oidstr = NULL;804
805file = repo_worktree_path(repo, GITMODULES_FILE);806if (file_exists(file)) {807config_source.file = file;808} else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||809repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {810config_source.blob = oidstr = xstrdup(oid_to_hex(&oid));811if (repo != the_repository)812add_submodule_odb_by_path(repo->objects->odb->path);813} else {814goto out;815}816
817config_with_options(fn, data, &config_source, repo, &opts);818
819out:820free(oidstr);821free(file);822}823}
824
825static int gitmodules_cb(const char *var, const char *value,826const struct config_context *ctx, void *data)827{
828struct repository *repo = data;829struct parse_config_parameter parameter;830
831parameter.cache = repo->submodule_cache;832parameter.treeish_name = NULL;833parameter.gitmodules_oid = null_oid();834parameter.overwrite = 1;835
836return parse_config(var, value, ctx, ¶meter);837}
838
839void repo_read_gitmodules(struct repository *repo, int skip_if_read)840{
841submodule_cache_check_init(repo);842
843if (repo->submodule_cache->gitmodules_read && skip_if_read)844return;845
846if (repo_read_index(repo) < 0)847return;848
849if (!is_gitmodules_unmerged(repo->index))850config_from_gitmodules(gitmodules_cb, repo, repo);851
852repo->submodule_cache->gitmodules_read = 1;853}
854
855void gitmodules_config_oid(const struct object_id *commit_oid)856{
857struct strbuf rev = STRBUF_INIT;858struct object_id oid;859
860submodule_cache_check_init(the_repository);861
862if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {863git_config_from_blob_oid(gitmodules_cb, rev.buf,864the_repository, &oid, the_repository,865CONFIG_SCOPE_UNKNOWN);866}867strbuf_release(&rev);868
869the_repository->submodule_cache->gitmodules_read = 1;870}
871
872const struct submodule *submodule_from_name(struct repository *r,873const struct object_id *treeish_name,874const char *name)875{
876repo_read_gitmodules(r, 1);877return config_from(r->submodule_cache, treeish_name, name, lookup_name);878}
879
880const struct submodule *submodule_from_path(struct repository *r,881const struct object_id *treeish_name,882const char *path)883{
884repo_read_gitmodules(r, 1);885return config_from(r->submodule_cache, treeish_name, path, lookup_path);886}
887
888/**
889* Used internally by submodules_of_tree(). Recurses into 'treeish_name'
890* and appends submodule entries to 'out'. The submodule_cache expects
891* a root-level treeish_name and paths, so keep track of these values
892* with 'root_tree' and 'prefix'.
893*/
894static void traverse_tree_submodules(struct repository *r,895const struct object_id *root_tree,896char *prefix,897const struct object_id *treeish_name,898struct submodule_entry_list *out)899{
900struct tree_desc tree;901struct submodule_tree_entry *st_entry;902struct name_entry name_entry;903char *tree_path = NULL;904
905fill_tree_descriptor(r, &tree, treeish_name);906while (tree_entry(&tree, &name_entry)) {907if (prefix)908tree_path =909mkpathdup("%s/%s", prefix, name_entry.path);910else911tree_path = xstrdup(name_entry.path);912
913if (S_ISGITLINK(name_entry.mode) &&914is_tree_submodule_active(r, root_tree, tree_path)) {915ALLOC_GROW(out->entries, out->entry_nr + 1,916out->entry_alloc);917st_entry = &out->entries[out->entry_nr++];918
919st_entry->name_entry = xmalloc(sizeof(*st_entry->name_entry));920*st_entry->name_entry = name_entry;921st_entry->submodule =922submodule_from_path(r, root_tree, tree_path);923st_entry->repo = xmalloc(sizeof(*st_entry->repo));924if (repo_submodule_init(st_entry->repo, r, tree_path,925root_tree))926FREE_AND_NULL(st_entry->repo);927
928} else if (S_ISDIR(name_entry.mode))929traverse_tree_submodules(r, root_tree, tree_path,930&name_entry.oid, out);931free(tree_path);932}933}
934
935void submodules_of_tree(struct repository *r,936const struct object_id *treeish_name,937struct submodule_entry_list *out)938{
939CALLOC_ARRAY(out->entries, 0);940out->entry_nr = 0;941out->entry_alloc = 0;942
943traverse_tree_submodules(r, treeish_name, NULL, treeish_name, out);944}
945
946void submodule_free(struct repository *r)947{
948if (r->submodule_cache)949submodule_cache_clear(r->submodule_cache);950}
951
952static int config_print_callback(const char *var, const char *value,953const struct config_context *ctx UNUSED,954void *cb_data)955{
956char *wanted_key = cb_data;957
958if (!strcmp(wanted_key, var))959printf("%s\n", value);960
961return 0;962}
963
964int print_config_from_gitmodules(struct repository *repo, const char *key)965{
966int ret;967char *store_key;968
969ret = git_config_parse_key(key, &store_key, NULL);970if (ret < 0)971return CONFIG_INVALID_KEY;972
973config_from_gitmodules(config_print_callback, repo, store_key);974
975free(store_key);976return 0;977}
978
979int config_set_in_gitmodules_file_gently(const char *key, const char *value)980{
981int ret;982
983ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);984if (ret < 0)985/* Maybe the user already did that, don't error out here */986warning(_("Could not update .gitmodules entry %s"), key);987
988return ret;989}
990
991struct fetch_config {992int *max_children;993int *recurse_submodules;994};995
996static int gitmodules_fetch_config(const char *var, const char *value,997const struct config_context *ctx,998void *cb)999{
1000struct fetch_config *config = cb;1001if (!strcmp(var, "submodule.fetchjobs")) {1002if (config->max_children)1003*(config->max_children) =1004parse_submodule_fetchjobs(var, value, ctx->kvi);1005return 0;1006} else if (!strcmp(var, "fetch.recursesubmodules")) {1007if (config->recurse_submodules)1008*(config->recurse_submodules) =1009parse_fetch_recurse_submodules_arg(var, value);1010return 0;1011}1012
1013return 0;1014}
1015
1016void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)1017{
1018struct fetch_config config = {1019.max_children = max_children,1020.recurse_submodules = recurse_submodules1021};1022config_from_gitmodules(gitmodules_fetch_config, the_repository, &config);1023}
1024
1025static int gitmodules_update_clone_config(const char *var, const char *value,1026const struct config_context *ctx,1027void *cb)1028{
1029int *max_jobs = cb;1030if (!strcmp(var, "submodule.fetchjobs"))1031*max_jobs = parse_submodule_fetchjobs(var, value, ctx->kvi);1032return 0;1033}
1034
1035void update_clone_config_from_gitmodules(int *max_jobs)1036{
1037config_from_gitmodules(gitmodules_update_clone_config, the_repository, &max_jobs);1038}
1039