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