]> git.proxmox.com Git - libgit2.git/blob - src/submodule.c
Merge pull request #906 from nulltoken/topic/git_reference_peel
[libgit2.git] / src / submodule.c
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"
15 #include "vector.h"
16 #include "posix.h"
17 #include "config_file.h"
18 #include "config.h"
19 #include "repository.h"
20 #include "submodule.h"
21 #include "tree.h"
22 #include "iterator.h"
23
24 #define GIT_MODULES_FILE ".gitmodules"
25
26 static git_cvar_map _sm_update_map[] = {
27 {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
28 {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
29 {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
30 {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
31 };
32
33 static git_cvar_map _sm_ignore_map[] = {
34 {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
35 {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
36 {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
37 {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
38 };
39
40 static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
41 {
42 khint_t h;
43
44 for (h = 0; *s; ++s)
45 if (s[1] != '\0' || *s != '/')
46 h = (h << 5) - h + *s;
47
48 return h;
49 }
50
51 static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
52 {
53 size_t alen = a ? strlen(a) : 0;
54 size_t blen = b ? strlen(b) : 0;
55
56 if (alen > 0 && a[alen - 1] == '/')
57 alen--;
58 if (blen > 0 && b[blen - 1] == '/')
59 blen--;
60
61 return (alen == blen && strncmp(a, b, alen) == 0);
62 }
63
64 __KHASH_IMPL(
65 str, static kh_inline, const char *, void *, 1,
66 str_hash_no_trailing_slash, str_equal_no_trailing_slash);
67
68 static int load_submodule_config(git_repository *repo, bool force);
69 static git_config_file *open_gitmodules(git_repository *, bool, const git_oid *);
70 static int lookup_head_remote(git_buf *url, git_repository *repo);
71 static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
72 static void submodule_release(git_submodule *sm, int decr);
73 static int submodule_load_from_index(git_repository *, const git_index_entry *);
74 static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
75 static int submodule_load_from_config(const char *, const char *, void *);
76 static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
77 static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
78 static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
79 static int submodule_index_status(unsigned int *status, git_submodule *sm);
80 static int submodule_wd_status(unsigned int *status, git_submodule *sm);
81
82 static int submodule_cmp(const void *a, const void *b)
83 {
84 return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
85 }
86
87 static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
88 {
89 ssize_t idx = git_buf_rfind(key, '.');
90 git_buf_truncate(key, (size_t)(idx + 1));
91 return git_buf_puts(key, suffix);
92 }
93
94 /*
95 * PUBLIC APIS
96 */
97
98 int git_submodule_lookup(
99 git_submodule **sm_ptr, /* NULL if user only wants to test existence */
100 git_repository *repo,
101 const char *name) /* trailing slash is allowed */
102 {
103 int error;
104 khiter_t pos;
105
106 assert(repo && name);
107
108 if ((error = load_submodule_config(repo, false)) < 0)
109 return error;
110
111 pos = git_strmap_lookup_index(repo->submodules, name);
112
113 if (!git_strmap_valid_index(repo->submodules, pos)) {
114 error = GIT_ENOTFOUND;
115
116 /* check if a plausible submodule exists at path */
117 if (git_repository_workdir(repo)) {
118 git_buf path = GIT_BUF_INIT;
119
120 if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0)
121 return -1;
122
123 if (git_path_contains_dir(&path, DOT_GIT))
124 error = GIT_EEXISTS;
125
126 git_buf_free(&path);
127 }
128
129 return error;
130 }
131
132 if (sm_ptr)
133 *sm_ptr = git_strmap_value_at(repo->submodules, pos);
134
135 return 0;
136 }
137
138 int git_submodule_foreach(
139 git_repository *repo,
140 int (*callback)(git_submodule *sm, const char *name, void *payload),
141 void *payload)
142 {
143 int error;
144 git_submodule *sm;
145 git_vector seen = GIT_VECTOR_INIT;
146 seen._cmp = submodule_cmp;
147
148 assert(repo && callback);
149
150 if ((error = load_submodule_config(repo, false)) < 0)
151 return error;
152
153 git_strmap_foreach_value(repo->submodules, sm, {
154 /* Usually the following will not come into play - it just prevents
155 * us from issuing a callback twice for a submodule where the name
156 * and path are not the same.
157 */
158 if (sm->refcount > 1) {
159 if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
160 continue;
161 if ((error = git_vector_insert(&seen, sm)) < 0)
162 break;
163 }
164
165 if (callback(sm, sm->name, payload)) {
166 giterr_clear();
167 error = GIT_EUSER;
168 break;
169 }
170 });
171
172 git_vector_free(&seen);
173
174 return error;
175 }
176
177 void git_submodule_config_free(git_repository *repo)
178 {
179 git_strmap *smcfg;
180 git_submodule *sm;
181
182 assert(repo);
183
184 smcfg = repo->submodules;
185 repo->submodules = NULL;
186
187 if (smcfg == NULL)
188 return;
189
190 git_strmap_foreach_value(smcfg, sm, {
191 submodule_release(sm,1);
192 });
193 git_strmap_free(smcfg);
194 }
195
196 int git_submodule_add_setup(
197 git_submodule **submodule,
198 git_repository *repo,
199 const char *url,
200 const char *path,
201 int use_gitlink)
202 {
203 int error = 0;
204 git_config_file *mods = NULL;
205 git_submodule *sm;
206 git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
207 git_repository_init_options initopt;
208 git_repository *subrepo = NULL;
209
210 assert(repo && url && path);
211
212 /* see if there is already an entry for this submodule */
213
214 if (git_submodule_lookup(&sm, repo, path) < 0)
215 giterr_clear();
216 else {
217 giterr_set(GITERR_SUBMODULE,
218 "Attempt to add a submodule that already exists");
219 return GIT_EEXISTS;
220 }
221
222 /* resolve parameters */
223
224 if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) {
225 if (!(error = lookup_head_remote(&real_url, repo)))
226 error = git_path_apply_relative(&real_url, url);
227 } else if (strchr(url, ':') != NULL || url[0] == '/') {
228 error = git_buf_sets(&real_url, url);
229 } else {
230 giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
231 error = -1;
232 }
233 if (error)
234 goto cleanup;
235
236 /* validate and normalize path */
237
238 if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
239 path += strlen(git_repository_workdir(repo));
240
241 if (git_path_root(path) >= 0) {
242 giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
243 error = -1;
244 goto cleanup;
245 }
246
247 /* update .gitmodules */
248
249 if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
250 giterr_set(GITERR_SUBMODULE,
251 "Adding submodules to a bare repository is not supported (for now)");
252 return -1;
253 }
254
255 if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
256 (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
257 goto cleanup;
258
259 if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
260 (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
261 goto cleanup;
262
263 git_buf_clear(&name);
264
265 /* init submodule repository and add origin remote as needed */
266
267 error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
268 if (error < 0)
269 goto cleanup;
270
271 /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
272 * gitlink in the sub-repo workdir directory to that repository
273 *
274 * Old style: sub-repo goes directly into repo/<name>/.git/
275 */
276
277 memset(&initopt, 0, sizeof(initopt));
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
310 if (!(error = submodule_get(&sm, repo, path, NULL)) &&
311 !(error = git_submodule_reload(sm)))
312 error = git_submodule_init(sm, false);
313
314 cleanup:
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
327 int 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 ||
335 (error = git_index_add(index, GIT_MODULES_FILE, 0)) < 0)
336 return error;
337
338 return git_submodule_add_to_index(sm, true);
339 }
340
341 int git_submodule_add_to_index(git_submodule *sm, int write_index)
342 {
343 int error;
344 git_repository *repo, *sm_repo;
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
355 /* force reload of wd OID by git_submodule_open */
356 sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
357
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 }
371
372 memset(&entry, 0, sizeof(entry));
373 entry.path = sm->path;
374 git_index__init_entry_from_stat(&st, &entry);
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
395 /* add it */
396 error = git_index_add2(index, &entry);
397
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
406 cleanup:
407 git_repository_free(sm_repo);
408 git_buf_free(&path);
409 return error;
410 }
411
412 int git_submodule_save(git_submodule *submodule)
413 {
414 int error = 0;
415 git_config_file *mods;
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
472 cleanup:
473 if (mods != NULL)
474 git_config_file_free(mods);
475 git_buf_free(&key);
476
477 return error;
478 }
479
480 git_repository *git_submodule_owner(git_submodule *submodule)
481 {
482 assert(submodule);
483 return submodule->owner;
484 }
485
486 const char *git_submodule_name(git_submodule *submodule)
487 {
488 assert(submodule);
489 return submodule->name;
490 }
491
492 const char *git_submodule_path(git_submodule *submodule)
493 {
494 assert(submodule);
495 return submodule->path;
496 }
497
498 const char *git_submodule_url(git_submodule *submodule)
499 {
500 assert(submodule);
501 return submodule->url;
502 }
503
504 int 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
516 const git_oid *git_submodule_index_oid(git_submodule *submodule)
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
526 const git_oid *git_submodule_head_oid(git_submodule *submodule)
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
536 const git_oid *git_submodule_wd_oid(git_submodule *submodule)
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);
546 else
547 giterr_clear();
548 }
549
550 if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
551 return &submodule->wd_oid;
552 else
553 return NULL;
554 }
555
556 git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
557 {
558 assert(submodule);
559 return submodule->ignore;
560 }
561
562 git_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
577 git_submodule_update_t git_submodule_update(git_submodule *submodule)
578 {
579 assert(submodule);
580 return submodule->update;
581 }
582
583 git_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
598 int git_submodule_fetch_recurse_submodules(
599 git_submodule *submodule)
600 {
601 assert(submodule);
602 return submodule->fetch_recurse;
603 }
604
605 int 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
618 int 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
649 int 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
663 int 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)) {
698 if (!git_reference_name_to_oid(
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
708 int git_submodule_reload_all(git_repository *repo)
709 {
710 assert(repo);
711 return load_submodule_config(repo, true);
712 }
713
714 int git_submodule_reload(git_submodule *submodule)
715 {
716 git_repository *repo;
717 git_index *index;
718 int pos, error;
719 git_tree *head;
720 git_config_file *mods;
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
730 submodule->flags = submodule->flags &
731 ~(GIT_SUBMODULE_STATUS_IN_INDEX |
732 GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
733
734 pos = git_index_find(index, submodule->path);
735 if (pos >= 0) {
736 git_index_entry *entry = git_index_get(index, pos);
737
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 }
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))) {
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 }
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\\.");
785 git_buf_puts_escape_regex(&path, submodule->name);
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);
795 git_config_file_free(mods);
796 }
797
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
808 return error;
809 }
810
811 int git_submodule_status(
812 unsigned int *status,
813 git_submodule *submodule)
814 {
815 int error = 0;
816 unsigned int status_val;
817
818 assert(status && submodule);
819
820 status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
821
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 }
826
827 *status = status_val;
828
829 return error;
830 }
831
832 /*
833 * INTERNAL FUNCTIONS
834 */
835
836 static git_submodule *submodule_alloc(git_repository *repo, const char *name)
837 {
838 git_submodule *sm;
839
840 if (!name || !strlen(name)) {
841 giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
842 return NULL;
843 }
844
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
853 sm->owner = repo;
854 sm->refcount = 1;
855
856 return sm;
857
858 fail:
859 submodule_release(sm, 0);
860 return NULL;
861 }
862
863 static 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
888 static int submodule_get(
889 git_submodule **sm_ptr,
890 git_repository *repo,
891 const char *name,
892 const char *alternate)
893 {
894 git_strmap *smcfg = repo->submodules;
895 khiter_t pos;
896 git_submodule *sm;
897 int error;
898
899 assert(repo && name);
900
901 pos = git_strmap_lookup_index(smcfg, name);
902
903 if (!git_strmap_valid_index(smcfg, pos) && alternate)
904 pos = git_strmap_lookup_index(smcfg, alternate);
905
906 if (!git_strmap_valid_index(smcfg, pos)) {
907 sm = submodule_alloc(repo, name);
908
909 /* insert value at name - if another thread beats us to it, then use
910 * their record and release our own.
911 */
912 pos = kh_put(str, smcfg, sm->name, &error);
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);
925 }
926
927 *sm_ptr = sm;
928
929 return (sm != NULL) ? 0 : -1;
930 }
931
932 static int submodule_load_from_index(
933 git_repository *repo, const git_index_entry *entry)
934 {
935 git_submodule *sm;
936
937 if (submodule_get(&sm, repo, entry->path, NULL) < 0)
938 return -1;
939
940 if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
941 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
942 return 0;
943 }
944
945 sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
946
947 git_oid_cpy(&sm->index_oid, &entry->oid);
948 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
949
950 return 0;
951 }
952
953 static int submodule_load_from_head(
954 git_repository *repo, const char *path, const git_oid *oid)
955 {
956 git_submodule *sm;
957
958 if (submodule_get(&sm, repo, path, NULL) < 0)
959 return -1;
960
961 sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
962
963 git_oid_cpy(&sm->head_oid, oid);
964 sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
965
966 return 0;
967 }
968
969 static 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;
974 }
975
976 static int submodule_load_from_config(
977 const char *key, const char *value, void *data)
978 {
979 git_repository *repo = data;
980 git_strmap *smcfg = repo->submodules;
981 const char *namestart, *property, *alternate = NULL;
982 git_buf name = GIT_BUF_INIT;
983 git_submodule *sm;
984 bool is_path;
985 int error = 0;
986
987 if (git__prefixcmp(key, "submodule.") != 0)
988 return 0;
989
990 namestart = key + strlen("submodule.");
991 property = strrchr(namestart, '.');
992 if (property == NULL)
993 return 0;
994 property++;
995 is_path = (strcasecmp(property, "path") == 0);
996
997 if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
998 return -1;
999
1000 if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
1001 git_buf_free(&name);
1002 return -1;
1003 }
1004
1005 sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
1006
1007 /* Only from config might we get differing names & paths. If so, then
1008 * update the submodule and insert under the alternative key.
1009 */
1010
1011 /* TODO: if case insensitive filesystem, then the following strcmps
1012 * should be strcasecmp
1013 */
1014
1015 if (strcmp(sm->name, name.ptr) != 0) {
1016 alternate = sm->name = git_buf_detach(&name);
1017 } else if (is_path && value && strcmp(sm->path, value) != 0) {
1018 alternate = sm->path = git__strdup(value);
1019 if (!sm->path)
1020 error = -1;
1021 }
1022 if (alternate) {
1023 void *old_sm = NULL;
1024 git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
1025
1026 if (error >= 0)
1027 sm->refcount++; /* inserted under a new key */
1028
1029 /* if we replaced an old module under this key, release the old one */
1030 if (old_sm && ((git_submodule *)old_sm) != sm) {
1031 submodule_release(old_sm, 1);
1032 /* TODO: log warning about multiple submodules with same path */
1033 }
1034 }
1035
1036 git_buf_free(&name);
1037 if (error < 0)
1038 return error;
1039
1040 /* TODO: Look up path in index and if it is present but not a GITLINK
1041 * then this should be deleted (at least to match git's behavior)
1042 */
1043
1044 if (is_path)
1045 return 0;
1046
1047 /* copy other properties into submodule entry */
1048 if (strcasecmp(property, "url") == 0) {
1049 git__free(sm->url);
1050 sm->url = NULL;
1051
1052 if (value != NULL && (sm->url = git__strdup(value)) == NULL)
1053 return -1;
1054 }
1055 else if (strcasecmp(property, "update") == 0) {
1056 int val;
1057 if (git_config_lookup_map_value(
1058 _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0)
1059 return submodule_config_error("update", value);
1060 sm->update_default = sm->update = (git_submodule_update_t)val;
1061 }
1062 else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
1063 if (git__parse_bool(&sm->fetch_recurse, value) < 0)
1064 return submodule_config_error("fetchRecurseSubmodules", value);
1065 }
1066 else if (strcasecmp(property, "ignore") == 0) {
1067 int val;
1068 if (git_config_lookup_map_value(
1069 _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0)
1070 return submodule_config_error("ignore", value);
1071 sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
1072 }
1073 /* ignore other unknown submodule properties */
1074
1075 return 0;
1076 }
1077
1078 static int submodule_load_from_wd_lite(
1079 git_submodule *sm, const char *name, void *payload)
1080 {
1081 git_repository *repo = git_submodule_owner(sm);
1082 git_buf path = GIT_BUF_INIT;
1083
1084 GIT_UNUSED(name);
1085 GIT_UNUSED(payload);
1086
1087 if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
1088 return -1;
1089
1090 if (git_path_isdir(path.ptr))
1091 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1092
1093 if (git_path_contains(&path, DOT_GIT))
1094 sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
1095
1096 git_buf_free(&path);
1097
1098 return 0;
1099 }
1100
1101 static void submodule_mode_mismatch(
1102 git_repository *repo, const char *path, unsigned int flag)
1103 {
1104 khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
1105
1106 if (git_strmap_valid_index(repo->submodules, pos)) {
1107 git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
1108
1109 sm->flags |= flag;
1110 }
1111 }
1112
1113 static int load_submodule_config_from_index(
1114 git_repository *repo, git_oid *gitmodules_oid)
1115 {
1116 int error;
1117 git_iterator *i;
1118 const git_index_entry *entry;
1119
1120 if ((error = git_iterator_for_index(&i, repo)) < 0)
1121 return error;
1122
1123 error = git_iterator_current(i, &entry);
1124
1125 while (!error && entry != NULL) {
1126
1127 if (S_ISGITLINK(entry->mode)) {
1128 error = submodule_load_from_index(repo, entry);
1129 if (error < 0)
1130 break;
1131 } else {
1132 submodule_mode_mismatch(
1133 repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
1134
1135 if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
1136 git_oid_cpy(gitmodules_oid, &entry->oid);
1137 }
1138
1139 error = git_iterator_advance(i, &entry);
1140 }
1141
1142 git_iterator_free(i);
1143
1144 return error;
1145 }
1146
1147 static int load_submodule_config_from_head(
1148 git_repository *repo, git_oid *gitmodules_oid)
1149 {
1150 int error;
1151 git_tree *head;
1152 git_iterator *i;
1153 const git_index_entry *entry;
1154
1155 if ((error = git_repository_head_tree(&head, repo)) < 0)
1156 return error;
1157
1158 if ((error = git_iterator_for_tree(&i, repo, head)) < 0) {
1159 git_tree_free(head);
1160 return error;
1161 }
1162
1163 error = git_iterator_current(i, &entry);
1164
1165 while (!error && entry != NULL) {
1166
1167 if (S_ISGITLINK(entry->mode)) {
1168 error = submodule_load_from_head(repo, entry->path, &entry->oid);
1169 if (error < 0)
1170 break;
1171 } else {
1172 submodule_mode_mismatch(
1173 repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
1174
1175 if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
1176 git_oid_iszero(gitmodules_oid))
1177 git_oid_cpy(gitmodules_oid, &entry->oid);
1178 }
1179
1180 error = git_iterator_advance(i, &entry);
1181 }
1182
1183 git_iterator_free(i);
1184 git_tree_free(head);
1185
1186 return error;
1187 }
1188
1189 static git_config_file *open_gitmodules(
1190 git_repository *repo,
1191 bool okay_to_create,
1192 const git_oid *gitmodules_oid)
1193 {
1194 const char *workdir = git_repository_workdir(repo);
1195 git_buf path = GIT_BUF_INIT;
1196 git_config_file *mods = NULL;
1197
1198 if (workdir != NULL) {
1199 if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1200 return NULL;
1201
1202 if (okay_to_create || git_path_isfile(path.ptr)) {
1203 /* git_config_file__ondisk should only fail if OOM */
1204 if (git_config_file__ondisk(&mods, path.ptr) < 0)
1205 mods = NULL;
1206 /* open should only fail here if the file is malformed */
1207 else if (git_config_file_open(mods) < 0) {
1208 git_config_file_free(mods);
1209 mods = NULL;
1210 }
1211 }
1212 }
1213
1214 if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
1215 /* TODO: Retrieve .gitmodules content from ODB */
1216
1217 /* Should we actually do this? Core git does not, but it means you
1218 * can't really get much information about submodules on bare repos.
1219 */
1220 }
1221
1222 git_buf_free(&path);
1223
1224 return mods;
1225 }
1226
1227 static int load_submodule_config(git_repository *repo, bool force)
1228 {
1229 int error;
1230 git_oid gitmodules_oid;
1231 git_buf path = GIT_BUF_INIT;
1232 git_config_file *mods = NULL;
1233
1234 if (repo->submodules && !force)
1235 return 0;
1236
1237 memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
1238
1239 /* Submodule data is kept in a hashtable keyed by both name and path.
1240 * These are usually the same, but that is not guaranteed.
1241 */
1242 if (!repo->submodules) {
1243 repo->submodules = git_strmap_alloc();
1244 GITERR_CHECK_ALLOC(repo->submodules);
1245 }
1246
1247 /* add submodule information from index */
1248
1249 if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
1250 goto cleanup;
1251
1252 /* add submodule information from HEAD */
1253
1254 if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
1255 goto cleanup;
1256
1257 /* add submodule information from .gitmodules */
1258
1259 if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
1260 error = git_config_file_foreach(mods, submodule_load_from_config, repo);
1261
1262 if (error != 0)
1263 goto cleanup;
1264
1265 /* shallow scan submodules in work tree */
1266
1267 if (!git_repository_is_bare(repo))
1268 error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
1269
1270 cleanup:
1271 git_buf_free(&path);
1272
1273 if (mods != NULL)
1274 git_config_file_free(mods);
1275
1276 if (error)
1277 git_submodule_config_free(repo);
1278
1279 return error;
1280 }
1281
1282 static int lookup_head_remote(git_buf *url, git_repository *repo)
1283 {
1284 int error;
1285 git_config *cfg;
1286 git_reference *head = NULL, *remote = NULL;
1287 const char *tgt, *scan;
1288 git_buf key = GIT_BUF_INIT;
1289
1290 /* 1. resolve HEAD -> refs/heads/BRANCH
1291 * 2. lookup config branch.BRANCH.remote -> ORIGIN
1292 * 3. lookup remote.ORIGIN.url
1293 */
1294
1295 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1296 return error;
1297
1298 if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
1299 giterr_set(GITERR_SUBMODULE,
1300 "Cannot resolve relative URL when HEAD cannot be resolved");
1301 error = GIT_ENOTFOUND;
1302 goto cleanup;
1303 }
1304
1305 if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
1306 giterr_set(GITERR_SUBMODULE,
1307 "Cannot resolve relative URL when HEAD is not symbolic");
1308 error = GIT_ENOTFOUND;
1309 goto cleanup;
1310 }
1311
1312 if ((error = git_branch_tracking(&remote, head)) < 0)
1313 goto cleanup;
1314
1315 /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
1316
1317 if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
1318 git__prefixcmp(git_reference_target(remote), "refs/remotes/") != 0)
1319 {
1320 giterr_set(GITERR_SUBMODULE,
1321 "Cannot resolve relative URL when HEAD is not symbolic");
1322 error = GIT_ENOTFOUND;
1323 goto cleanup;
1324 }
1325
1326 scan = tgt = git_reference_target(remote) + strlen("refs/remotes/");
1327 while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
1328 scan++; /* find non-escaped slash to end ORIGIN name */
1329
1330 error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
1331 if (error < 0)
1332 goto cleanup;
1333
1334 if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
1335 goto cleanup;
1336
1337 error = git_buf_sets(url, tgt);
1338
1339 cleanup:
1340 git_buf_free(&key);
1341 git_reference_free(head);
1342 git_reference_free(remote);
1343
1344 return error;
1345 }
1346
1347 static int submodule_update_config(
1348 git_submodule *submodule,
1349 const char *attr,
1350 const char *value,
1351 bool overwrite,
1352 bool only_existing)
1353 {
1354 int error;
1355 git_config *config;
1356 git_buf key = GIT_BUF_INIT;
1357 const char *old = NULL;
1358
1359 assert(submodule);
1360
1361 error = git_repository_config__weakptr(&config, submodule->owner);
1362 if (error < 0)
1363 return error;
1364
1365 error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
1366 if (error < 0)
1367 goto cleanup;
1368
1369 if (git_config_get_string(&old, config, key.ptr) < 0)
1370 giterr_clear();
1371
1372 if (!old && only_existing)
1373 goto cleanup;
1374 if (old && !overwrite)
1375 goto cleanup;
1376 if ((!old && !value) || (old && value && strcmp(old, value) == 0))
1377 goto cleanup;
1378
1379 if (!value)
1380 error = git_config_delete(config, key.ptr);
1381 else
1382 error = git_config_set_string(config, key.ptr, value);
1383
1384 cleanup:
1385 git_buf_free(&key);
1386 return error;
1387 }
1388
1389 static int submodule_index_status(unsigned int *status, git_submodule *sm)
1390 {
1391 const git_oid *head_oid = git_submodule_head_oid(sm);
1392 const git_oid *index_oid = git_submodule_index_oid(sm);
1393
1394 if (!head_oid) {
1395 if (index_oid)
1396 *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
1397 }
1398 else if (!index_oid)
1399 *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
1400 else if (!git_oid_equal(head_oid, index_oid))
1401 *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
1402
1403 return 0;
1404 }
1405
1406 static int submodule_wd_status(unsigned int *status, git_submodule *sm)
1407 {
1408 int error = 0;
1409 const git_oid *wd_oid, *index_oid;
1410 git_repository *sm_repo = NULL;
1411
1412 /* open repo now if we need it (so wd_oid() call won't reopen) */
1413 if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
1414 sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
1415 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
1416 {
1417 if ((error = git_submodule_open(&sm_repo, sm)) < 0)
1418 return error;
1419 }
1420
1421 index_oid = git_submodule_index_oid(sm);
1422 wd_oid = git_submodule_wd_oid(sm);
1423
1424 if (!index_oid) {
1425 if (wd_oid)
1426 *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
1427 }
1428 else if (!wd_oid) {
1429 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
1430 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
1431 *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
1432 else
1433 *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
1434 }
1435 else if (!git_oid_equal(index_oid, wd_oid))
1436 *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
1437
1438 if (sm_repo != NULL) {
1439 git_tree *sm_head;
1440 git_diff_options opt;
1441 git_diff_list *diff;
1442
1443 /* the diffs below could be optimized with an early termination
1444 * option to the git_diff functions, but for now this is sufficient
1445 * (and certainly no worse that what core git does).
1446 */
1447
1448 /* perform head-to-index diff on submodule */
1449
1450 if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
1451 return error;
1452
1453 memset(&opt, 0, sizeof(opt));
1454 if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
1455 opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
1456
1457 error = git_diff_index_to_tree(sm_repo, &opt, sm_head, &diff);
1458
1459 if (!error) {
1460 if (git_diff_entrycount(diff, -1) > 0)
1461 *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
1462
1463 git_diff_list_free(diff);
1464 diff = NULL;
1465 }
1466
1467 git_tree_free(sm_head);
1468
1469 if (error < 0)
1470 return error;
1471
1472 /* perform index-to-workdir diff on submodule */
1473
1474 error = git_diff_workdir_to_index(sm_repo, &opt, &diff);
1475
1476 if (!error) {
1477 int untracked = git_diff_entrycount(diff, GIT_DELTA_UNTRACKED);
1478
1479 if (untracked > 0)
1480 *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
1481
1482 if (git_diff_entrycount(diff, -1) - untracked > 0)
1483 *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
1484
1485 git_diff_list_free(diff);
1486 diff = NULL;
1487 }
1488
1489 git_repository_free(sm_repo);
1490 }
1491
1492 return error;
1493 }