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