2 * Copyright (C) 2012 the libgit2 contributors
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
9 #include "git2/config.h"
10 #include "git2/types.h"
11 #include "git2/repository.h"
12 #include "git2/index.h"
13 #include "git2/submodule.h"
17 #include "config_file.h"
19 #include "repository.h"
21 static git_cvar_map _sm_update_map
[] = {
22 {GIT_CVAR_STRING
, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT
},
23 {GIT_CVAR_STRING
, "rebase", GIT_SUBMODULE_UPDATE_REBASE
},
24 {GIT_CVAR_STRING
, "merge", GIT_SUBMODULE_UPDATE_MERGE
}
27 static git_cvar_map _sm_ignore_map
[] = {
28 {GIT_CVAR_STRING
, "all", GIT_SUBMODULE_IGNORE_ALL
},
29 {GIT_CVAR_STRING
, "dirty", GIT_SUBMODULE_IGNORE_DIRTY
},
30 {GIT_CVAR_STRING
, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED
},
31 {GIT_CVAR_STRING
, "none", GIT_SUBMODULE_IGNORE_NONE
}
34 static inline khint_t
str_hash_no_trailing_slash(const char *s
)
39 if (s
[1] || *s
!= '/')
40 h
= (h
<< 5) - h
+ *s
;
45 static inline int str_equal_no_trailing_slash(const char *a
, const char *b
)
47 size_t alen
= a
? strlen(a
) : 0;
48 size_t blen
= b
? strlen(b
) : 0;
50 if (alen
&& a
[alen
] == '/')
52 if (blen
&& b
[blen
] == '/')
55 return (alen
== blen
&& strncmp(a
, b
, alen
) == 0);
58 __KHASH_IMPL(str
, static inline, const char *, void *, 1, str_hash_no_trailing_slash
, str_equal_no_trailing_slash
);
60 static git_submodule
*submodule_alloc(const char *name
)
62 git_submodule
*sm
= git__calloc(1, sizeof(git_submodule
));
66 sm
->path
= sm
->name
= git__strdup(name
);
75 static void submodule_release(git_submodule
*sm
, int decr
)
82 if (sm
->refcount
== 0) {
83 if (sm
->name
!= sm
->path
)
91 static int submodule_from_entry(
92 git_khash_str
*smcfg
, git_index_entry
*entry
)
99 pos
= git_khash_str_lookup_index(smcfg
, entry
->path
);
101 if (git_khash_str_valid_index(smcfg
, pos
))
102 sm
= git_khash_str_value_at(smcfg
, pos
);
104 sm
= submodule_alloc(entry
->path
);
106 git_oid_cpy(&sm
->oid
, &entry
->oid
);
108 if (strcmp(sm
->path
, entry
->path
) != 0) {
109 if (sm
->path
!= sm
->name
) {
113 sm
->path
= git__strdup(entry
->path
);
118 git_khash_str_insert2(smcfg
, sm
->path
, sm
, old_sm
, error
);
123 if (old_sm
&& ((git_submodule
*)old_sm
) != sm
) {
124 /* TODO: log warning about multiple entrys for same submodule path */
125 submodule_release(old_sm
, 1);
131 submodule_release(sm
, 0);
135 static int submodule_from_config(
136 const char *key
, const char *value
, void *data
)
138 git_khash_str
*smcfg
= data
;
139 const char *namestart
;
140 const char *property
;
141 git_buf name
= GIT_BUF_INIT
;
148 if (git__prefixcmp(key
, "submodule.") != 0)
151 namestart
= key
+ strlen("submodule.");
152 property
= strrchr(namestart
, '.');
153 if (property
== NULL
)
156 is_path
= (strcmp(property
, "path") == 0);
158 if (git_buf_set(&name
, namestart
, property
- namestart
- 1) < 0)
161 pos
= git_khash_str_lookup_index(smcfg
, name
.ptr
);
162 if (!git_khash_str_valid_index(smcfg
, pos
) && is_path
)
163 pos
= git_khash_str_lookup_index(smcfg
, value
);
164 if (!git_khash_str_valid_index(smcfg
, pos
))
165 sm
= submodule_alloc(name
.ptr
);
167 sm
= git_khash_str_value_at(smcfg
, pos
);
171 if (strcmp(sm
->name
, name
.ptr
) != 0) {
172 assert(sm
->path
== sm
->name
);
173 sm
->name
= git_buf_detach(&name
);
175 git_khash_str_insert2(smcfg
, sm
->name
, sm
, old_sm
, error
);
180 else if (is_path
&& strcmp(sm
->path
, value
) != 0) {
181 assert(sm
->path
== sm
->name
);
182 sm
->path
= git__strdup(value
);
183 if (sm
->path
== NULL
)
186 git_khash_str_insert2(smcfg
, sm
->path
, sm
, old_sm
, error
);
193 if (old_sm
&& ((git_submodule
*)old_sm
) != sm
) {
194 /* TODO: log warning about multiple submodules with same path */
195 submodule_release(old_sm
, 1);
201 /* copy other properties into submodule entry */
202 if (strcmp(property
, "url") == 0) {
207 if ((sm
->url
= git__strdup(value
)) == NULL
)
210 else if (strcmp(property
, "update") == 0) {
212 if (git_config_lookup_map_value(
213 _sm_update_map
, ARRAY_SIZE(_sm_update_map
), value
, &val
) < 0) {
214 giterr_set(GITERR_INVALID
,
215 "Invalid value for submodule update property: '%s'", value
);
218 sm
->update
= (git_submodule_update_t
)val
;
220 else if (strcmp(property
, "fetchRecurseSubmodules") == 0) {
221 if (git_config_parse_bool(&sm
->fetch_recurse
, value
) < 0)
224 else if (strcmp(property
, "ignore") == 0) {
226 if (git_config_lookup_map_value(
227 _sm_ignore_map
, ARRAY_SIZE(_sm_ignore_map
), value
, &val
) < 0) {
228 giterr_set(GITERR_INVALID
,
229 "Invalid value for submodule ignore property: '%s'", value
);
232 sm
->ignore
= (git_submodule_ignore_t
)val
;
234 /* ignore other unknown submodule properties */
239 submodule_release(sm
, 0);
244 static int load_submodule_config(git_repository
*repo
)
248 unsigned int i
, max_i
;
249 git_oid gitmodules_oid
;
250 git_khash_str
*smcfg
;
251 struct git_config_file
*mods
= NULL
;
253 if (repo
->submodules
)
256 /* submodule data is kept in a hashtable with each submodule stored
257 * under both its name and its path. These are usually the same, but
258 * that is not guaranteed.
260 smcfg
= git_khash_str_alloc();
261 GITERR_CHECK_ALLOC(smcfg
);
263 /* scan index for gitmodules (and .gitmodules entry) */
264 if ((error
= git_repository_index__weakptr(&index
, repo
)) < 0)
266 memset(&gitmodules_oid
, 0, sizeof(gitmodules_oid
));
267 max_i
= git_index_entrycount(index
);
269 for (i
= 0; i
< max_i
; i
++) {
270 git_index_entry
*entry
= git_index_get(index
, i
);
271 if (S_ISGITLINK(entry
->mode
)) {
272 if ((error
= submodule_from_entry(smcfg
, entry
)) < 0)
275 else if (strcmp(entry
->path
, ".gitmodules") == 0)
276 git_oid_cpy(&gitmodules_oid
, &entry
->oid
);
279 /* load .gitmodules from workdir if it exists */
280 if (git_repository_workdir(repo
) != NULL
) {
281 /* look in workdir for .gitmodules */
282 git_buf path
= GIT_BUF_INIT
;
283 if (!git_buf_joinpath(
284 &path
, git_repository_workdir(repo
), ".gitmodules") &&
285 git_path_isfile(path
.ptr
))
287 if (!(error
= git_config_file__ondisk(&mods
, path
.ptr
)))
288 error
= git_config_file_open(mods
);
293 /* load .gitmodules from object cache if not in workdir */
294 if (!error
&& mods
== NULL
&& !git_oid_iszero(&gitmodules_oid
)) {
295 /* TODO: is it worth loading gitmodules from object cache? */
298 /* process .gitmodules info */
299 if (!error
&& mods
!= NULL
)
300 error
= git_config_file_foreach(mods
, submodule_from_config
, smcfg
);
302 /* store submodule config in repo */
304 repo
->submodules
= smcfg
;
308 git_config_file_free(mods
);
310 git_khash_str_free(smcfg
);
314 void git_submodule_config_free(git_repository
*repo
)
316 git_khash_str
*smcfg
= repo
->submodules
;
319 repo
->submodules
= NULL
;
324 git_khash_str_foreach_value(smcfg
, sm
, {
325 submodule_release(sm
,1);
327 git_khash_str_free(smcfg
);
330 static int submodule_cmp(const void *a
, const void *b
)
332 return strcmp(((git_submodule
*)a
)->name
, ((git_submodule
*)b
)->name
);
335 int git_submodule_foreach(
336 git_repository
*repo
,
337 int (*callback
)(const char *name
, void *payload
),
342 git_vector seen
= GIT_VECTOR_INIT
;
343 seen
._cmp
= submodule_cmp
;
345 if ((error
= load_submodule_config(repo
)) < 0)
348 git_khash_str_foreach_value(repo
->submodules
, sm
, {
349 /* usually the following will not come into play */
350 if (sm
->refcount
> 1) {
351 if (git_vector_bsearch(&seen
, sm
) != GIT_ENOTFOUND
)
353 if ((error
= git_vector_insert(&seen
, sm
)) < 0)
357 if ((error
= callback(sm
->name
, payload
)) < 0)
361 git_vector_free(&seen
);
366 int git_submodule_lookup(
367 git_submodule
**sm_ptr
, /* NULL allowed if user only wants to test */
368 git_repository
*repo
,
369 const char *name
) /* trailing slash is allowed */
373 if (load_submodule_config(repo
) < 0)
376 pos
= git_khash_str_lookup_index(repo
->submodules
, name
);
377 if (!git_khash_str_valid_index(repo
->submodules
, pos
))
378 return GIT_ENOTFOUND
;
381 *sm_ptr
= git_khash_str_value_at(repo
->submodules
, pos
);