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