]> git.proxmox.com Git - libgit2.git/blame - src/submodule.c
Merge branch 'development' into clar2
[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
5f4a61ae 69static int load_submodule_config(git_repository *repo, bool force);
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
109 if ((error = load_submodule_config(repo, false)) < 0)
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
151 if ((error = load_submodule_config(repo, false)) < 0)
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);
711 return load_submodule_config(repo, true);
712}
713
714int git_submodule_reload(git_submodule *submodule)
715{
716 git_repository *repo;
717 git_index *index;
718 int pos, error;
719 git_tree *head;
54b2a37a 720 git_config_backend *mods;
aa13bf05
RB
721
722 assert(submodule);
723
724 /* refresh index data */
725
726 repo = submodule->owner;
727 if (git_repository_index__weakptr(&index, repo) < 0)
728 return -1;
729
5f4a61ae
RB
730 submodule->flags = submodule->flags &
731 ~(GIT_SUBMODULE_STATUS_IN_INDEX |
732 GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
733
aa13bf05
RB
734 pos = git_index_find(index, submodule->path);
735 if (pos >= 0) {
f45d51ff 736 const git_index_entry *entry = git_index_get_byindex(index, pos);
aa13bf05 737
5f4a61ae
RB
738 if (S_ISGITLINK(entry->mode)) {
739 if ((error = submodule_load_from_index(repo, entry)) < 0)
740 return error;
741 } else {
742 submodule_mode_mismatch(
743 repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
744 }
aa13bf05
RB
745 }
746
747 /* refresh HEAD tree data */
748
749 if (!(error = git_repository_head_tree(&head, repo))) {
750 git_tree_entry *te;
751
752 submodule->flags = submodule->flags &
753 ~(GIT_SUBMODULE_STATUS_IN_HEAD |
754 GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
755
756 if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
5f4a61ae
RB
757
758 if (S_ISGITLINK(te->attr)) {
759 error = submodule_load_from_head(repo, submodule->path, &te->oid);
760 } else {
761 submodule_mode_mismatch(
762 repo, submodule->path,
763 GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
764 }
aa13bf05
RB
765
766 git_tree_entry_free(te);
767 }
768 else if (error == GIT_ENOTFOUND) {
769 giterr_clear();
770 error = 0;
771 }
772
773 git_tree_free(head);
774 }
775
776 if (error < 0)
777 return error;
778
779 /* refresh config data */
780
781 if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
782 git_buf path = GIT_BUF_INIT;
783
784 git_buf_sets(&path, "submodule\\.");
7bf87ab6 785 git_buf_text_puts_escape_regex(&path, submodule->name);
aa13bf05
RB
786 git_buf_puts(&path, ".*");
787
788 if (git_buf_oom(&path))
789 error = -1;
790 else
791 error = git_config_file_foreach_match(
792 mods, path.ptr, submodule_load_from_config, repo);
793
794 git_buf_free(&path);
0c8858de 795 git_config_file_free(mods);
aa13bf05
RB
796 }
797
5f4a61ae
RB
798 if (error < 0)
799 return error;
800
801 /* refresh wd data */
802
803 submodule->flags = submodule->flags &
804 ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
805
806 error = submodule_load_from_wd_lite(submodule, submodule->path, NULL);
807
aa13bf05
RB
808 return error;
809}
810
811int git_submodule_status(
812 unsigned int *status,
813 git_submodule *submodule)
814{
5f4a61ae
RB
815 int error = 0;
816 unsigned int status_val;
817
aa13bf05
RB
818 assert(status && submodule);
819
5f4a61ae 820 status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
0c8858de 821
5f4a61ae
RB
822 if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
823 if (!(error = submodule_index_status(&status_val, submodule)))
824 error = submodule_wd_status(&status_val, submodule);
825 }
aa13bf05 826
5f4a61ae 827 *status = status_val;
aa13bf05 828
5f4a61ae 829 return error;
aa13bf05
RB
830}
831
832/*
833 * INTERNAL FUNCTIONS
834 */
835
836static git_submodule *submodule_alloc(git_repository *repo, const char *name)
bfc9ca59 837{
0c8858de 838 git_submodule *sm;
bfc9ca59 839
0c8858de
RB
840 if (!name || !strlen(name)) {
841 giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
bfc9ca59
RB
842 return NULL;
843 }
844
0c8858de
RB
845 sm = git__calloc(1, sizeof(git_submodule));
846 if (sm == NULL)
847 goto fail;
848
849 sm->path = sm->name = git__strdup(name);
850 if (!sm->name)
851 goto fail;
852
aa13bf05 853 sm->owner = repo;
0c8858de 854 sm->refcount = 1;
aa13bf05
RB
855
856 return sm;
0c8858de
RB
857
858fail:
859 submodule_release(sm, 0);
860 return NULL;
aa13bf05
RB
861}
862
863static void submodule_release(git_submodule *sm, int decr)
864{
865 if (!sm)
866 return;
867
868 sm->refcount -= decr;
869
870 if (sm->refcount == 0) {
871 if (sm->name != sm->path) {
872 git__free(sm->path);
873 sm->path = NULL;
874 }
875
876 git__free(sm->name);
877 sm->name = NULL;
878
879 git__free(sm->url);
880 sm->url = NULL;
881
882 sm->owner = NULL;
883
884 git__free(sm);
885 }
886}
887
0c8858de
RB
888static int submodule_get(
889 git_submodule **sm_ptr,
890 git_repository *repo,
891 const char *name,
892 const char *alternate)
aa13bf05
RB
893{
894 git_strmap *smcfg = repo->submodules;
895 khiter_t pos;
896 git_submodule *sm;
0c8858de 897 int error;
aa13bf05 898
0c8858de 899 assert(repo && name);
aa13bf05 900
0c8858de 901 pos = git_strmap_lookup_index(smcfg, name);
aa13bf05 902
0c8858de
RB
903 if (!git_strmap_valid_index(smcfg, pos) && alternate)
904 pos = git_strmap_lookup_index(smcfg, alternate);
aa13bf05 905
0c8858de
RB
906 if (!git_strmap_valid_index(smcfg, pos)) {
907 sm = submodule_alloc(repo, name);
aa13bf05 908
0c8858de
RB
909 /* insert value at name - if another thread beats us to it, then use
910 * their record and release our own.
911 */
5f4a61ae 912 pos = kh_put(str, smcfg, sm->name, &error);
0c8858de
RB
913
914 if (error < 0) {
915 submodule_release(sm, 1);
916 sm = NULL;
917 } else if (error == 0) {
918 submodule_release(sm, 1);
919 sm = git_strmap_value_at(smcfg, pos);
920 } else {
921 git_strmap_set_value_at(smcfg, pos, sm);
922 }
923 } else {
924 sm = git_strmap_value_at(smcfg, pos);
aa13bf05
RB
925 }
926
0c8858de 927 *sm_ptr = sm;
aa13bf05 928
0c8858de 929 return (sm != NULL) ? 0 : -1;
bfc9ca59
RB
930}
931
aa13bf05
RB
932static int submodule_load_from_index(
933 git_repository *repo, const git_index_entry *entry)
bfc9ca59 934{
0c8858de 935 git_submodule *sm;
bfc9ca59 936
0c8858de 937 if (submodule_get(&sm, repo, entry->path, NULL) < 0)
aa13bf05 938 return -1;
bfc9ca59 939
aa13bf05
RB
940 if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
941 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
942 return 0;
bfc9ca59 943 }
bfc9ca59 944
aa13bf05 945 sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
01fed0a8 946
aa13bf05
RB
947 git_oid_cpy(&sm->index_oid, &entry->oid);
948 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
bfc9ca59 949
0c8858de 950 return 0;
aa13bf05 951}
bfc9ca59 952
aa13bf05
RB
953static int submodule_load_from_head(
954 git_repository *repo, const char *path, const git_oid *oid)
955{
0c8858de 956 git_submodule *sm;
bfc9ca59 957
0c8858de 958 if (submodule_get(&sm, repo, path, NULL) < 0)
aa13bf05 959 return -1;
bfc9ca59 960
aa13bf05 961 sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
bfc9ca59 962
aa13bf05
RB
963 git_oid_cpy(&sm->head_oid, oid);
964 sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
bfc9ca59 965
0c8858de
RB
966 return 0;
967}
968
969static int submodule_config_error(const char *property, const char *value)
970{
971 giterr_set(GITERR_INVALID,
972 "Invalid value for submodule '%s' property: '%s'", property, value);
973 return -1;
bfc9ca59
RB
974}
975
aa13bf05 976static int submodule_load_from_config(
a1abe66a 977 const git_config_entry *entry, void *data)
bfc9ca59 978{
aa13bf05
RB
979 git_repository *repo = data;
980 git_strmap *smcfg = repo->submodules;
0c8858de 981 const char *namestart, *property, *alternate = NULL;
a1abe66a 982 const char *key = entry->name, *value = entry->value;
bfc9ca59
RB
983 git_buf name = GIT_BUF_INIT;
984 git_submodule *sm;
bfc9ca59 985 bool is_path;
0c8858de 986 int error = 0;
bfc9ca59
RB
987
988 if (git__prefixcmp(key, "submodule.") != 0)
989 return 0;
990
991 namestart = key + strlen("submodule.");
992 property = strrchr(namestart, '.');
993 if (property == NULL)
994 return 0;
995 property++;
aa13bf05 996 is_path = (strcasecmp(property, "path") == 0);
bfc9ca59
RB
997
998 if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
999 return -1;
1000
0c8858de
RB
1001 if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
1002 git_buf_free(&name);
1003 return -1;
1004 }
bfc9ca59 1005
aa13bf05
RB
1006 sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
1007
0c8858de
RB
1008 /* Only from config might we get differing names & paths. If so, then
1009 * update the submodule and insert under the alternative key.
1010 */
01fed0a8 1011
0c8858de
RB
1012 /* TODO: if case insensitive filesystem, then the following strcmps
1013 * should be strcasecmp
1014 */
01fed0a8 1015
0c8858de
RB
1016 if (strcmp(sm->name, name.ptr) != 0) {
1017 alternate = sm->name = git_buf_detach(&name);
1018 } else if (is_path && value && strcmp(sm->path, value) != 0) {
1019 alternate = sm->path = git__strdup(value);
1020 if (!sm->path)
1021 error = -1;
bfc9ca59 1022 }
0c8858de
RB
1023 if (alternate) {
1024 void *old_sm = NULL;
1025 git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
bfc9ca59 1026
0c8858de
RB
1027 if (error >= 0)
1028 sm->refcount++; /* inserted under a new key */
1029
1030 /* if we replaced an old module under this key, release the old one */
1031 if (old_sm && ((git_submodule *)old_sm) != sm) {
1032 submodule_release(old_sm, 1);
1033 /* TODO: log warning about multiple submodules with same path */
1034 }
bfc9ca59
RB
1035 }
1036
0c8858de
RB
1037 git_buf_free(&name);
1038 if (error < 0)
1039 return error;
1040
aa13bf05
RB
1041 /* TODO: Look up path in index and if it is present but not a GITLINK
1042 * then this should be deleted (at least to match git's behavior)
1043 */
1044
bfc9ca59
RB
1045 if (is_path)
1046 return 0;
1047
1048 /* copy other properties into submodule entry */
aa13bf05 1049 if (strcasecmp(property, "url") == 0) {
0c8858de
RB
1050 git__free(sm->url);
1051 sm->url = NULL;
1052
aa13bf05 1053 if (value != NULL && (sm->url = git__strdup(value)) == NULL)
0c8858de 1054 return -1;
bfc9ca59 1055 }
aa13bf05 1056 else if (strcasecmp(property, "update") == 0) {
95dfb031
RB
1057 int val;
1058 if (git_config_lookup_map_value(
a1abe66a 1059 &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
0c8858de 1060 return submodule_config_error("update", value);
aa13bf05 1061 sm->update_default = sm->update = (git_submodule_update_t)val;
bfc9ca59 1062 }
aa13bf05 1063 else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
0c8858de
RB
1064 if (git__parse_bool(&sm->fetch_recurse, value) < 0)
1065 return submodule_config_error("fetchRecurseSubmodules", value);
bfc9ca59 1066 }
aa13bf05 1067 else if (strcasecmp(property, "ignore") == 0) {
95dfb031
RB
1068 int val;
1069 if (git_config_lookup_map_value(
a1abe66a 1070 &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
0c8858de 1071 return submodule_config_error("ignore", value);
aa13bf05 1072 sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
bfc9ca59
RB
1073 }
1074 /* ignore other unknown submodule properties */
1075
1076 return 0;
bfc9ca59
RB
1077}
1078
aa13bf05
RB
1079static int submodule_load_from_wd_lite(
1080 git_submodule *sm, const char *name, void *payload)
1081{
1082 git_repository *repo = git_submodule_owner(sm);
1083 git_buf path = GIT_BUF_INIT;
1084
1085 GIT_UNUSED(name);
1086 GIT_UNUSED(payload);
1087
1088 if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
1089 return -1;
1090
1091 if (git_path_isdir(path.ptr))
1092 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1093
1094 if (git_path_contains(&path, DOT_GIT))
1095 sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
1096
1097 git_buf_free(&path);
1098
1099 return 0;
1100}
1101
5f4a61ae
RB
1102static void submodule_mode_mismatch(
1103 git_repository *repo, const char *path, unsigned int flag)
1104{
1105 khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
1106
1107 if (git_strmap_valid_index(repo->submodules, pos)) {
1108 git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
1109
1110 sm->flags |= flag;
1111 }
1112}
1113
aa13bf05
RB
1114static int load_submodule_config_from_index(
1115 git_repository *repo, git_oid *gitmodules_oid)
bfc9ca59
RB
1116{
1117 int error;
aa13bf05
RB
1118 git_iterator *i;
1119 const git_index_entry *entry;
bfc9ca59 1120
bad68c0a 1121 if ((error = git_iterator_for_repo_index(&i, repo)) < 0)
aa13bf05 1122 return error;
bfc9ca59 1123
aa13bf05 1124 error = git_iterator_current(i, &entry);
bfc9ca59 1125
aa13bf05 1126 while (!error && entry != NULL) {
bfc9ca59 1127
bfc9ca59 1128 if (S_ISGITLINK(entry->mode)) {
aa13bf05
RB
1129 error = submodule_load_from_index(repo, entry);
1130 if (error < 0)
1131 break;
5f4a61ae
RB
1132 } else {
1133 submodule_mode_mismatch(
1134 repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
1135
1136 if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
1137 git_oid_cpy(gitmodules_oid, &entry->oid);
1138 }
aa13bf05
RB
1139
1140 error = git_iterator_advance(i, &entry);
bfc9ca59
RB
1141 }
1142
aa13bf05
RB
1143 git_iterator_free(i);
1144
1145 return error;
1146}
1147
1148static int load_submodule_config_from_head(
1149 git_repository *repo, git_oid *gitmodules_oid)
1150{
1151 int error;
1152 git_tree *head;
1153 git_iterator *i;
1154 const git_index_entry *entry;
1155
1156 if ((error = git_repository_head_tree(&head, repo)) < 0)
1157 return error;
1158
9950d27a 1159 if ((error = git_iterator_for_tree(&i, head)) < 0) {
aa13bf05
RB
1160 git_tree_free(head);
1161 return error;
1162 }
1163
1164 error = git_iterator_current(i, &entry);
1165
1166 while (!error && entry != NULL) {
1167
1168 if (S_ISGITLINK(entry->mode)) {
1169 error = submodule_load_from_head(repo, entry->path, &entry->oid);
1170 if (error < 0)
1171 break;
5f4a61ae
RB
1172 } else {
1173 submodule_mode_mismatch(
1174 repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
1175
1176 if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
1177 git_oid_iszero(gitmodules_oid))
1178 git_oid_cpy(gitmodules_oid, &entry->oid);
1179 }
aa13bf05
RB
1180
1181 error = git_iterator_advance(i, &entry);
1182 }
1183
1184 git_iterator_free(i);
1185 git_tree_free(head);
1186
1187 return error;
1188}
1189
54b2a37a 1190static git_config_backend *open_gitmodules(
aa13bf05
RB
1191 git_repository *repo,
1192 bool okay_to_create,
1193 const git_oid *gitmodules_oid)
1194{
1195 const char *workdir = git_repository_workdir(repo);
1196 git_buf path = GIT_BUF_INIT;
54b2a37a 1197 git_config_backend *mods = NULL;
aa13bf05
RB
1198
1199 if (workdir != NULL) {
1200 if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1201 return NULL;
1202
1203 if (okay_to_create || git_path_isfile(path.ptr)) {
1204 /* git_config_file__ondisk should only fail if OOM */
1205 if (git_config_file__ondisk(&mods, path.ptr) < 0)
0c8858de 1206 mods = NULL;
aa13bf05 1207 /* open should only fail here if the file is malformed */
a1abe66a 1208 else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
aa13bf05
RB
1209 git_config_file_free(mods);
1210 mods = NULL;
1211 }
bfc9ca59 1212 }
bfc9ca59
RB
1213 }
1214
aa13bf05
RB
1215 if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
1216 /* TODO: Retrieve .gitmodules content from ODB */
1217
1218 /* Should we actually do this? Core git does not, but it means you
1219 * can't really get much information about submodules on bare repos.
1220 */
1221 }
1222
0c8858de
RB
1223 git_buf_free(&path);
1224
aa13bf05
RB
1225 return mods;
1226}
1227
1228static int load_submodule_config(git_repository *repo, bool force)
1229{
1230 int error;
1231 git_oid gitmodules_oid;
1232 git_buf path = GIT_BUF_INIT;
54b2a37a 1233 git_config_backend *mods = NULL;
aa13bf05
RB
1234
1235 if (repo->submodules && !force)
1236 return 0;
1237
1238 memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
1239
1240 /* Submodule data is kept in a hashtable keyed by both name and path.
1241 * These are usually the same, but that is not guaranteed.
1242 */
1243 if (!repo->submodules) {
1244 repo->submodules = git_strmap_alloc();
1245 GITERR_CHECK_ALLOC(repo->submodules);
bfc9ca59
RB
1246 }
1247
aa13bf05
RB
1248 /* add submodule information from index */
1249
1250 if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
1251 goto cleanup;
1252
1253 /* add submodule information from HEAD */
1254
1255 if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
1256 goto cleanup;
1257
1258 /* add submodule information from .gitmodules */
1259
1260 if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
1261 error = git_config_file_foreach(mods, submodule_load_from_config, repo);
1262
1263 if (error != 0)
1264 goto cleanup;
1265
1266 /* shallow scan submodules in work tree */
bfc9ca59 1267
aa13bf05
RB
1268 if (!git_repository_is_bare(repo))
1269 error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
bfc9ca59
RB
1270
1271cleanup:
aa13bf05
RB
1272 git_buf_free(&path);
1273
bfc9ca59
RB
1274 if (mods != NULL)
1275 git_config_file_free(mods);
aa13bf05 1276
bfc9ca59 1277 if (error)
aa13bf05
RB
1278 git_submodule_config_free(repo);
1279
bfc9ca59
RB
1280 return error;
1281}
1282
aa13bf05 1283static int lookup_head_remote(git_buf *url, git_repository *repo)
bfc9ca59 1284{
aa13bf05
RB
1285 int error;
1286 git_config *cfg;
1287 git_reference *head = NULL, *remote = NULL;
1288 const char *tgt, *scan;
1289 git_buf key = GIT_BUF_INIT;
1290
1291 /* 1. resolve HEAD -> refs/heads/BRANCH
1292 * 2. lookup config branch.BRANCH.remote -> ORIGIN
1293 * 3. lookup remote.ORIGIN.url
1294 */
bfc9ca59 1295
aa13bf05
RB
1296 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1297 return error;
bfc9ca59 1298
aa13bf05
RB
1299 if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
1300 giterr_set(GITERR_SUBMODULE,
1301 "Cannot resolve relative URL when HEAD cannot be resolved");
1302 error = GIT_ENOTFOUND;
1303 goto cleanup;
1304 }
bfc9ca59 1305
aa13bf05
RB
1306 if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
1307 giterr_set(GITERR_SUBMODULE,
1308 "Cannot resolve relative URL when HEAD is not symbolic");
1309 error = GIT_ENOTFOUND;
1310 goto cleanup;
1311 }
1312
1313 if ((error = git_branch_tracking(&remote, head)) < 0)
1314 goto cleanup;
1315
1316 /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
1317
1318 if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
2508cc66 1319 git__prefixcmp(git_reference_symbolic_target(remote), GIT_REFS_REMOTES_DIR) != 0)
aa13bf05
RB
1320 {
1321 giterr_set(GITERR_SUBMODULE,
1322 "Cannot resolve relative URL when HEAD is not symbolic");
1323 error = GIT_ENOTFOUND;
1324 goto cleanup;
1325 }
1326
2508cc66 1327 scan = tgt = git_reference_symbolic_target(remote) + strlen(GIT_REFS_REMOTES_DIR);
aa13bf05
RB
1328 while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
1329 scan++; /* find non-escaped slash to end ORIGIN name */
1330
1331 error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
1332 if (error < 0)
1333 goto cleanup;
1334
1335 if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
1336 goto cleanup;
1337
1338 error = git_buf_sets(url, tgt);
1339
1340cleanup:
1341 git_buf_free(&key);
1342 git_reference_free(head);
1343 git_reference_free(remote);
1344
1345 return error;
bfc9ca59
RB
1346}
1347
aa13bf05
RB
1348static int submodule_update_config(
1349 git_submodule *submodule,
1350 const char *attr,
1351 const char *value,
1352 bool overwrite,
1353 bool only_existing)
bfc9ca59 1354{
aa13bf05
RB
1355 int error;
1356 git_config *config;
1357 git_buf key = GIT_BUF_INIT;
1358 const char *old = NULL;
1359
1360 assert(submodule);
1361
1362 error = git_repository_config__weakptr(&config, submodule->owner);
1363 if (error < 0)
1364 return error;
1365
1366 error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
1367 if (error < 0)
1368 goto cleanup;
1369
1370 if (git_config_get_string(&old, config, key.ptr) < 0)
1371 giterr_clear();
1372
1373 if (!old && only_existing)
1374 goto cleanup;
1375 if (old && !overwrite)
1376 goto cleanup;
1377 if ((!old && !value) || (old && value && strcmp(old, value) == 0))
1378 goto cleanup;
1379
1380 if (!value)
54b2a37a 1381 error = git_config_delete_entry(config, key.ptr);
aa13bf05
RB
1382 else
1383 error = git_config_set_string(config, key.ptr, value);
1384
1385cleanup:
1386 git_buf_free(&key);
1387 return error;
bfc9ca59
RB
1388}
1389
5f4a61ae 1390static int submodule_index_status(unsigned int *status, git_submodule *sm)
aa13bf05 1391{
9cd42358
RB
1392 const git_oid *head_oid = git_submodule_head_id(sm);
1393 const git_oid *index_oid = git_submodule_index_id(sm);
aa13bf05 1394
5f4a61ae
RB
1395 if (!head_oid) {
1396 if (index_oid)
1397 *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
1398 }
1399 else if (!index_oid)
1400 *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
1401 else if (!git_oid_equal(head_oid, index_oid))
1402 *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
aa13bf05 1403
5f4a61ae 1404 return 0;
aa13bf05
RB
1405}
1406
5f4a61ae 1407static int submodule_wd_status(unsigned int *status, git_submodule *sm)
bfc9ca59 1408{
5f4a61ae
RB
1409 int error = 0;
1410 const git_oid *wd_oid, *index_oid;
1411 git_repository *sm_repo = NULL;
01fed0a8 1412
9cd42358 1413 /* open repo now if we need it (so wd_id() call won't reopen) */
5f4a61ae
RB
1414 if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
1415 sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
1416 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
1417 {
1418 if ((error = git_submodule_open(&sm_repo, sm)) < 0)
1419 return error;
aa13bf05 1420 }
bfc9ca59 1421
9cd42358
RB
1422 index_oid = git_submodule_index_id(sm);
1423 wd_oid = git_submodule_wd_id(sm);
aa13bf05 1424
5f4a61ae
RB
1425 if (!index_oid) {
1426 if (wd_oid)
1427 *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
aa13bf05 1428 }
5f4a61ae
RB
1429 else if (!wd_oid) {
1430 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
1431 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
1432 *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
1433 else
1434 *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
aa13bf05 1435 }
5f4a61ae
RB
1436 else if (!git_oid_equal(index_oid, wd_oid))
1437 *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
aa13bf05 1438
5f4a61ae
RB
1439 if (sm_repo != NULL) {
1440 git_tree *sm_head;
2f8d30be 1441 git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
5f4a61ae 1442 git_diff_list *diff;
aa13bf05 1443
5f4a61ae
RB
1444 /* the diffs below could be optimized with an early termination
1445 * option to the git_diff functions, but for now this is sufficient
1446 * (and certainly no worse that what core git does).
1447 */
aa13bf05 1448
5f4a61ae 1449 /* perform head-to-index diff on submodule */
aa13bf05 1450
5f4a61ae
RB
1451 if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
1452 return error;
bfc9ca59 1453
5f4a61ae
RB
1454 if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
1455 opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
bfc9ca59 1456
56c72b75 1457 error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
aa13bf05 1458
5f4a61ae 1459 if (!error) {
5f69a31f 1460 if (git_diff_num_deltas(diff) > 0)
5f4a61ae 1461 *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
aa13bf05 1462
5f4a61ae
RB
1463 git_diff_list_free(diff);
1464 diff = NULL;
1465 }
aa13bf05 1466
5f4a61ae 1467 git_tree_free(sm_head);
bfc9ca59 1468
5f4a61ae
RB
1469 if (error < 0)
1470 return error;
aa13bf05 1471
5f4a61ae 1472 /* perform index-to-workdir diff on submodule */
aa13bf05 1473
56c72b75 1474 error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
aa13bf05 1475
5f4a61ae 1476 if (!error) {
cc5bf359 1477 size_t untracked =
5f69a31f 1478 git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
bfc9ca59 1479
5f4a61ae
RB
1480 if (untracked > 0)
1481 *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
bfc9ca59 1482
5f69a31f 1483 if ((git_diff_num_deltas(diff) - untracked) > 0)
5f4a61ae 1484 *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
bfc9ca59 1485
5f4a61ae
RB
1486 git_diff_list_free(diff);
1487 diff = NULL;
aa13bf05 1488 }
aa13bf05 1489
5f4a61ae
RB
1490 git_repository_free(sm_repo);
1491 }
aa13bf05
RB
1492
1493 return error;
bfc9ca59 1494}