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