]> git.proxmox.com Git - libgit2.git/blob - src/submodule.c
Merge pull request #4130 from libgit2/ethomson/clar_messages
[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 git_cvar_map _sm_recurse_map[] = {
47 {GIT_CVAR_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
48 {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
49 {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
50 };
51
52 enum {
53 CACHE_OK = 0,
54 CACHE_REFRESH = 1,
55 CACHE_FLUSH = 2
56 };
57 enum {
58 GITMODULES_EXISTING = 0,
59 GITMODULES_CREATE = 1,
60 };
61
62 static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
63 {
64 khint_t h;
65
66 for (h = 0; *s; ++s)
67 if (s[1] != '\0' || *s != '/')
68 h = (h << 5) - h + *s;
69
70 return h;
71 }
72
73 static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
74 {
75 size_t alen = a ? strlen(a) : 0;
76 size_t blen = b ? strlen(b) : 0;
77
78 if (alen > 0 && a[alen - 1] == '/')
79 alen--;
80 if (blen > 0 && b[blen - 1] == '/')
81 blen--;
82
83 return (alen == 0 && blen == 0) ||
84 (alen == blen && strncmp(a, b, alen) == 0);
85 }
86
87 __KHASH_IMPL(
88 str, static kh_inline, const char *, void *, 1,
89 str_hash_no_trailing_slash, str_equal_no_trailing_slash)
90
91 static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
92 static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
93 static git_config *gitmodules_snapshot(git_repository *repo);
94 static int get_url_base(git_buf *url, git_repository *repo);
95 static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
96 static int lookup_default_remote(git_remote **remote, git_repository *repo);
97 static int submodule_load_each(const git_config_entry *entry, void *payload);
98 static int submodule_read_config(git_submodule *sm, git_config *cfg);
99 static int submodule_load_from_wd_lite(git_submodule *);
100 static void submodule_get_index_status(unsigned int *, git_submodule *);
101 static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
102 static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie);
103 static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id);
104
105 static int submodule_cmp(const void *a, const void *b)
106 {
107 return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
108 }
109
110 static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
111 {
112 ssize_t idx = git_buf_rfind(key, '.');
113 git_buf_truncate(key, (size_t)(idx + 1));
114 return git_buf_puts(key, suffix);
115 }
116
117 /*
118 * PUBLIC APIS
119 */
120
121 static void submodule_set_lookup_error(int error, const char *name)
122 {
123 if (!error)
124 return;
125
126 giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
127 "no submodule named '%s'" :
128 "submodule '%s' has not been added yet", name);
129 }
130
131 typedef struct {
132 const char *path;
133 char *name;
134 } fbp_data;
135
136 static int find_by_path(const git_config_entry *entry, void *payload)
137 {
138 fbp_data *data = payload;
139
140 if (!strcmp(entry->value, data->path)) {
141 const char *fdot, *ldot;
142 fdot = strchr(entry->name, '.');
143 ldot = strrchr(entry->name, '.');
144 data->name = git__strndup(fdot + 1, ldot - fdot - 1);
145 GITERR_CHECK_ALLOC(data->name);
146 }
147
148 return 0;
149 }
150
151 /**
152 * Release the name map returned by 'load_submodule_names'.
153 */
154 static void free_submodule_names(git_strmap *names)
155 {
156 git_buf *name = 0;
157 if (names == NULL)
158 return;
159 git_strmap_foreach_value(names, name, {
160 git__free(name);
161 });
162 git_strmap_free(names);
163 return;
164 }
165
166 /**
167 * Map submodule paths to names.
168 * TODO: for some use-cases, this might need case-folding on a
169 * case-insensitive filesystem
170 */
171 static int load_submodule_names(git_strmap *out, git_config *cfg)
172 {
173 const char *key = "submodule\\..*\\.path";
174 git_config_iterator *iter;
175 git_config_entry *entry;
176 git_buf buf = GIT_BUF_INIT;
177 int rval;
178 int error = 0;
179
180 if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
181 return error;
182
183 while (git_config_next(&entry, iter) == 0) {
184 const char *fdot, *ldot;
185 fdot = strchr(entry->name, '.');
186 ldot = strrchr(entry->name, '.');
187
188 git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
189 git_strmap_insert(out, entry->value, git_buf_detach(&buf), &rval);
190 if (rval < 0) {
191 giterr_set(GITERR_NOMEMORY, "error inserting submodule into hash table");
192 return -1;
193 }
194 }
195
196 git_config_iterator_free(iter);
197 return 0;
198 }
199
200 int git_submodule_lookup(
201 git_submodule **out, /* NULL if user only wants to test existence */
202 git_repository *repo,
203 const char *name) /* trailing slash is allowed */
204 {
205 int error;
206 unsigned int location;
207 git_submodule *sm;
208
209 assert(repo && name);
210
211 if (repo->submodule_cache != NULL) {
212 khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
213 if (git_strmap_valid_index(repo->submodule_cache, pos)) {
214 if (out) {
215 *out = git_strmap_value_at(repo->submodule_cache, pos);
216 GIT_REFCOUNT_INC(*out);
217 }
218 return 0;
219 }
220 }
221
222 if ((error = submodule_alloc(&sm, repo, name)) < 0)
223 return error;
224
225 if ((error = git_submodule_reload(sm, false)) < 0) {
226 git_submodule_free(sm);
227 return error;
228 }
229
230 if ((error = git_submodule_location(&location, sm)) < 0) {
231 git_submodule_free(sm);
232 return error;
233 }
234
235 /* If it's not configured or we're looking by path */
236 if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
237 git_config_backend *mods;
238 const char *pattern = "submodule\\..*\\.path";
239 git_buf path = GIT_BUF_INIT;
240 fbp_data data = { NULL, NULL };
241
242 git_buf_puts(&path, name);
243 while (path.ptr[path.size-1] == '/') {
244 path.ptr[--path.size] = '\0';
245 }
246 data.path = path.ptr;
247
248 mods = open_gitmodules(repo, GITMODULES_EXISTING);
249
250 if (mods)
251 error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
252
253 git_config_file_free(mods);
254
255 if (error < 0) {
256 git_submodule_free(sm);
257 git_buf_free(&path);
258 return error;
259 }
260
261 if (data.name) {
262 git__free(sm->name);
263 sm->name = data.name;
264 sm->path = git_buf_detach(&path);
265
266 /* Try to load again with the right name */
267 if ((error = git_submodule_reload(sm, false)) < 0) {
268 git_submodule_free(sm);
269 return error;
270 }
271 }
272
273 git_buf_free(&path);
274 }
275
276 if ((error = git_submodule_location(&location, sm)) < 0) {
277 git_submodule_free(sm);
278 return error;
279 }
280
281 /* If we still haven't found it, do the WD check */
282 if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
283 git_submodule_free(sm);
284 error = GIT_ENOTFOUND;
285
286 /* If it's not configured, we still check if there's a repo at the path */
287 if (git_repository_workdir(repo)) {
288 git_buf path = GIT_BUF_INIT;
289 if (git_buf_join3(&path,
290 '/', git_repository_workdir(repo), name, DOT_GIT) < 0)
291 return -1;
292
293 if (git_path_exists(path.ptr))
294 error = GIT_EEXISTS;
295
296 git_buf_free(&path);
297 }
298
299 submodule_set_lookup_error(error, name);
300 return error;
301 }
302
303 if (out)
304 *out = sm;
305 else
306 git_submodule_free(sm);
307
308 return 0;
309 }
310
311 static void submodule_free_dup(void *sm)
312 {
313 git_submodule_free(sm);
314 }
315
316 static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
317 {
318 int error = 0;
319 khiter_t pos;
320 git_submodule *sm = NULL;
321
322 pos = git_strmap_lookup_index(map, name);
323 if (git_strmap_valid_index(map, pos)) {
324 sm = git_strmap_value_at(map, pos);
325 goto done;
326 }
327
328 /* if the submodule doesn't exist yet in the map, create it */
329 if ((error = submodule_alloc(&sm, repo, name)) < 0)
330 return error;
331
332 pos = git_strmap_put(map, sm->name, &error);
333 /* nobody can beat us to adding it */
334 assert(error != 0);
335 if (error < 0) {
336 git_submodule_free(sm);
337 return error;
338 }
339
340 git_strmap_set_value_at(map, pos, sm);
341
342 done:
343 GIT_REFCOUNT_INC(sm);
344 *out = sm;
345 return 0;
346 }
347
348 static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
349 {
350 int error;
351 git_iterator *i = NULL;
352 const git_index_entry *entry;
353 git_strmap *names = 0;
354
355 git_strmap_alloc(&names);
356 if ((error = load_submodule_names(names, cfg)))
357 goto done;
358
359 if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
360 goto done;
361
362 while (!(error = git_iterator_advance(&entry, i))) {
363 khiter_t pos = git_strmap_lookup_index(map, entry->path);
364 git_submodule *sm;
365
366 if (git_strmap_valid_index(map, pos)) {
367 sm = git_strmap_value_at(map, pos);
368
369 if (S_ISGITLINK(entry->mode))
370 submodule_update_from_index_entry(sm, entry);
371 else
372 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
373 } else if (S_ISGITLINK(entry->mode)) {
374 khiter_t name_pos;
375 const char *name;
376
377 name_pos = git_strmap_lookup_index(names, entry->path);
378 if (git_strmap_valid_index(names, name_pos)) {
379 name = git_strmap_value_at(names, name_pos);
380 } else {
381 name = entry->path;
382 }
383
384 if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
385 submodule_update_from_index_entry(sm, entry);
386 git_submodule_free(sm);
387 }
388 }
389 }
390
391 if (error == GIT_ITEROVER)
392 error = 0;
393
394 done:
395 git_iterator_free(i);
396 free_submodule_names(names);
397
398 return error;
399 }
400
401 static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
402 {
403 int error;
404 git_iterator *i = NULL;
405 const git_index_entry *entry;
406 git_strmap *names = 0;
407 git_strmap_alloc(&names);
408 if ((error = load_submodule_names(names, cfg)))
409 goto done;
410
411 if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
412 goto done;
413
414 while (!(error = git_iterator_advance(&entry, i))) {
415 khiter_t pos = git_strmap_lookup_index(map, entry->path);
416 git_submodule *sm;
417
418 if (git_strmap_valid_index(map, pos)) {
419 sm = git_strmap_value_at(map, pos);
420
421 if (S_ISGITLINK(entry->mode))
422 submodule_update_from_head_data(sm, entry->mode, &entry->id);
423 else
424 sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
425 } else if (S_ISGITLINK(entry->mode)) {
426 khiter_t name_pos;
427 const char *name;
428
429 name_pos = git_strmap_lookup_index(names, entry->path);
430 if (git_strmap_valid_index(names, name_pos)) {
431 name = git_strmap_value_at(names, name_pos);
432 } else {
433 name = entry->path;
434 }
435
436 if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
437 submodule_update_from_head_data(
438 sm, entry->mode, &entry->id);
439 git_submodule_free(sm);
440 }
441 }
442 }
443
444 if (error == GIT_ITEROVER)
445 error = 0;
446
447 done:
448 git_iterator_free(i);
449 free_submodule_names(names);
450
451 return error;
452 }
453
454 /* If have_sm is true, sm is populated, otherwise map an repo are. */
455 typedef struct {
456 git_config *mods;
457 git_strmap *map;
458 git_repository *repo;
459 } lfc_data;
460
461 int git_submodule__map(git_repository *repo, git_strmap *map)
462 {
463 int error = 0;
464 git_index *idx = NULL;
465 git_tree *head = NULL;
466 const char *wd = NULL;
467 git_buf path = GIT_BUF_INIT;
468 git_submodule *sm;
469 git_config *mods = NULL;
470 uint32_t mask;
471
472 assert(repo && map);
473
474 /* get sources that we will need to check */
475 if (git_repository_index(&idx, repo) < 0)
476 giterr_clear();
477 if (git_repository_head_tree(&head, repo) < 0)
478 giterr_clear();
479
480 wd = git_repository_workdir(repo);
481 if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
482 goto cleanup;
483
484 /* clear submodule flags that are to be refreshed */
485 mask = 0;
486 mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
487 GIT_SUBMODULE_STATUS__INDEX_FLAGS |
488 GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
489 GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
490
491 mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
492 GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
493 mask |= GIT_SUBMODULE_STATUS_IN_CONFIG;
494 if (mask != 0)
495 mask |= GIT_SUBMODULE_STATUS_IN_WD |
496 GIT_SUBMODULE_STATUS__WD_SCANNED |
497 GIT_SUBMODULE_STATUS__WD_FLAGS |
498 GIT_SUBMODULE_STATUS__WD_OID_VALID;
499
500 /* add submodule information from .gitmodules */
501 if (wd) {
502 lfc_data data = { 0 };
503 data.map = map;
504 data.repo = repo;
505
506 if ((mods = gitmodules_snapshot(repo)) == NULL)
507 goto cleanup;
508
509 data.mods = mods;
510 if ((error = git_config_foreach(
511 mods, submodule_load_each, &data)) < 0)
512 goto cleanup;
513 }
514 /* add back submodule information from index */
515 if (mods && idx) {
516 if ((error = submodules_from_index(map, idx, mods)) < 0)
517 goto cleanup;
518 }
519 /* add submodule information from HEAD */
520 if (mods && head) {
521 if ((error = submodules_from_head(map, head, mods)) < 0)
522 goto cleanup;
523 }
524 /* shallow scan submodules in work tree as needed */
525 if (wd && mask != 0) {
526 git_strmap_foreach_value(map, sm, {
527 submodule_load_from_wd_lite(sm);
528 });
529 }
530
531 cleanup:
532 git_config_free(mods);
533 /* TODO: if we got an error, mark submodule config as invalid? */
534 git_index_free(idx);
535 git_tree_free(head);
536 git_buf_free(&path);
537 return error;
538 }
539
540 int git_submodule_foreach(
541 git_repository *repo,
542 git_submodule_cb callback,
543 void *payload)
544 {
545 git_vector snapshot = GIT_VECTOR_INIT;
546 git_strmap *submodules;
547 git_submodule *sm;
548 int error;
549 size_t i;
550
551 if ((error = git_strmap_alloc(&submodules)) < 0)
552 return error;
553
554 if ((error = git_submodule__map(repo, submodules)) < 0)
555 goto done;
556
557 if (!(error = git_vector_init(
558 &snapshot, git_strmap_num_entries(submodules), submodule_cmp))) {
559
560 git_strmap_foreach_value(submodules, sm, {
561 if ((error = git_vector_insert(&snapshot, sm)) < 0)
562 break;
563 GIT_REFCOUNT_INC(sm);
564 });
565 }
566
567 if (error < 0)
568 goto done;
569
570 git_vector_uniq(&snapshot, submodule_free_dup);
571
572 git_vector_foreach(&snapshot, i, sm) {
573 if ((error = callback(sm, sm->name, payload)) != 0) {
574 giterr_set_after_callback(error);
575 break;
576 }
577 }
578
579 done:
580 git_vector_foreach(&snapshot, i, sm)
581 git_submodule_free(sm);
582 git_vector_free(&snapshot);
583
584 git_strmap_foreach_value(submodules, sm, {
585 git_submodule_free(sm);
586 });
587 git_strmap_free(submodules);
588
589 return error;
590 }
591
592 static int submodule_repo_init(
593 git_repository **out,
594 git_repository *parent_repo,
595 const char *path,
596 const char *url,
597 bool use_gitlink)
598 {
599 int error = 0;
600 git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
601 git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
602 git_repository *subrepo = NULL;
603
604 error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
605 if (error < 0)
606 goto cleanup;
607
608 initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
609 initopt.origin_url = url;
610
611 /* init submodule repository and add origin remote as needed */
612
613 /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
614 * gitlink in the sub-repo workdir directory to that repository
615 *
616 * Old style: sub-repo goes directly into repo/<name>/.git/
617 */
618 if (use_gitlink) {
619 error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
620 if (error < 0)
621 goto cleanup;
622 error = git_buf_joinpath(&repodir, repodir.ptr, path);
623 if (error < 0)
624 goto cleanup;
625
626 initopt.workdir_path = workdir.ptr;
627 initopt.flags |=
628 GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
629 GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
630
631 error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
632 } else
633 error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
634
635 cleanup:
636 git_buf_free(&workdir);
637 git_buf_free(&repodir);
638
639 *out = subrepo;
640
641 return error;
642 }
643
644 int git_submodule_add_setup(
645 git_submodule **out,
646 git_repository *repo,
647 const char *url,
648 const char *path,
649 int use_gitlink)
650 {
651 int error = 0;
652 git_config_backend *mods = NULL;
653 git_submodule *sm = NULL;
654 git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
655 git_repository *subrepo = NULL;
656
657 assert(repo && url && path);
658
659 /* see if there is already an entry for this submodule */
660
661 if (git_submodule_lookup(NULL, repo, path) < 0)
662 giterr_clear();
663 else {
664 giterr_set(GITERR_SUBMODULE,
665 "attempt to add submodule '%s' that already exists", path);
666 return GIT_EEXISTS;
667 }
668
669 /* validate and normalize path */
670
671 if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
672 path += strlen(git_repository_workdir(repo));
673
674 if (git_path_root(path) >= 0) {
675 giterr_set(GITERR_SUBMODULE, "submodule path must be a relative path");
676 error = -1;
677 goto cleanup;
678 }
679
680 /* update .gitmodules */
681
682 if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
683 giterr_set(GITERR_SUBMODULE,
684 "adding submodules to a bare repository is not supported");
685 return -1;
686 }
687
688 if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
689 (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
690 goto cleanup;
691
692 if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
693 (error = git_config_file_set_string(mods, name.ptr, url)) < 0)
694 goto cleanup;
695
696 git_buf_clear(&name);
697
698 /* init submodule repository and add origin remote as needed */
699
700 error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
701 if (error < 0)
702 goto cleanup;
703
704 /* if the repo does not already exist, then init a new repo and add it.
705 * Otherwise, just add the existing repo.
706 */
707 if (!(git_path_exists(name.ptr) &&
708 git_path_contains(&name, DOT_GIT))) {
709
710 /* resolve the actual URL to use */
711 if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0)
712 goto cleanup;
713
714 if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
715 goto cleanup;
716 }
717
718 if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
719 goto cleanup;
720
721 error = git_submodule_init(sm, false);
722
723 cleanup:
724 if (error && sm) {
725 git_submodule_free(sm);
726 sm = NULL;
727 }
728 if (out != NULL)
729 *out = sm;
730
731 git_config_file_free(mods);
732 git_repository_free(subrepo);
733 git_buf_free(&real_url);
734 git_buf_free(&name);
735
736 return error;
737 }
738
739 int git_submodule_repo_init(
740 git_repository **out,
741 const git_submodule *sm,
742 int use_gitlink)
743 {
744 int error;
745 git_repository *sub_repo = NULL;
746 const char *configured_url;
747 git_config *cfg = NULL;
748 git_buf buf = GIT_BUF_INIT;
749
750 assert(out && sm);
751
752 /* get the configured remote url of the submodule */
753 if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
754 (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
755 (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
756 (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
757 goto done;
758
759 *out = sub_repo;
760
761 done:
762 git_config_free(cfg);
763 git_buf_free(&buf);
764 return error;
765 }
766
767 int git_submodule_add_finalize(git_submodule *sm)
768 {
769 int error;
770 git_index *index;
771
772 assert(sm);
773
774 if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
775 (error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
776 return error;
777
778 return git_submodule_add_to_index(sm, true);
779 }
780
781 int git_submodule_add_to_index(git_submodule *sm, int write_index)
782 {
783 int error;
784 git_repository *sm_repo = NULL;
785 git_index *index;
786 git_buf path = GIT_BUF_INIT;
787 git_commit *head;
788 git_index_entry entry;
789 struct stat st;
790
791 assert(sm);
792
793 /* force reload of wd OID by git_submodule_open */
794 sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
795
796 if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
797 (error = git_buf_joinpath(
798 &path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
799 (error = git_submodule_open(&sm_repo, sm)) < 0)
800 goto cleanup;
801
802 /* read stat information for submodule working directory */
803 if (p_stat(path.ptr, &st) < 0) {
804 giterr_set(GITERR_SUBMODULE,
805 "cannot add submodule without working directory");
806 error = -1;
807 goto cleanup;
808 }
809
810 memset(&entry, 0, sizeof(entry));
811 entry.path = sm->path;
812 git_index_entry__init_from_stat(
813 &entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
814
815 /* calling git_submodule_open will have set sm->wd_oid if possible */
816 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
817 giterr_set(GITERR_SUBMODULE,
818 "cannot add submodule without HEAD to index");
819 error = -1;
820 goto cleanup;
821 }
822 git_oid_cpy(&entry.id, &sm->wd_oid);
823
824 if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
825 goto cleanup;
826
827 entry.ctime.seconds = (int32_t)git_commit_time(head);
828 entry.ctime.nanoseconds = 0;
829 entry.mtime.seconds = (int32_t)git_commit_time(head);
830 entry.mtime.nanoseconds = 0;
831
832 git_commit_free(head);
833
834 /* add it */
835 error = git_index_add(index, &entry);
836
837 /* write it, if requested */
838 if (!error && write_index) {
839 error = git_index_write(index);
840
841 if (!error)
842 git_oid_cpy(&sm->index_oid, &sm->wd_oid);
843 }
844
845 cleanup:
846 git_repository_free(sm_repo);
847 git_buf_free(&path);
848 return error;
849 }
850
851 const char *git_submodule_update_to_str(git_submodule_update_t update)
852 {
853 int i;
854 for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
855 if (_sm_update_map[i].map_value == (int)update)
856 return _sm_update_map[i].str_match;
857 return NULL;
858 }
859
860 git_repository *git_submodule_owner(git_submodule *submodule)
861 {
862 assert(submodule);
863 return submodule->repo;
864 }
865
866 const char *git_submodule_name(git_submodule *submodule)
867 {
868 assert(submodule);
869 return submodule->name;
870 }
871
872 const char *git_submodule_path(git_submodule *submodule)
873 {
874 assert(submodule);
875 return submodule->path;
876 }
877
878 const char *git_submodule_url(git_submodule *submodule)
879 {
880 assert(submodule);
881 return submodule->url;
882 }
883
884 int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
885 {
886 int error = 0;
887 git_buf normalized = GIT_BUF_INIT;
888
889 assert(out && repo && url);
890
891 git_buf_sanitize(out);
892
893 /* We do this in all platforms in case someone on Windows created the .gitmodules */
894 if (strchr(url, '\\')) {
895 if ((error = git_path_normalize_slashes(&normalized, url)) < 0)
896 return error;
897
898 url = normalized.ptr;
899 }
900
901
902 if (git_path_is_relative(url)) {
903 if (!(error = get_url_base(out, repo)))
904 error = git_path_apply_relative(out, url);
905 } else if (strchr(url, ':') != NULL || url[0] == '/') {
906 error = git_buf_sets(out, url);
907 } else {
908 giterr_set(GITERR_SUBMODULE, "invalid format for submodule URL");
909 error = -1;
910 }
911
912 git_buf_free(&normalized);
913 return error;
914 }
915
916 static int write_var(git_repository *repo, const char *name, const char *var, const char *val)
917 {
918 git_buf key = GIT_BUF_INIT;
919 git_config_backend *mods;
920 int error;
921
922 mods = open_gitmodules(repo, GITMODULES_CREATE);
923 if (!mods)
924 return -1;
925
926 if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0)
927 goto cleanup;
928
929 if (val)
930 error = git_config_file_set_string(mods, key.ptr, val);
931 else
932 error = git_config_file_delete(mods, key.ptr);
933
934 git_buf_free(&key);
935
936 cleanup:
937 git_config_file_free(mods);
938 return error;
939 }
940
941 static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival)
942 {
943 git_cvar_t type;
944 const char *val;
945
946 if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
947 giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
948 return -1;
949 }
950
951 if (type == GIT_CVAR_TRUE)
952 val = "true";
953
954 return write_var(repo, name, var, val);
955 }
956
957 const char *git_submodule_branch(git_submodule *submodule)
958 {
959 assert(submodule);
960 return submodule->branch;
961 }
962
963 int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
964 {
965
966 assert(repo && name);
967
968 return write_var(repo, name, "branch", branch);
969 }
970
971 int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
972 {
973 assert(repo && name && url);
974
975 return write_var(repo, name, "url", url);
976 }
977
978 const git_oid *git_submodule_index_id(git_submodule *submodule)
979 {
980 assert(submodule);
981
982 if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
983 return &submodule->index_oid;
984 else
985 return NULL;
986 }
987
988 const git_oid *git_submodule_head_id(git_submodule *submodule)
989 {
990 assert(submodule);
991
992 if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
993 return &submodule->head_oid;
994 else
995 return NULL;
996 }
997
998 const git_oid *git_submodule_wd_id(git_submodule *submodule)
999 {
1000 assert(submodule);
1001
1002 /* load unless we think we have a valid oid */
1003 if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
1004 git_repository *subrepo;
1005
1006 /* calling submodule open grabs the HEAD OID if possible */
1007 if (!git_submodule_open_bare(&subrepo, submodule))
1008 git_repository_free(subrepo);
1009 else
1010 giterr_clear();
1011 }
1012
1013 if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
1014 return &submodule->wd_oid;
1015 else
1016 return NULL;
1017 }
1018
1019 git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
1020 {
1021 assert(submodule);
1022 return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
1023 GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
1024 }
1025
1026 int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
1027 {
1028 assert(repo && name);
1029
1030 return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
1031 }
1032
1033 git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
1034 {
1035 assert(submodule);
1036 return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
1037 GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
1038 }
1039
1040 int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
1041 {
1042 assert(repo && name);
1043
1044 return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
1045 }
1046
1047 git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
1048 git_submodule *submodule)
1049 {
1050 assert(submodule);
1051 return submodule->fetch_recurse;
1052 }
1053
1054 int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
1055 {
1056 assert(repo && name);
1057
1058 return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
1059 }
1060
1061 static int submodule_repo_create(
1062 git_repository **out,
1063 git_repository *parent_repo,
1064 const char *path)
1065 {
1066 int error = 0;
1067 git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
1068 git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
1069 git_repository *subrepo = NULL;
1070
1071 initopt.flags =
1072 GIT_REPOSITORY_INIT_MKPATH |
1073 GIT_REPOSITORY_INIT_NO_REINIT |
1074 GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
1075 GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
1076
1077 /* Workdir: path to sub-repo working directory */
1078 error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
1079 if (error < 0)
1080 goto cleanup;
1081
1082 initopt.workdir_path = workdir.ptr;
1083
1084 /**
1085 * Repodir: path to the sub-repo. sub-repo goes in:
1086 * <repo-dir>/modules/<name>/ with a gitlink in the
1087 * sub-repo workdir directory to that repository.
1088 */
1089 error = git_repository_item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
1090 if (error < 0)
1091 goto cleanup;
1092 error = git_buf_joinpath(&repodir, repodir.ptr, path);
1093 if (error < 0)
1094 goto cleanup;
1095
1096 error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
1097
1098 cleanup:
1099 git_buf_free(&workdir);
1100 git_buf_free(&repodir);
1101
1102 *out = subrepo;
1103
1104 return error;
1105 }
1106
1107 /**
1108 * Callback to override sub-repository creation when
1109 * cloning a sub-repository.
1110 */
1111 static int git_submodule_update_repo_init_cb(
1112 git_repository **out,
1113 const char *path,
1114 int bare,
1115 void *payload)
1116 {
1117 git_submodule *sm;
1118
1119 GIT_UNUSED(bare);
1120
1121 sm = payload;
1122
1123 return submodule_repo_create(out, sm->repo, path);
1124 }
1125
1126 int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
1127 {
1128 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
1129 opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
1130 return 0;
1131 }
1132
1133 int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
1134 {
1135 int error;
1136 unsigned int submodule_status;
1137 git_config *config = NULL;
1138 const char *submodule_url;
1139 git_repository *sub_repo = NULL;
1140 git_remote *remote = NULL;
1141 git_object *target_commit = NULL;
1142 git_buf buf = GIT_BUF_INIT;
1143 git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
1144 git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
1145
1146 assert(sm);
1147
1148 if (_update_options)
1149 memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
1150
1151 GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
1152
1153 /* Copy over the remote callbacks */
1154 memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
1155
1156 /* Get the status of the submodule to determine if it is already initialized */
1157 if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0)
1158 goto done;
1159
1160 /*
1161 * If submodule work dir is not already initialized, check to see
1162 * what we need to do (initialize, clone, return error...)
1163 */
1164 if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
1165 /*
1166 * Work dir is not initialized, check to see if the submodule
1167 * info has been copied into .git/config
1168 */
1169 if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
1170 (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
1171 goto done;
1172
1173 if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) {
1174 /*
1175 * If the error is not "not found" or if it is "not found" and we are not
1176 * initializing the submodule, then return error.
1177 */
1178 if (error != GIT_ENOTFOUND)
1179 goto done;
1180
1181 if (!init) {
1182 giterr_set(GITERR_SUBMODULE, "submodule is not initialized");
1183 error = GIT_ERROR;
1184 goto done;
1185 }
1186
1187 /* The submodule has not been initialized yet - initialize it now.*/
1188 if ((error = git_submodule_init(sm, 0)) < 0)
1189 goto done;
1190
1191 git_config_free(config);
1192 config = NULL;
1193
1194 if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
1195 (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0)
1196 goto done;
1197 }
1198
1199 /** submodule is initialized - now clone it **/
1200 /* override repo creation */
1201 clone_options.repository_cb = git_submodule_update_repo_init_cb;
1202 clone_options.repository_cb_payload = sm;
1203
1204 /*
1205 * Do not perform checkout as part of clone, instead we
1206 * will checkout the specific commit manually.
1207 */
1208 clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
1209
1210 if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
1211 (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
1212 (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
1213 goto done;
1214 } else {
1215 /**
1216 * Work dir is initialized - look up the commit in the parent repository's index,
1217 * update the workdir contents of the subrepository, and set the subrepository's
1218 * head to the new commit.
1219 */
1220 if ((error = git_submodule_open(&sub_repo, sm)) < 0)
1221 goto done;
1222
1223 /* Look up the target commit in the submodule. */
1224 if ((error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0) {
1225 /* If it isn't found then fetch and try again. */
1226 if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
1227 (error = lookup_default_remote(&remote, sub_repo)) < 0 ||
1228 (error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 ||
1229 (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0)
1230 goto done;
1231 }
1232
1233 if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
1234 (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0)
1235 goto done;
1236
1237 /* Invalidate the wd flags as the workdir has been updated. */
1238 sm->flags = sm->flags &
1239 ~(GIT_SUBMODULE_STATUS_IN_WD |
1240 GIT_SUBMODULE_STATUS__WD_OID_VALID |
1241 GIT_SUBMODULE_STATUS__WD_SCANNED);
1242 }
1243
1244 done:
1245 git_buf_free(&buf);
1246 git_config_free(config);
1247 git_object_free(target_commit);
1248 git_remote_free(remote);
1249 git_repository_free(sub_repo);
1250
1251 return error;
1252 }
1253
1254 int git_submodule_init(git_submodule *sm, int overwrite)
1255 {
1256 int error;
1257 const char *val;
1258 git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT;
1259 git_config *cfg = NULL;
1260
1261 if (!sm->url) {
1262 giterr_set(GITERR_SUBMODULE,
1263 "no URL configured for submodule '%s'", sm->name);
1264 return -1;
1265 }
1266
1267 if ((error = git_repository_config(&cfg, sm->repo)) < 0)
1268 return error;
1269
1270 /* write "submodule.NAME.url" */
1271
1272 if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
1273 (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
1274 (error = git_config__update_entry(
1275 cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
1276 goto cleanup;
1277
1278 /* write "submodule.NAME.update" if not default */
1279
1280 val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
1281 NULL : git_submodule_update_to_str(sm->update);
1282
1283 if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 ||
1284 (error = git_config__update_entry(
1285 cfg, key.ptr, val, overwrite != 0, false)) < 0)
1286 goto cleanup;
1287
1288 /* success */
1289
1290 cleanup:
1291 git_config_free(cfg);
1292 git_buf_free(&key);
1293 git_buf_free(&effective_submodule_url);
1294
1295 return error;
1296 }
1297
1298 int git_submodule_sync(git_submodule *sm)
1299 {
1300 int error = 0;
1301 git_config *cfg = NULL;
1302 git_buf key = GIT_BUF_INIT;
1303 git_repository *smrepo = NULL;
1304
1305 if (!sm->url) {
1306 giterr_set(GITERR_SUBMODULE,
1307 "no URL configured for submodule '%s'", sm->name);
1308 return -1;
1309 }
1310
1311 /* copy URL over to config only if it already exists */
1312
1313 if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) &&
1314 !(error = git_buf_printf(&key, "submodule.%s.url", sm->name)))
1315 error = git_config__update_entry(cfg, key.ptr, sm->url, true, true);
1316
1317 /* if submodule exists in the working directory, update remote url */
1318
1319 if (!error &&
1320 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
1321 !(error = git_submodule_open(&smrepo, sm)))
1322 {
1323 git_buf remote_name = GIT_BUF_INIT;
1324
1325 if ((error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
1326 /* return error from reading submodule config */;
1327 else if ((error = lookup_head_remote_key(&remote_name, smrepo)) < 0) {
1328 giterr_clear();
1329 error = git_buf_sets(&key, "remote.origin.url");
1330 } else {
1331 error = git_buf_join3(
1332 &key, '.', "remote", remote_name.ptr, "url");
1333 git_buf_free(&remote_name);
1334 }
1335
1336 if (!error)
1337 error = git_config__update_entry(cfg, key.ptr, sm->url, true, false);
1338
1339 git_repository_free(smrepo);
1340 }
1341
1342 git_buf_free(&key);
1343
1344 return error;
1345 }
1346
1347 static int git_submodule__open(
1348 git_repository **subrepo, git_submodule *sm, bool bare)
1349 {
1350 int error;
1351 git_buf path = GIT_BUF_INIT;
1352 unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
1353 const char *wd;
1354
1355 assert(sm && subrepo);
1356
1357 if (git_repository__ensure_not_bare(
1358 sm->repo, "open submodule repository") < 0)
1359 return GIT_EBAREREPO;
1360
1361 wd = git_repository_workdir(sm->repo);
1362
1363 if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
1364 git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
1365 return -1;
1366
1367 sm->flags = sm->flags &
1368 ~(GIT_SUBMODULE_STATUS_IN_WD |
1369 GIT_SUBMODULE_STATUS__WD_OID_VALID |
1370 GIT_SUBMODULE_STATUS__WD_SCANNED);
1371
1372 if (bare)
1373 flags |= GIT_REPOSITORY_OPEN_BARE;
1374
1375 error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
1376
1377 /* if we opened the submodule successfully, grab HEAD OID, etc. */
1378 if (!error) {
1379 sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
1380 GIT_SUBMODULE_STATUS__WD_SCANNED;
1381
1382 if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
1383 sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
1384 else
1385 giterr_clear();
1386 } else if (git_path_exists(path.ptr)) {
1387 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
1388 GIT_SUBMODULE_STATUS_IN_WD;
1389 } else {
1390 git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
1391
1392 if (git_path_isdir(path.ptr))
1393 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1394 }
1395
1396 git_buf_free(&path);
1397
1398 return error;
1399 }
1400
1401 int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
1402 {
1403 return git_submodule__open(subrepo, sm, true);
1404 }
1405
1406 int git_submodule_open(git_repository **subrepo, git_submodule *sm)
1407 {
1408 return git_submodule__open(subrepo, sm, false);
1409 }
1410
1411 static void submodule_update_from_index_entry(
1412 git_submodule *sm, const git_index_entry *ie)
1413 {
1414 bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
1415
1416 if (!S_ISGITLINK(ie->mode)) {
1417 if (!already_found)
1418 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
1419 } else {
1420 if (already_found)
1421 sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
1422 else
1423 git_oid_cpy(&sm->index_oid, &ie->id);
1424
1425 sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
1426 GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
1427 }
1428 }
1429
1430 static int submodule_update_index(git_submodule *sm)
1431 {
1432 git_index *index;
1433 const git_index_entry *ie;
1434
1435 if (git_repository_index__weakptr(&index, sm->repo) < 0)
1436 return -1;
1437
1438 sm->flags = sm->flags &
1439 ~(GIT_SUBMODULE_STATUS_IN_INDEX |
1440 GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
1441
1442 if (!(ie = git_index_get_bypath(index, sm->path, 0)))
1443 return 0;
1444
1445 submodule_update_from_index_entry(sm, ie);
1446
1447 return 0;
1448 }
1449
1450 static void submodule_update_from_head_data(
1451 git_submodule *sm, mode_t mode, const git_oid *id)
1452 {
1453 if (!S_ISGITLINK(mode))
1454 sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
1455 else {
1456 git_oid_cpy(&sm->head_oid, id);
1457
1458 sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
1459 GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
1460 }
1461 }
1462
1463 static int submodule_update_head(git_submodule *submodule)
1464 {
1465 git_tree *head = NULL;
1466 git_tree_entry *te = NULL;
1467
1468 submodule->flags = submodule->flags &
1469 ~(GIT_SUBMODULE_STATUS_IN_HEAD |
1470 GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
1471
1472 /* if we can't look up file in current head, then done */
1473 if (git_repository_head_tree(&head, submodule->repo) < 0 ||
1474 git_tree_entry_bypath(&te, head, submodule->path) < 0)
1475 giterr_clear();
1476 else
1477 submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
1478
1479 git_tree_entry_free(te);
1480 git_tree_free(head);
1481 return 0;
1482 }
1483
1484 int git_submodule_reload(git_submodule *sm, int force)
1485 {
1486 int error = 0;
1487 git_config *mods;
1488
1489 GIT_UNUSED(force);
1490
1491 assert(sm);
1492
1493 if (!git_repository_is_bare(sm->repo)) {
1494 /* refresh config data */
1495 mods = gitmodules_snapshot(sm->repo);
1496 if (mods != NULL) {
1497 error = submodule_read_config(sm, mods);
1498 git_config_free(mods);
1499
1500 if (error < 0)
1501 return error;
1502 }
1503
1504 /* refresh wd data */
1505 sm->flags &=
1506 ~(GIT_SUBMODULE_STATUS_IN_WD |
1507 GIT_SUBMODULE_STATUS__WD_OID_VALID |
1508 GIT_SUBMODULE_STATUS__WD_FLAGS);
1509
1510 error = submodule_load_from_wd_lite(sm);
1511 }
1512
1513 if (error == 0 && (error = submodule_update_index(sm)) == 0)
1514 error = submodule_update_head(sm);
1515
1516 return error;
1517 }
1518
1519 static void submodule_copy_oid_maybe(
1520 git_oid *tgt, const git_oid *src, bool valid)
1521 {
1522 if (tgt) {
1523 if (valid)
1524 memcpy(tgt, src, sizeof(*tgt));
1525 else
1526 memset(tgt, 0, sizeof(*tgt));
1527 }
1528 }
1529
1530 int git_submodule__status(
1531 unsigned int *out_status,
1532 git_oid *out_head_id,
1533 git_oid *out_index_id,
1534 git_oid *out_wd_id,
1535 git_submodule *sm,
1536 git_submodule_ignore_t ign)
1537 {
1538 unsigned int status;
1539 git_repository *smrepo = NULL;
1540
1541 if (ign == GIT_SUBMODULE_IGNORE_UNSPECIFIED)
1542 ign = sm->ignore;
1543
1544 /* only return location info if ignore == all */
1545 if (ign == GIT_SUBMODULE_IGNORE_ALL) {
1546 *out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
1547 return 0;
1548 }
1549
1550 /* If the user has requested caching submodule state, performing these
1551 * expensive operations (especially `submodule_update_head`, which is
1552 * bottlenecked on `git_repository_head_tree`) eliminates much of the
1553 * advantage. We will, therefore, interpret the request for caching to
1554 * apply here to and skip them.
1555 */
1556
1557 if (sm->repo->submodule_cache == NULL) {
1558 /* refresh the index OID */
1559 if (submodule_update_index(sm) < 0)
1560 return -1;
1561
1562 /* refresh the HEAD OID */
1563 if (submodule_update_head(sm) < 0)
1564 return -1;
1565 }
1566
1567 /* for ignore == dirty, don't scan the working directory */
1568 if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
1569 /* git_submodule_open_bare will load WD OID data */
1570 if (git_submodule_open_bare(&smrepo, sm) < 0)
1571 giterr_clear();
1572 else
1573 git_repository_free(smrepo);
1574 smrepo = NULL;
1575 } else if (git_submodule_open(&smrepo, sm) < 0) {
1576 giterr_clear();
1577 smrepo = NULL;
1578 }
1579
1580 status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
1581
1582 submodule_get_index_status(&status, sm);
1583 submodule_get_wd_status(&status, sm, smrepo, ign);
1584
1585 git_repository_free(smrepo);
1586
1587 *out_status = status;
1588
1589 submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
1590 (sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
1591 submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
1592 (sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
1593 submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
1594 (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
1595
1596 return 0;
1597 }
1598
1599 int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore)
1600 {
1601 git_submodule *sm;
1602 int error;
1603
1604 assert(status && repo && name);
1605
1606 if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
1607 return error;
1608
1609 error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore);
1610 git_submodule_free(sm);
1611
1612 return error;
1613 }
1614
1615 int git_submodule_location(unsigned int *location, git_submodule *sm)
1616 {
1617 assert(location && sm);
1618
1619 return git_submodule__status(
1620 location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
1621 }
1622
1623 /*
1624 * INTERNAL FUNCTIONS
1625 */
1626
1627 static int submodule_alloc(
1628 git_submodule **out, git_repository *repo, const char *name)
1629 {
1630 size_t namelen;
1631 git_submodule *sm;
1632
1633 if (!name || !(namelen = strlen(name))) {
1634 giterr_set(GITERR_SUBMODULE, "invalid submodule name");
1635 return -1;
1636 }
1637
1638 sm = git__calloc(1, sizeof(git_submodule));
1639 GITERR_CHECK_ALLOC(sm);
1640
1641 sm->name = sm->path = git__strdup(name);
1642 if (!sm->name) {
1643 git__free(sm);
1644 return -1;
1645 }
1646
1647 GIT_REFCOUNT_INC(sm);
1648 sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
1649 sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
1650 sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
1651 sm->repo = repo;
1652 sm->branch = NULL;
1653
1654 *out = sm;
1655 return 0;
1656 }
1657
1658 static void submodule_release(git_submodule *sm)
1659 {
1660 if (!sm)
1661 return;
1662
1663 if (sm->repo) {
1664 sm->repo = NULL;
1665 }
1666
1667 if (sm->path != sm->name)
1668 git__free(sm->path);
1669 git__free(sm->name);
1670 git__free(sm->url);
1671 git__free(sm->branch);
1672 git__memzero(sm, sizeof(*sm));
1673 git__free(sm);
1674 }
1675
1676 void git_submodule_free(git_submodule *sm)
1677 {
1678 if (!sm)
1679 return;
1680 GIT_REFCOUNT_DEC(sm, submodule_release);
1681 }
1682
1683 static int submodule_config_error(const char *property, const char *value)
1684 {
1685 giterr_set(GITERR_INVALID,
1686 "invalid value for submodule '%s' property: '%s'", property, value);
1687 return -1;
1688 }
1689
1690 int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
1691 {
1692 int val;
1693
1694 if (git_config_lookup_map_value(
1695 &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
1696 *out = GIT_SUBMODULE_IGNORE_NONE;
1697 return submodule_config_error("ignore", value);
1698 }
1699
1700 *out = (git_submodule_ignore_t)val;
1701 return 0;
1702 }
1703
1704 int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
1705 {
1706 int val;
1707
1708 if (git_config_lookup_map_value(
1709 &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
1710 *out = GIT_SUBMODULE_UPDATE_CHECKOUT;
1711 return submodule_config_error("update", value);
1712 }
1713
1714 *out = (git_submodule_update_t)val;
1715 return 0;
1716 }
1717
1718 int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
1719 {
1720 int val;
1721
1722 if (git_config_lookup_map_value(
1723 &val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) {
1724 *out = GIT_SUBMODULE_RECURSE_YES;
1725 return submodule_config_error("recurse", value);
1726 }
1727
1728 *out = (git_submodule_recurse_t)val;
1729 return 0;
1730 }
1731
1732 static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field)
1733 {
1734 int error;
1735
1736 git_buf_clear(buf);
1737
1738 if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 ||
1739 (error = git_config_get_string(out, cfg, buf->ptr)) < 0)
1740 return error;
1741
1742 return error;
1743 }
1744
1745 static int submodule_read_config(git_submodule *sm, git_config *cfg)
1746 {
1747 git_buf key = GIT_BUF_INIT;
1748 const char *value;
1749 int error, in_config = 0;
1750
1751 /*
1752 * TODO: Look up path in index and if it is present but not a GITLINK
1753 * then this should be deleted (at least to match git's behavior)
1754 */
1755
1756 if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
1757 in_config = 1;
1758 /*
1759 * TODO: if case insensitive filesystem, then the following strcmp
1760 * should be strcasecmp
1761 */
1762 if (strcmp(sm->name, value) != 0) {
1763 if (sm->path != sm->name)
1764 git__free(sm->path);
1765 sm->path = git__strdup(value);
1766 GITERR_CHECK_ALLOC(sm->path);
1767 }
1768 } else if (error != GIT_ENOTFOUND) {
1769 goto cleanup;
1770 }
1771
1772 if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
1773 in_config = 1;
1774 sm->url = git__strdup(value);
1775 GITERR_CHECK_ALLOC(sm->url);
1776 } else if (error != GIT_ENOTFOUND) {
1777 goto cleanup;
1778 }
1779
1780 if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
1781 in_config = 1;
1782 sm->branch = git__strdup(value);
1783 GITERR_CHECK_ALLOC(sm->branch);
1784 } else if (error != GIT_ENOTFOUND) {
1785 goto cleanup;
1786 }
1787
1788 if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) {
1789 in_config = 1;
1790 if ((error = git_submodule_parse_update(&sm->update, value)) < 0)
1791 goto cleanup;
1792 sm->update_default = sm->update;
1793 } else if (error != GIT_ENOTFOUND) {
1794 goto cleanup;
1795 }
1796
1797 if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) {
1798 in_config = 1;
1799 if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
1800 goto cleanup;
1801 sm->fetch_recurse_default = sm->fetch_recurse;
1802 } else if (error != GIT_ENOTFOUND) {
1803 goto cleanup;
1804 }
1805
1806 if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) {
1807 in_config = 1;
1808 if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0)
1809 goto cleanup;
1810 sm->ignore_default = sm->ignore;
1811 } else if (error != GIT_ENOTFOUND) {
1812 goto cleanup;
1813 }
1814
1815 if (in_config)
1816 sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
1817
1818 error = 0;
1819
1820 cleanup:
1821 git_buf_free(&key);
1822 return error;
1823 }
1824
1825 static int submodule_load_each(const git_config_entry *entry, void *payload)
1826 {
1827 lfc_data *data = payload;
1828 const char *namestart, *property;
1829 git_strmap_iter pos;
1830 git_strmap *map = data->map;
1831 git_buf name = GIT_BUF_INIT;
1832 git_submodule *sm;
1833 int error;
1834
1835 if (git__prefixcmp(entry->name, "submodule.") != 0)
1836 return 0;
1837
1838 namestart = entry->name + strlen("submodule.");
1839 property = strrchr(namestart, '.');
1840
1841 if (!property || (property == namestart))
1842 return 0;
1843
1844 property++;
1845
1846 if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
1847 return error;
1848
1849 /*
1850 * Now that we have the submodule's name, we can use that to
1851 * figure out whether it's in the map. If it's not, we create
1852 * a new submodule, load the config and insert it. If it's
1853 * already inserted, we've already loaded it, so we skip.
1854 */
1855 pos = git_strmap_lookup_index(map, name.ptr);
1856 if (git_strmap_valid_index(map, pos)) {
1857 error = 0;
1858 goto done;
1859 }
1860
1861 if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
1862 goto done;
1863
1864 if ((error = submodule_read_config(sm, data->mods)) < 0) {
1865 git_submodule_free(sm);
1866 goto done;
1867 }
1868
1869 git_strmap_insert(map, sm->name, sm, &error);
1870 assert(error != 0);
1871 if (error < 0)
1872 goto done;
1873
1874 error = 0;
1875
1876 done:
1877 git_buf_free(&name);
1878 return error;
1879 }
1880
1881 static int submodule_load_from_wd_lite(git_submodule *sm)
1882 {
1883 git_buf path = GIT_BUF_INIT;
1884
1885 if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
1886 return -1;
1887
1888 if (git_path_isdir(path.ptr))
1889 sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1890
1891 if (git_path_contains(&path, DOT_GIT))
1892 sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
1893
1894 git_buf_free(&path);
1895 return 0;
1896 }
1897
1898 /**
1899 * Returns a snapshot of $WORK_TREE/.gitmodules.
1900 *
1901 * We ignore any errors and just pretend the file isn't there.
1902 */
1903 static git_config *gitmodules_snapshot(git_repository *repo)
1904 {
1905 const char *workdir = git_repository_workdir(repo);
1906 git_config *mods = NULL, *snap = NULL;
1907 git_buf path = GIT_BUF_INIT;
1908
1909 if (workdir != NULL) {
1910 if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1911 return NULL;
1912
1913 if (git_config_open_ondisk(&mods, path.ptr) < 0)
1914 mods = NULL;
1915 }
1916
1917 git_buf_free(&path);
1918
1919 if (mods) {
1920 git_config_snapshot(&snap, mods);
1921 git_config_free(mods);
1922 }
1923
1924 return snap;
1925 }
1926
1927 static git_config_backend *open_gitmodules(
1928 git_repository *repo,
1929 int okay_to_create)
1930 {
1931 const char *workdir = git_repository_workdir(repo);
1932 git_buf path = GIT_BUF_INIT;
1933 git_config_backend *mods = NULL;
1934
1935 if (workdir != NULL) {
1936 if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1937 return NULL;
1938
1939 if (okay_to_create || git_path_isfile(path.ptr)) {
1940 /* git_config_file__ondisk should only fail if OOM */
1941 if (git_config_file__ondisk(&mods, path.ptr) < 0)
1942 mods = NULL;
1943 /* open should only fail here if the file is malformed */
1944 else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
1945 git_config_file_free(mods);
1946 mods = NULL;
1947 }
1948 }
1949 }
1950
1951 git_buf_free(&path);
1952
1953 return mods;
1954 }
1955
1956 /* Lookup name of remote of the local tracking branch HEAD points to */
1957 static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
1958 {
1959 int error;
1960 git_reference *head = NULL;
1961 git_buf upstream_name = GIT_BUF_INIT;
1962
1963 /* lookup and dereference HEAD */
1964 if ((error = git_repository_head(&head, repo)) < 0)
1965 return error;
1966
1967 /**
1968 * If head does not refer to a branch, then return
1969 * GIT_ENOTFOUND to indicate that we could not find
1970 * a remote key for the local tracking branch HEAD points to.
1971 **/
1972 if (!git_reference_is_branch(head)) {
1973 giterr_set(GITERR_INVALID,
1974 "HEAD does not refer to a branch.");
1975 error = GIT_ENOTFOUND;
1976 goto done;
1977 }
1978
1979 /* lookup remote tracking branch of HEAD */
1980 if ((error = git_branch_upstream_name(
1981 &upstream_name,
1982 repo,
1983 git_reference_name(head))) < 0)
1984 goto done;
1985
1986 /* lookup remote of remote tracking branch */
1987 if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0)
1988 goto done;
1989
1990 done:
1991 git_buf_free(&upstream_name);
1992 git_reference_free(head);
1993
1994 return error;
1995 }
1996
1997 /* Lookup the remote of the local tracking branch HEAD points to */
1998 static int lookup_head_remote(git_remote **remote, git_repository *repo)
1999 {
2000 int error;
2001 git_buf remote_name = GIT_BUF_INIT;
2002
2003 /* lookup remote of remote tracking branch name */
2004 if (!(error = lookup_head_remote_key(&remote_name, repo)))
2005 error = git_remote_lookup(remote, repo, remote_name.ptr);
2006
2007 git_buf_free(&remote_name);
2008
2009 return error;
2010 }
2011
2012 /* Lookup remote, either from HEAD or fall back on origin */
2013 static int lookup_default_remote(git_remote **remote, git_repository *repo)
2014 {
2015 int error = lookup_head_remote(remote, repo);
2016
2017 /* if that failed, use 'origin' instead */
2018 if (error == GIT_ENOTFOUND)
2019 error = git_remote_lookup(remote, repo, "origin");
2020
2021 if (error == GIT_ENOTFOUND)
2022 giterr_set(
2023 GITERR_SUBMODULE,
2024 "cannot get default remote for submodule - no local tracking "
2025 "branch for HEAD and origin does not exist");
2026
2027 return error;
2028 }
2029
2030 static int get_url_base(git_buf *url, git_repository *repo)
2031 {
2032 int error;
2033 git_remote *remote = NULL;
2034
2035 if (!(error = lookup_default_remote(&remote, repo))) {
2036 error = git_buf_sets(url, git_remote_url(remote));
2037 git_remote_free(remote);
2038 }
2039 else if (error == GIT_ENOTFOUND) {
2040 /* if repository does not have a default remote, use workdir instead */
2041 giterr_clear();
2042 error = git_buf_sets(url, git_repository_workdir(repo));
2043 }
2044
2045 return error;
2046 }
2047
2048 static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
2049 {
2050 const git_oid *head_oid = git_submodule_head_id(sm);
2051 const git_oid *index_oid = git_submodule_index_id(sm);
2052
2053 *status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
2054
2055 if (!head_oid) {
2056 if (index_oid)
2057 *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
2058 }
2059 else if (!index_oid)
2060 *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
2061 else if (!git_oid_equal(head_oid, index_oid))
2062 *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
2063 }
2064
2065
2066 static void submodule_get_wd_status(
2067 unsigned int *status,
2068 git_submodule *sm,
2069 git_repository *sm_repo,
2070 git_submodule_ignore_t ign)
2071 {
2072 const git_oid *index_oid = git_submodule_index_id(sm);
2073 const git_oid *wd_oid =
2074 (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
2075 git_tree *sm_head = NULL;
2076 git_index *index = NULL;
2077 git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
2078 git_diff *diff;
2079
2080 *status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
2081
2082 if (!index_oid) {
2083 if (wd_oid)
2084 *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
2085 }
2086 else if (!wd_oid) {
2087 if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
2088 (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
2089 *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
2090 else
2091 *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
2092 }
2093 else if (!git_oid_equal(index_oid, wd_oid))
2094 *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
2095
2096 /* if we have no repo, then we're done */
2097 if (!sm_repo)
2098 return;
2099
2100 /* the diffs below could be optimized with an early termination
2101 * option to the git_diff functions, but for now this is sufficient
2102 * (and certainly no worse that what core git does).
2103 */
2104
2105 if (ign == GIT_SUBMODULE_IGNORE_NONE)
2106 opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
2107
2108 (void)git_repository_index__weakptr(&index, sm_repo);
2109
2110 /* if we don't have an unborn head, check diff with index */
2111 if (git_repository_head_tree(&sm_head, sm_repo) < 0)
2112 giterr_clear();
2113 else {
2114 /* perform head to index diff on submodule */
2115 if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
2116 giterr_clear();
2117 else {
2118 if (git_diff_num_deltas(diff) > 0)
2119 *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
2120 git_diff_free(diff);
2121 diff = NULL;
2122 }
2123
2124 git_tree_free(sm_head);
2125 }
2126
2127 /* perform index-to-workdir diff on submodule */
2128 if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
2129 giterr_clear();
2130 else {
2131 size_t untracked =
2132 git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
2133
2134 if (untracked > 0)
2135 *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
2136
2137 if (git_diff_num_deltas(diff) != untracked)
2138 *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
2139
2140 git_diff_free(diff);
2141 diff = NULL;
2142 }
2143 }