]> git.proxmox.com Git - libgit2.git/blame - src/submodule.c
Add range checking around cache opts
[libgit2.git] / src / submodule.c
CommitLineData
bfc9ca59 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
bfc9ca59
RB
3 *
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.
6 */
7
8#include "common.h"
9#include "git2/config.h"
83041c71 10#include "git2/sys/config.h"
bfc9ca59
RB
11#include "git2/types.h"
12#include "git2/repository.h"
13#include "git2/index.h"
14#include "git2/submodule.h"
15#include "buffer.h"
7bf87ab6 16#include "buf_text.h"
bfc9ca59
RB
17#include "vector.h"
18#include "posix.h"
19#include "config_file.h"
20#include "config.h"
21#include "repository.h"
aa13bf05
RB
22#include "submodule.h"
23#include "tree.h"
24#include "iterator.h"
25
26#define GIT_MODULES_FILE ".gitmodules"
bfc9ca59 27
95dfb031
RB
28static git_cvar_map _sm_update_map[] = {
29 {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
30 {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
aa13bf05
RB
31 {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
32 {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
bfc9ca59
RB
33};
34
95dfb031 35static git_cvar_map _sm_ignore_map[] = {
aa13bf05 36 {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
95dfb031 37 {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
aa13bf05
RB
38 {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
39 {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
bfc9ca59
RB
40};
41
72ee0787 42static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
bfc9ca59 43{
01fed0a8 44 khint_t h;
bfc9ca59 45
01fed0a8 46 for (h = 0; *s; ++s)
5f4a61ae 47 if (s[1] != '\0' || *s != '/')
01fed0a8 48 h = (h << 5) - h + *s;
bfc9ca59 49
01fed0a8 50 return h;
bfc9ca59
RB
51}
52
72ee0787 53static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
bfc9ca59 54{
01fed0a8
RB
55 size_t alen = a ? strlen(a) : 0;
56 size_t blen = b ? strlen(b) : 0;
bfc9ca59 57
5f4a61ae 58 if (alen > 0 && a[alen - 1] == '/')
bfc9ca59 59 alen--;
5f4a61ae 60 if (blen > 0 && b[blen - 1] == '/')
bfc9ca59
RB
61 blen--;
62
01fed0a8 63 return (alen == blen && strncmp(a, b, alen) == 0);
bfc9ca59
RB
64}
65
aa13bf05
RB
66__KHASH_IMPL(
67 str, static kh_inline, const char *, void *, 1,
68 str_hash_no_trailing_slash, str_equal_no_trailing_slash);
69
a9a73007 70static int load_submodule_config(git_repository *repo);
54b2a37a 71static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
5f4a61ae
RB
72static int lookup_head_remote(git_buf *url, git_repository *repo);
73static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
74static void submodule_release(git_submodule *sm, int decr);
75static int submodule_load_from_index(git_repository *, const git_index_entry *);
76static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
a1abe66a 77static int submodule_load_from_config(const git_config_entry *, void *);
5f4a61ae
RB
78static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
79static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
80static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
81static int submodule_index_status(unsigned int *status, git_submodule *sm);
82static int submodule_wd_status(unsigned int *status, git_submodule *sm);
aa13bf05
RB
83
84static int submodule_cmp(const void *a, const void *b)
85{
86 return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
87}
88
89static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
90{
91 ssize_t idx = git_buf_rfind(key, '.');
92 git_buf_truncate(key, (size_t)(idx + 1));
93 return git_buf_puts(key, suffix);
94}
95
96/*
97 * PUBLIC APIS
98 */
99
100int git_submodule_lookup(
101 git_submodule **sm_ptr, /* NULL if user only wants to test existence */
102 git_repository *repo,
103 const char *name) /* trailing slash is allowed */
104{
105 int error;
106 khiter_t pos;
107
108 assert(repo && name);
109
a9a73007 110 if ((error = load_submodule_config(repo)) < 0)
aa13bf05
RB
111 return error;
112
113 pos = git_strmap_lookup_index(repo->submodules, name);
114
115 if (!git_strmap_valid_index(repo->submodules, pos)) {
116 error = GIT_ENOTFOUND;
117
118 /* check if a plausible submodule exists at path */
119 if (git_repository_workdir(repo)) {
120 git_buf path = GIT_BUF_INIT;
121
122 if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0)
123 return -1;
124
125 if (git_path_contains_dir(&path, DOT_GIT))
126 error = GIT_EEXISTS;
127
128 git_buf_free(&path);
129 }
130
131 return error;
132 }
133
134 if (sm_ptr)
135 *sm_ptr = git_strmap_value_at(repo->submodules, pos);
136
137 return 0;
138}
01fed0a8 139
aa13bf05
RB
140int git_submodule_foreach(
141 git_repository *repo,
142 int (*callback)(git_submodule *sm, const char *name, void *payload),
143 void *payload)
144{
145 int error;
146 git_submodule *sm;
147 git_vector seen = GIT_VECTOR_INIT;
148 seen._cmp = submodule_cmp;
149
150 assert(repo && callback);
151
a9a73007 152 if ((error = load_submodule_config(repo)) < 0)
aa13bf05
RB
153 return error;
154
155 git_strmap_foreach_value(repo->submodules, sm, {
156 /* Usually the following will not come into play - it just prevents
157 * us from issuing a callback twice for a submodule where the name
158 * and path are not the same.
159 */
160 if (sm->refcount > 1) {
11d9f6b3 161 if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND)
aa13bf05
RB
162 continue;
163 if ((error = git_vector_insert(&seen, sm)) < 0)
164 break;
165 }
166
5f4a61ae 167 if (callback(sm, sm->name, payload)) {
f335ecd6 168 giterr_clear();
5f4a61ae 169 error = GIT_EUSER;
aa13bf05 170 break;
5f4a61ae 171 }
aa13bf05
RB
172 });
173
174 git_vector_free(&seen);
175
176 return error;
177}
178
179void git_submodule_config_free(git_repository *repo)
180{
181 git_strmap *smcfg;
182 git_submodule *sm;
183
184 assert(repo);
185
186 smcfg = repo->submodules;
187 repo->submodules = NULL;
188
189 if (smcfg == NULL)
190 return;
191
192 git_strmap_foreach_value(smcfg, sm, {
193 submodule_release(sm,1);
194 });
195 git_strmap_free(smcfg);
196}
197
198int git_submodule_add_setup(
199 git_submodule **submodule,
200 git_repository *repo,
201 const char *url,
202 const char *path,
203 int use_gitlink)
204{
205 int error = 0;
54b2a37a 206 git_config_backend *mods = NULL;
aa13bf05
RB
207 git_submodule *sm;
208 git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
b4d13652 209 git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
aa13bf05
RB
210 git_repository *subrepo = NULL;
211
212 assert(repo && url && path);
213
214 /* see if there is already an entry for this submodule */
215
216 if (git_submodule_lookup(&sm, repo, path) < 0)
217 giterr_clear();
218 else {
219 giterr_set(GITERR_SUBMODULE,
220 "Attempt to add a submodule that already exists");
221 return GIT_EEXISTS;
222 }
223
224 /* resolve parameters */
225
226 if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) {
227 if (!(error = lookup_head_remote(&real_url, repo)))
228 error = git_path_apply_relative(&real_url, url);
229 } else if (strchr(url, ':') != NULL || url[0] == '/') {
230 error = git_buf_sets(&real_url, url);
231 } else {
232 giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
233 error = -1;
234 }
235 if (error)
236 goto cleanup;
237
238 /* validate and normalize path */
239
240 if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
241 path += strlen(git_repository_workdir(repo));
242
243 if (git_path_root(path) >= 0) {
244 giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
245 error = -1;
246 goto cleanup;
247 }
248
249 /* update .gitmodules */
250
251 if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
252 giterr_set(GITERR_SUBMODULE,
253 "Adding submodules to a bare repository is not supported (for now)");
254 return -1;
255 }
256
257 if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
258 (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
259 goto cleanup;
260
261 if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
262 (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
263 goto cleanup;
264
265 git_buf_clear(&name);
266
267 /* init submodule repository and add origin remote as needed */
268
269 error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
270 if (error < 0)
271 goto cleanup;
272
273 /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
274 * gitlink in the sub-repo workdir directory to that repository
275 *
276 * Old style: sub-repo goes directly into repo/<name>/.git/
277 */
278
aa13bf05
RB
279 initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
280 GIT_REPOSITORY_INIT_NO_REINIT;
281 initopt.origin_url = real_url.ptr;
282
283 if (git_path_exists(name.ptr) &&
284 git_path_contains(&name, DOT_GIT))
285 {
286 /* repo appears to already exist - reinit? */
287 }
288 else if (use_gitlink) {
289 git_buf repodir = GIT_BUF_INIT;
290
291 error = git_buf_join_n(
292 &repodir, '/', 3, git_repository_path(repo), "modules", path);
293 if (error < 0)
294 goto cleanup;
295
296 initopt.workdir_path = name.ptr;
297 initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
298
299 error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
300
301 git_buf_free(&repodir);
302 }
303 else {
304 error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
305 }
306 if (error < 0)
307 goto cleanup;
308
309 /* add submodule to hash and "reload" it */
310
0c8858de
RB
311 if (!(error = submodule_get(&sm, repo, path, NULL)) &&
312 !(error = git_submodule_reload(sm)))
313 error = git_submodule_init(sm, false);
aa13bf05
RB
314
315cleanup:
316 if (submodule != NULL)
317 *submodule = !error ? sm : NULL;
318
319 if (mods != NULL)
320 git_config_file_free(mods);
321 git_repository_free(subrepo);
322 git_buf_free(&real_url);
323 git_buf_free(&name);
324
325 return error;
326}
327
328int git_submodule_add_finalize(git_submodule *sm)
329{
330 int error;
331 git_index *index;
332
333 assert(sm);
334
335 if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
25743bd7 336 (error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
aa13bf05
RB
337 return error;
338
5f4a61ae 339 return git_submodule_add_to_index(sm, true);
aa13bf05
RB
340}
341
5f4a61ae 342int git_submodule_add_to_index(git_submodule *sm, int write_index)
aa13bf05
RB
343{
344 int error;
7cdad6c7 345 git_repository *repo, *sm_repo = NULL;
aa13bf05
RB
346 git_index *index;
347 git_buf path = GIT_BUF_INIT;
348 git_commit *head;
349 git_index_entry entry;
350 struct stat st;
351
352 assert(sm);
353
354 repo = sm->owner;
355
5f4a61ae
RB
356 /* force reload of wd OID by git_submodule_open */
357 sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
358
aa13bf05
RB
359 if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
360 (error = git_buf_joinpath(
361 &path, git_repository_workdir(repo), sm->path)) < 0 ||
362 (error = git_submodule_open(&sm_repo, sm)) < 0)
363 goto cleanup;
364
365 /* read stat information for submodule working directory */
366 if (p_stat(path.ptr, &st) < 0) {
367 giterr_set(GITERR_SUBMODULE,
368 "Cannot add submodule without working directory");
369 error = -1;
370 goto cleanup;
371 }
97a17e4e
RB
372
373 memset(&entry, 0, sizeof(entry));
5f4a61ae 374 entry.path = sm->path;
55cbd05b 375 git_index_entry__init_from_stat(&entry, &st);
aa13bf05
RB
376
377 /* calling git_submodule_open will have set sm->wd_oid if possible */
378 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
379 giterr_set(GITERR_SUBMODULE,
380 "Cannot add submodule without HEAD to index");
381 error = -1;
382 goto cleanup;
383 }
384 git_oid_cpy(&entry.oid, &sm->wd_oid);
385
386 if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
387 goto cleanup;
388
389 entry.ctime.seconds = git_commit_time(head);
390 entry.ctime.nanoseconds = 0;
391 entry.mtime.seconds = git_commit_time(head);
392 entry.mtime.nanoseconds = 0;
393
394 git_commit_free(head);
395
5f4a61ae 396 /* add it */
f45ec1a0 397 error = git_index_add(index, &entry);
aa13bf05 398
5f4a61ae
RB
399 /* write it, if requested */
400 if (!error && write_index) {
401 error = git_index_write(index);
402
403 if (!error)
404 git_oid_cpy(&sm->index_oid, &sm->wd_oid);
405 }
406
aa13bf05
RB
407cleanup:
408 git_repository_free(sm_repo);
409 git_buf_free(&path);
410 return error;
411}
412
413int git_submodule_save(git_submodule *submodule)
414{
415 int error = 0;
54b2a37a 416 git_config_backend *mods;
aa13bf05
RB
417 git_buf key = GIT_BUF_INIT;
418
419 assert(submodule);
420
421 mods = open_gitmodules(submodule->owner, true, NULL);
422 if (!mods) {
423 giterr_set(GITERR_SUBMODULE,
424 "Adding submodules to a bare repository is not supported (for now)");
425 return -1;
426 }
427
428 if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0)
429 goto cleanup;
430
431 /* save values for path, url, update, ignore, fetchRecurseSubmodules */
432
433 if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 ||
434 (error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0)
435 goto cleanup;
436
437 if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 ||
438 (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0)
439 goto cleanup;
440
441 if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
442 submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
443 {
444 const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
445 NULL : _sm_update_map[submodule->update].str_match;
446 error = git_config_file_set_string(mods, key.ptr, val);
447 }
448 if (error < 0)
449 goto cleanup;
450
451 if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
452 submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
453 {
454 const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
455 NULL : _sm_ignore_map[submodule->ignore].str_match;
456 error = git_config_file_set_string(mods, key.ptr, val);
457 }
458 if (error < 0)
459 goto cleanup;
460
461 if ((error = submodule_config_key_trunc_puts(
462 &key, "fetchRecurseSubmodules")) < 0 ||
463 (error = git_config_file_set_string(
464 mods, key.ptr, submodule->fetch_recurse ? "true" : "false")) < 0)
465 goto cleanup;
466
467 /* update internal defaults */
468
469 submodule->ignore_default = submodule->ignore;
470 submodule->update_default = submodule->update;
471 submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
472
473cleanup:
474 if (mods != NULL)
475 git_config_file_free(mods);
476 git_buf_free(&key);
477
478 return error;
479}
480
481git_repository *git_submodule_owner(git_submodule *submodule)
482{
483 assert(submodule);
484 return submodule->owner;
485}
486
487const char *git_submodule_name(git_submodule *submodule)
488{
489 assert(submodule);
490 return submodule->name;
491}
492
493const char *git_submodule_path(git_submodule *submodule)
494{
495 assert(submodule);
496 return submodule->path;
497}
498
499const char *git_submodule_url(git_submodule *submodule)
500{
501 assert(submodule);
502 return submodule->url;
503}
504
505int git_submodule_set_url(git_submodule *submodule, const char *url)
506{
507 assert(submodule && url);
508
509 git__free(submodule->url);
510
511 submodule->url = git__strdup(url);
512 GITERR_CHECK_ALLOC(submodule->url);
513
514 return 0;
515}
516
9cd42358 517const git_oid *git_submodule_index_id(git_submodule *submodule)
aa13bf05
RB
518{
519 assert(submodule);
520
521 if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
522 return &submodule->index_oid;
523 else
524 return NULL;
525}
526
9cd42358 527const git_oid *git_submodule_head_id(git_submodule *submodule)
aa13bf05
RB
528{
529 assert(submodule);
530
531 if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
532 return &submodule->head_oid;
533 else
534 return NULL;
535}
536
9cd42358 537const git_oid *git_submodule_wd_id(git_submodule *submodule)
aa13bf05
RB
538{
539 assert(submodule);
540
541 if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
542 git_repository *subrepo;
543
544 /* calling submodule open grabs the HEAD OID if possible */
545 if (!git_submodule_open(&subrepo, submodule))
546 git_repository_free(subrepo);
5f4a61ae
RB
547 else
548 giterr_clear();
aa13bf05
RB
549 }
550
551 if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
552 return &submodule->wd_oid;
553 else
554 return NULL;
555}
556
557git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
558{
559 assert(submodule);
560 return submodule->ignore;
561}
562
563git_submodule_ignore_t git_submodule_set_ignore(
564 git_submodule *submodule, git_submodule_ignore_t ignore)
565{
566 git_submodule_ignore_t old;
567
568 assert(submodule);
569
570 if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
571 ignore = submodule->ignore_default;
572
573 old = submodule->ignore;
574 submodule->ignore = ignore;
575 return old;
576}
577
578git_submodule_update_t git_submodule_update(git_submodule *submodule)
579{
580 assert(submodule);
581 return submodule->update;
582}
583
584git_submodule_update_t git_submodule_set_update(
585 git_submodule *submodule, git_submodule_update_t update)
586{
587 git_submodule_update_t old;
588
589 assert(submodule);
590
591 if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
592 update = submodule->update_default;
593
594 old = submodule->update;
595 submodule->update = update;
596 return old;
597}
598
17b06f4d
RB
599int git_submodule_fetch_recurse_submodules(
600 git_submodule *submodule)
601{
602 assert(submodule);
603 return submodule->fetch_recurse;
604}
605
606int git_submodule_set_fetch_recurse_submodules(
607 git_submodule *submodule,
608 int fetch_recurse_submodules)
609{
610 int old;
611
612 assert(submodule);
613
614 old = submodule->fetch_recurse;
615 submodule->fetch_recurse = (fetch_recurse_submodules != 0);
616 return old;
617}
618
aa13bf05
RB
619int git_submodule_init(git_submodule *submodule, int overwrite)
620{
621 int error;
622
623 /* write "submodule.NAME.url" */
624
625 if (!submodule->url) {
626 giterr_set(GITERR_SUBMODULE,
627 "No URL configured for submodule '%s'", submodule->name);
628 return -1;
629 }
630
631 error = submodule_update_config(
632 submodule, "url", submodule->url, overwrite != 0, false);
633 if (error < 0)
634 return error;
635
636 /* write "submodule.NAME.update" if not default */
637
638 if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
639 error = submodule_update_config(
640 submodule, "update", NULL, (overwrite != 0), false);
641 else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
642 error = submodule_update_config(
643 submodule, "update",
644 _sm_update_map[submodule->update].str_match,
645 (overwrite != 0), false);
646
647 return error;
648}
649
650int git_submodule_sync(git_submodule *submodule)
651{
652 if (!submodule->url) {
653 giterr_set(GITERR_SUBMODULE,
654 "No URL configured for submodule '%s'", submodule->name);
655 return -1;
656 }
657
658 /* copy URL over to config only if it already exists */
659
660 return submodule_update_config(
661 submodule, "url", submodule->url, true, true);
662}
663
664int git_submodule_open(
665 git_repository **subrepo,
666 git_submodule *submodule)
667{
668 int error;
669 git_buf path = GIT_BUF_INIT;
670 git_repository *repo;
671 const char *workdir;
672
673 assert(submodule && subrepo);
674
675 repo = submodule->owner;
676 workdir = git_repository_workdir(repo);
677
678 if (!workdir) {
679 giterr_set(GITERR_REPOSITORY,
680 "Cannot open submodule repository in a bare repo");
681 return GIT_ENOTFOUND;
682 }
683
684 if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
685 giterr_set(GITERR_REPOSITORY,
686 "Cannot open submodule repository that is not checked out");
687 return GIT_ENOTFOUND;
688 }
689
690 if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
691 return -1;
692
693 error = git_repository_open(subrepo, path.ptr);
694
695 git_buf_free(&path);
696
697 /* if we have opened the submodule successfully, let's grab the HEAD OID */
37ee70fa 698 if (!error) {
2508cc66 699 if (!git_reference_name_to_id(
aa13bf05
RB
700 &submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
701 submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
702 else
703 giterr_clear();
704 }
705
706 return error;
707}
708
709int git_submodule_reload_all(git_repository *repo)
710{
711 assert(repo);
a9a73007
RB
712 git_submodule_config_free(repo);
713 return load_submodule_config(repo);
aa13bf05
RB
714}
715
716int git_submodule_reload(git_submodule *submodule)
717{
718 git_repository *repo;
719 git_index *index;
11d9f6b3
PK
720 int error;
721 size_t pos;
aa13bf05 722 git_tree *head;
54b2a37a 723 git_config_backend *mods;
aa13bf05
RB
724
725 assert(submodule);
726
727 /* refresh index data */
728
729 repo = submodule->owner;
730 if (git_repository_index__weakptr(&index, repo) < 0)
731 return -1;
732
5f4a61ae
RB
733 submodule->flags = submodule->flags &
734 ~(GIT_SUBMODULE_STATUS_IN_INDEX |
735 GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
736
11d9f6b3 737 if (!git_index_find(&pos, index, submodule->path)) {
f45d51ff 738 const git_index_entry *entry = git_index_get_byindex(index, pos);
aa13bf05 739
5f4a61ae
RB
740 if (S_ISGITLINK(entry->mode)) {
741 if ((error = submodule_load_from_index(repo, entry)) < 0)
742 return error;
743 } else {
744 submodule_mode_mismatch(
745 repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
746 }
aa13bf05
RB
747 }
748
749 /* refresh HEAD tree data */
750
751 if (!(error = git_repository_head_tree(&head, repo))) {
752 git_tree_entry *te;
753
754 submodule->flags = submodule->flags &
755 ~(GIT_SUBMODULE_STATUS_IN_HEAD |
756 GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
757
758 if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
5f4a61ae
RB
759
760 if (S_ISGITLINK(te->attr)) {
761 error = submodule_load_from_head(repo, submodule->path, &te->oid);
762 } else {
763 submodule_mode_mismatch(
764 repo, submodule->path,
765 GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
766 }
aa13bf05
RB
767
768 git_tree_entry_free(te);
769 }
770 else if (error == GIT_ENOTFOUND) {
771 giterr_clear();
772 error = 0;
773 }
774
775 git_tree_free(head);
776 }
777
778 if (error < 0)
779 return error;
780
781 /* refresh config data */
782
783 if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
784 git_buf path = GIT_BUF_INIT;
785
786 git_buf_sets(&path, "submodule\\.");
7bf87ab6 787 git_buf_text_puts_escape_regex(&path, submodule->name);
aa13bf05
RB
788 git_buf_puts(&path, ".*");
789
790 if (git_buf_oom(&path))
791 error = -1;
792 else
793 error = git_config_file_foreach_match(
794 mods, path.ptr, submodule_load_from_config, repo);
795
796 git_buf_free(&path);
0c8858de 797 git_config_file_free(mods);
aa13bf05
RB
798 }
799
5f4a61ae
RB
800 if (error < 0)
801 return error;
802
803 /* refresh wd data */
804
805 submodule->flags = submodule->flags &
806 ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
807
808 error = submodule_load_from_wd_lite(submodule, submodule->path, NULL);
809
aa13bf05
RB
810 return error;
811}
812
813int git_submodule_status(
814 unsigned int *status,
815 git_submodule *submodule)
816{
5f4a61ae
RB
817 int error = 0;
818 unsigned int status_val;
819
aa13bf05
RB
820 assert(status && submodule);
821
5f4a61ae 822 status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
0c8858de 823
5f4a61ae
RB
824 if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
825 if (!(error = submodule_index_status(&status_val, submodule)))
826 error = submodule_wd_status(&status_val, submodule);
827 }
aa13bf05 828
5f4a61ae 829 *status = status_val;
aa13bf05 830
5f4a61ae 831 return error;
aa13bf05
RB
832}
833
a9a73007
RB
834int git_submodule_location(
835 unsigned int *location_status,
836 git_submodule *submodule)
837{
838 assert(location_status && submodule);
839
840 *location_status = submodule->flags &
841 (GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX |
842 GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD);
843
844 return 0;
845}
846
847
aa13bf05
RB
848/*
849 * INTERNAL FUNCTIONS
850 */
851
852static git_submodule *submodule_alloc(git_repository *repo, const char *name)
bfc9ca59 853{
0c8858de 854 git_submodule *sm;
bfc9ca59 855
0c8858de
RB
856 if (!name || !strlen(name)) {
857 giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
bfc9ca59
RB
858 return NULL;
859 }
860
0c8858de
RB
861 sm = git__calloc(1, sizeof(git_submodule));
862 if (sm == NULL)
863 goto fail;
864
865 sm->path = sm->name = git__strdup(name);
866 if (!sm->name)
867 goto fail;
868
aa13bf05 869 sm->owner = repo;
0c8858de 870 sm->refcount = 1;
aa13bf05
RB
871
872 return sm;
0c8858de
RB
873
874fail:
875 submodule_release(sm, 0);
876 return NULL;
aa13bf05
RB
877}
878
879static void submodule_release(git_submodule *sm, int decr)
880{
881 if (!sm)
882 return;
883
884 sm->refcount -= decr;
885
886 if (sm->refcount == 0) {
887 if (sm->name != sm->path) {
888 git__free(sm->path);
889 sm->path = NULL;
890 }
891
892 git__free(sm->name);
893 sm->name = NULL;
894
895 git__free(sm->url);
896 sm->url = NULL;
897
898 sm->owner = NULL;
899
900 git__free(sm);
901 }
902}
903
0c8858de
RB
904static int submodule_get(
905 git_submodule **sm_ptr,
906 git_repository *repo,
907 const char *name,
908 const char *alternate)
aa13bf05
RB
909{
910 git_strmap *smcfg = repo->submodules;
911 khiter_t pos;
912 git_submodule *sm;
0c8858de 913 int error;
aa13bf05 914
0c8858de 915 assert(repo && name);
aa13bf05 916
0c8858de 917 pos = git_strmap_lookup_index(smcfg, name);
aa13bf05 918
0c8858de
RB
919 if (!git_strmap_valid_index(smcfg, pos) && alternate)
920 pos = git_strmap_lookup_index(smcfg, alternate);
aa13bf05 921
0c8858de
RB
922 if (!git_strmap_valid_index(smcfg, pos)) {
923 sm = submodule_alloc(repo, name);
aa13bf05 924
0c8858de
RB
925 /* insert value at name - if another thread beats us to it, then use
926 * their record and release our own.
927 */
5f4a61ae 928 pos = kh_put(str, smcfg, sm->name, &error);
0c8858de
RB
929
930 if (error < 0) {
931 submodule_release(sm, 1);
932 sm = NULL;
933 } else if (error == 0) {
934 submodule_release(sm, 1);
935 sm = git_strmap_value_at(smcfg, pos);
936 } else {
937 git_strmap_set_value_at(smcfg, pos, sm);
938 }
939 } else {
940 sm = git_strmap_value_at(smcfg, pos);
aa13bf05
RB
941 }
942
0c8858de 943 *sm_ptr = sm;
aa13bf05 944
0c8858de 945 return (sm != NULL) ? 0 : -1;
bfc9ca59
RB
946}
947
aa13bf05
RB
948static int submodule_load_from_index(
949 git_repository *repo, const git_index_entry *entry)
bfc9ca59 950{
0c8858de 951 git_submodule *sm;
bfc9ca59 952
0c8858de 953 if (submodule_get(&sm, repo, entry->path, NULL) < 0)
aa13bf05 954 return -1;
bfc9ca59 955
aa13bf05
RB
956 if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
957 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
958 return 0;
bfc9ca59 959 }
bfc9ca59 960
aa13bf05 961 sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
01fed0a8 962
aa13bf05
RB
963 git_oid_cpy(&sm->index_oid, &entry->oid);
964 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
bfc9ca59 965
0c8858de 966 return 0;
aa13bf05 967}
bfc9ca59 968
aa13bf05
RB
969static int submodule_load_from_head(
970 git_repository *repo, const char *path, const git_oid *oid)
971{
0c8858de 972 git_submodule *sm;
bfc9ca59 973
0c8858de 974 if (submodule_get(&sm, repo, path, NULL) < 0)
aa13bf05 975 return -1;
bfc9ca59 976
aa13bf05 977 sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
bfc9ca59 978
aa13bf05
RB
979 git_oid_cpy(&sm->head_oid, oid);
980 sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
bfc9ca59 981
0c8858de
RB
982 return 0;
983}
984
985static int submodule_config_error(const char *property, const char *value)
986{
987 giterr_set(GITERR_INVALID,
988 "Invalid value for submodule '%s' property: '%s'", property, value);
989 return -1;
bfc9ca59
RB
990}
991
aa13bf05 992static int submodule_load_from_config(
a1abe66a 993 const git_config_entry *entry, void *data)
bfc9ca59 994{
aa13bf05
RB
995 git_repository *repo = data;
996 git_strmap *smcfg = repo->submodules;
0c8858de 997 const char *namestart, *property, *alternate = NULL;
a1abe66a 998 const char *key = entry->name, *value = entry->value;
bfc9ca59
RB
999 git_buf name = GIT_BUF_INIT;
1000 git_submodule *sm;
bfc9ca59 1001 bool is_path;
0c8858de 1002 int error = 0;
bfc9ca59
RB
1003
1004 if (git__prefixcmp(key, "submodule.") != 0)
1005 return 0;
1006
1007 namestart = key + strlen("submodule.");
1008 property = strrchr(namestart, '.');
1009 if (property == NULL)
1010 return 0;
1011 property++;
aa13bf05 1012 is_path = (strcasecmp(property, "path") == 0);
bfc9ca59
RB
1013
1014 if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
1015 return -1;
1016
0c8858de
RB
1017 if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
1018 git_buf_free(&name);
1019 return -1;
1020 }
bfc9ca59 1021
aa13bf05
RB
1022 sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
1023
0c8858de
RB
1024 /* Only from config might we get differing names & paths. If so, then
1025 * update the submodule and insert under the alternative key.
1026 */
01fed0a8 1027
0c8858de
RB
1028 /* TODO: if case insensitive filesystem, then the following strcmps
1029 * should be strcasecmp
1030 */
01fed0a8 1031
0c8858de
RB
1032 if (strcmp(sm->name, name.ptr) != 0) {
1033 alternate = sm->name = git_buf_detach(&name);
1034 } else if (is_path && value && strcmp(sm->path, value) != 0) {
1035 alternate = sm->path = git__strdup(value);
1036 if (!sm->path)
1037 error = -1;
bfc9ca59 1038 }
0c8858de
RB
1039 if (alternate) {
1040 void *old_sm = NULL;
1041 git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
bfc9ca59 1042
0c8858de
RB
1043 if (error >= 0)
1044 sm->refcount++; /* inserted under a new key */
1045
1046 /* if we replaced an old module under this key, release the old one */
1047 if (old_sm && ((git_submodule *)old_sm) != sm) {
1048 submodule_release(old_sm, 1);
1049 /* TODO: log warning about multiple submodules with same path */
1050 }
bfc9ca59
RB
1051 }
1052
0c8858de
RB
1053 git_buf_free(&name);
1054 if (error < 0)
1055 return error;
1056
aa13bf05
RB
1057 /* TODO: Look up path in index and if it is present but not a GITLINK
1058 * then this should be deleted (at least to match git's behavior)
1059 */
1060
bfc9ca59
RB
1061 if (is_path)
1062 return 0;
1063
1064 /* copy other properties into submodule entry */
aa13bf05 1065 if (strcasecmp(property, "url") == 0) {
0c8858de
RB
1066 git__free(sm->url);
1067 sm->url = NULL;
1068
aa13bf05 1069 if (value != NULL && (sm->url = git__strdup(value)) == NULL)
0c8858de 1070 return -1;
bfc9ca59 1071 }
aa13bf05 1072 else if (strcasecmp(property, "update") == 0) {
95dfb031
RB
1073 int val;
1074 if (git_config_lookup_map_value(
a1abe66a 1075 &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
0c8858de 1076 return submodule_config_error("update", value);
aa13bf05 1077 sm->update_default = sm->update = (git_submodule_update_t)val;
bfc9ca59 1078 }
aa13bf05 1079 else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
0c8858de
RB
1080 if (git__parse_bool(&sm->fetch_recurse, value) < 0)
1081 return submodule_config_error("fetchRecurseSubmodules", value);
bfc9ca59 1082 }
aa13bf05 1083 else if (strcasecmp(property, "ignore") == 0) {
95dfb031
RB
1084 int val;
1085 if (git_config_lookup_map_value(
a1abe66a 1086 &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
0c8858de 1087 return submodule_config_error("ignore", value);
aa13bf05 1088 sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
bfc9ca59
RB
1089 }
1090 /* ignore other unknown submodule properties */
1091
1092 return 0;
bfc9ca59
RB
1093}
1094
aa13bf05
RB
1095static int submodule_load_from_wd_lite(
1096 git_submodule *sm, const char *name, void *payload)
1097{
1098 git_repository *repo = git_submodule_owner(sm);
1099 git_buf path = GIT_BUF_INIT;
1100
1101 GIT_UNUSED(name);
1102 GIT_UNUSED(payload);
1103
1104 if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
1105 return -1;
1106
1107 if (git_path_isdir(path.ptr))
1108 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1109
1110 if (git_path_contains(&path, DOT_GIT))
1111 sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
1112
1113 git_buf_free(&path);
1114
1115 return 0;
1116}
1117
5f4a61ae
RB
1118static void submodule_mode_mismatch(
1119 git_repository *repo, const char *path, unsigned int flag)
1120{
1121 khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
1122
1123 if (git_strmap_valid_index(repo->submodules, pos)) {
1124 git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
1125
1126 sm->flags |= flag;
1127 }
1128}
1129
aa13bf05
RB
1130static int load_submodule_config_from_index(
1131 git_repository *repo, git_oid *gitmodules_oid)
bfc9ca59
RB
1132{
1133 int error;
4b181037 1134 git_index *index;
aa13bf05
RB
1135 git_iterator *i;
1136 const git_index_entry *entry;
bfc9ca59 1137
4b181037 1138 if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
169dc616 1139 (error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0)
aa13bf05 1140 return error;
bfc9ca59 1141
169dc616 1142 error = git_iterator_current(&entry, i);
bfc9ca59 1143
aa13bf05 1144 while (!error && entry != NULL) {
bfc9ca59 1145
bfc9ca59 1146 if (S_ISGITLINK(entry->mode)) {
aa13bf05
RB
1147 error = submodule_load_from_index(repo, entry);
1148 if (error < 0)
1149 break;
5f4a61ae
RB
1150 } else {
1151 submodule_mode_mismatch(
1152 repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
1153
1154 if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
1155 git_oid_cpy(gitmodules_oid, &entry->oid);
1156 }
aa13bf05 1157
169dc616 1158 error = git_iterator_advance(&entry, i);
bfc9ca59
RB
1159 }
1160
aa13bf05
RB
1161 git_iterator_free(i);
1162
1163 return error;
1164}
1165
1166static int load_submodule_config_from_head(
1167 git_repository *repo, git_oid *gitmodules_oid)
1168{
1169 int error;
1170 git_tree *head;
1171 git_iterator *i;
1172 const git_index_entry *entry;
1173
1174 if ((error = git_repository_head_tree(&head, repo)) < 0)
1175 return error;
1176
169dc616 1177 if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
aa13bf05
RB
1178 git_tree_free(head);
1179 return error;
1180 }
1181
169dc616 1182 error = git_iterator_current(&entry, i);
aa13bf05
RB
1183
1184 while (!error && entry != NULL) {
1185
1186 if (S_ISGITLINK(entry->mode)) {
1187 error = submodule_load_from_head(repo, entry->path, &entry->oid);
1188 if (error < 0)
1189 break;
5f4a61ae
RB
1190 } else {
1191 submodule_mode_mismatch(
1192 repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
1193
1194 if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
1195 git_oid_iszero(gitmodules_oid))
1196 git_oid_cpy(gitmodules_oid, &entry->oid);
1197 }
aa13bf05 1198
169dc616 1199 error = git_iterator_advance(&entry, i);
aa13bf05
RB
1200 }
1201
1202 git_iterator_free(i);
1203 git_tree_free(head);
1204
1205 return error;
1206}
1207
54b2a37a 1208static git_config_backend *open_gitmodules(
aa13bf05
RB
1209 git_repository *repo,
1210 bool okay_to_create,
1211 const git_oid *gitmodules_oid)
1212{
1213 const char *workdir = git_repository_workdir(repo);
1214 git_buf path = GIT_BUF_INIT;
54b2a37a 1215 git_config_backend *mods = NULL;
aa13bf05
RB
1216
1217 if (workdir != NULL) {
1218 if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1219 return NULL;
1220
1221 if (okay_to_create || git_path_isfile(path.ptr)) {
1222 /* git_config_file__ondisk should only fail if OOM */
1223 if (git_config_file__ondisk(&mods, path.ptr) < 0)
0c8858de 1224 mods = NULL;
aa13bf05 1225 /* open should only fail here if the file is malformed */
a1abe66a 1226 else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
aa13bf05
RB
1227 git_config_file_free(mods);
1228 mods = NULL;
1229 }
bfc9ca59 1230 }
bfc9ca59
RB
1231 }
1232
aa13bf05
RB
1233 if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
1234 /* TODO: Retrieve .gitmodules content from ODB */
1235
1236 /* Should we actually do this? Core git does not, but it means you
1237 * can't really get much information about submodules on bare repos.
1238 */
1239 }
1240
0c8858de
RB
1241 git_buf_free(&path);
1242
aa13bf05
RB
1243 return mods;
1244}
1245
a9a73007 1246static int load_submodule_config(git_repository *repo)
aa13bf05
RB
1247{
1248 int error;
1249 git_oid gitmodules_oid;
1250 git_buf path = GIT_BUF_INIT;
54b2a37a 1251 git_config_backend *mods = NULL;
aa13bf05 1252
a9a73007 1253 if (repo->submodules)
aa13bf05
RB
1254 return 0;
1255
1256 memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
1257
1258 /* Submodule data is kept in a hashtable keyed by both name and path.
1259 * These are usually the same, but that is not guaranteed.
1260 */
1261 if (!repo->submodules) {
1262 repo->submodules = git_strmap_alloc();
1263 GITERR_CHECK_ALLOC(repo->submodules);
bfc9ca59
RB
1264 }
1265
aa13bf05
RB
1266 /* add submodule information from index */
1267
1268 if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
1269 goto cleanup;
1270
1271 /* add submodule information from HEAD */
1272
1273 if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
1274 goto cleanup;
1275
1276 /* add submodule information from .gitmodules */
1277
1278 if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
1279 error = git_config_file_foreach(mods, submodule_load_from_config, repo);
1280
1281 if (error != 0)
1282 goto cleanup;
1283
1284 /* shallow scan submodules in work tree */
bfc9ca59 1285
aa13bf05
RB
1286 if (!git_repository_is_bare(repo))
1287 error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
bfc9ca59
RB
1288
1289cleanup:
aa13bf05
RB
1290 git_buf_free(&path);
1291
bfc9ca59
RB
1292 if (mods != NULL)
1293 git_config_file_free(mods);
aa13bf05 1294
bfc9ca59 1295 if (error)
aa13bf05
RB
1296 git_submodule_config_free(repo);
1297
bfc9ca59
RB
1298 return error;
1299}
1300
aa13bf05 1301static int lookup_head_remote(git_buf *url, git_repository *repo)
bfc9ca59 1302{
aa13bf05
RB
1303 int error;
1304 git_config *cfg;
1305 git_reference *head = NULL, *remote = NULL;
1306 const char *tgt, *scan;
1307 git_buf key = GIT_BUF_INIT;
1308
1309 /* 1. resolve HEAD -> refs/heads/BRANCH
1310 * 2. lookup config branch.BRANCH.remote -> ORIGIN
1311 * 3. lookup remote.ORIGIN.url
1312 */
bfc9ca59 1313
aa13bf05
RB
1314 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1315 return error;
bfc9ca59 1316
aa13bf05
RB
1317 if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
1318 giterr_set(GITERR_SUBMODULE,
1319 "Cannot resolve relative URL when HEAD cannot be resolved");
1320 error = GIT_ENOTFOUND;
1321 goto cleanup;
1322 }
bfc9ca59 1323
aa13bf05
RB
1324 if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
1325 giterr_set(GITERR_SUBMODULE,
1326 "Cannot resolve relative URL when HEAD is not symbolic");
1327 error = GIT_ENOTFOUND;
1328 goto cleanup;
1329 }
1330
a258d8e3 1331 if ((error = git_branch_upstream(&remote, head)) < 0)
aa13bf05
RB
1332 goto cleanup;
1333
1334 /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
1335
1336 if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
2508cc66 1337 git__prefixcmp(git_reference_symbolic_target(remote), GIT_REFS_REMOTES_DIR) != 0)
aa13bf05
RB
1338 {
1339 giterr_set(GITERR_SUBMODULE,
1340 "Cannot resolve relative URL when HEAD is not symbolic");
1341 error = GIT_ENOTFOUND;
1342 goto cleanup;
1343 }
1344
2508cc66 1345 scan = tgt = git_reference_symbolic_target(remote) + strlen(GIT_REFS_REMOTES_DIR);
aa13bf05
RB
1346 while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
1347 scan++; /* find non-escaped slash to end ORIGIN name */
1348
1349 error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
1350 if (error < 0)
1351 goto cleanup;
1352
1353 if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
1354 goto cleanup;
1355
1356 error = git_buf_sets(url, tgt);
1357
1358cleanup:
1359 git_buf_free(&key);
1360 git_reference_free(head);
1361 git_reference_free(remote);
1362
1363 return error;
bfc9ca59
RB
1364}
1365
aa13bf05
RB
1366static int submodule_update_config(
1367 git_submodule *submodule,
1368 const char *attr,
1369 const char *value,
1370 bool overwrite,
1371 bool only_existing)
bfc9ca59 1372{
aa13bf05
RB
1373 int error;
1374 git_config *config;
1375 git_buf key = GIT_BUF_INIT;
1376 const char *old = NULL;
1377
1378 assert(submodule);
1379
1380 error = git_repository_config__weakptr(&config, submodule->owner);
1381 if (error < 0)
1382 return error;
1383
1384 error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
1385 if (error < 0)
1386 goto cleanup;
1387
1388 if (git_config_get_string(&old, config, key.ptr) < 0)
1389 giterr_clear();
1390
1391 if (!old && only_existing)
1392 goto cleanup;
1393 if (old && !overwrite)
1394 goto cleanup;
1395 if ((!old && !value) || (old && value && strcmp(old, value) == 0))
1396 goto cleanup;
1397
1398 if (!value)
54b2a37a 1399 error = git_config_delete_entry(config, key.ptr);
aa13bf05
RB
1400 else
1401 error = git_config_set_string(config, key.ptr, value);
1402
1403cleanup:
1404 git_buf_free(&key);
1405 return error;
bfc9ca59
RB
1406}
1407
5f4a61ae 1408static int submodule_index_status(unsigned int *status, git_submodule *sm)
aa13bf05 1409{
9cd42358
RB
1410 const git_oid *head_oid = git_submodule_head_id(sm);
1411 const git_oid *index_oid = git_submodule_index_id(sm);
aa13bf05 1412
5f4a61ae
RB
1413 if (!head_oid) {
1414 if (index_oid)
1415 *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
1416 }
1417 else if (!index_oid)
1418 *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
1419 else if (!git_oid_equal(head_oid, index_oid))
1420 *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
aa13bf05 1421
5f4a61ae 1422 return 0;
aa13bf05
RB
1423}
1424
5f4a61ae 1425static int submodule_wd_status(unsigned int *status, git_submodule *sm)
bfc9ca59 1426{
5f4a61ae
RB
1427 int error = 0;
1428 const git_oid *wd_oid, *index_oid;
1429 git_repository *sm_repo = NULL;
01fed0a8 1430
9cd42358 1431 /* open repo now if we need it (so wd_id() call won't reopen) */
5f4a61ae
RB
1432 if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
1433 sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
1434 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
1435 {
1436 if ((error = git_submodule_open(&sm_repo, sm)) < 0)
1437 return error;
aa13bf05 1438 }
bfc9ca59 1439
9cd42358
RB
1440 index_oid = git_submodule_index_id(sm);
1441 wd_oid = git_submodule_wd_id(sm);
aa13bf05 1442
5f4a61ae
RB
1443 if (!index_oid) {
1444 if (wd_oid)
1445 *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
aa13bf05 1446 }
5f4a61ae
RB
1447 else if (!wd_oid) {
1448 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
1449 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
1450 *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
1451 else
1452 *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
aa13bf05 1453 }
5f4a61ae
RB
1454 else if (!git_oid_equal(index_oid, wd_oid))
1455 *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
aa13bf05 1456
5f4a61ae
RB
1457 if (sm_repo != NULL) {
1458 git_tree *sm_head;
2f8d30be 1459 git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
5f4a61ae 1460 git_diff_list *diff;
aa13bf05 1461
5f4a61ae
RB
1462 /* the diffs below could be optimized with an early termination
1463 * option to the git_diff functions, but for now this is sufficient
1464 * (and certainly no worse that what core git does).
1465 */
aa13bf05 1466
5f4a61ae 1467 /* perform head-to-index diff on submodule */
aa13bf05 1468
5f4a61ae
RB
1469 if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
1470 return error;
bfc9ca59 1471
5f4a61ae
RB
1472 if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
1473 opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
bfc9ca59 1474
56c72b75 1475 error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
aa13bf05 1476
5f4a61ae 1477 if (!error) {
5f69a31f 1478 if (git_diff_num_deltas(diff) > 0)
5f4a61ae 1479 *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
aa13bf05 1480
5f4a61ae
RB
1481 git_diff_list_free(diff);
1482 diff = NULL;
1483 }
aa13bf05 1484
5f4a61ae 1485 git_tree_free(sm_head);
bfc9ca59 1486
5f4a61ae
RB
1487 if (error < 0)
1488 return error;
aa13bf05 1489
5f4a61ae 1490 /* perform index-to-workdir diff on submodule */
aa13bf05 1491
56c72b75 1492 error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
aa13bf05 1493
5f4a61ae 1494 if (!error) {
cc5bf359 1495 size_t untracked =
5f69a31f 1496 git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
bfc9ca59 1497
5f4a61ae
RB
1498 if (untracked > 0)
1499 *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
bfc9ca59 1500
10c06114 1501 if (git_diff_num_deltas(diff) != untracked)
5f4a61ae 1502 *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
bfc9ca59 1503
5f4a61ae
RB
1504 git_diff_list_free(diff);
1505 diff = NULL;
aa13bf05 1506 }
aa13bf05 1507
5f4a61ae
RB
1508 git_repository_free(sm_repo);
1509 }
aa13bf05
RB
1510
1511 return error;
bfc9ca59 1512}