diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-11-06 18:16:46 +0300 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-11-07 10:33:27 +0300 |
commit | 62a617dc683c1e73eebd0e1b6209f76748e67ed4 (patch) | |
tree | 5893fe2b801d96825582d8a461b50e80f4a2a43a /src | |
parent | f1a7906fdf0657fba88449bdab07237847f36670 (diff) |
iterator: submodules are determined by an index or tree
We cannot know from looking at .gitmodules whether a directory is a
submodule or not. We need the index or tree we are comparing against to
tell us. Otherwise we have to assume the entry in .gitmodules is stale
or otherwise invalid.
Thus we pass the index of the repository into the workdir iterator, even
if we do not want to compare against it. This follows what git does,
which even for `git diff <tree>`, it will consider staged submodules as
such.
Diffstat (limited to 'src')
-rw-r--r-- | src/checkout.c | 12 | ||||
-rw-r--r-- | src/checkout.h | 1 | ||||
-rw-r--r-- | src/diff.c | 9 | ||||
-rw-r--r-- | src/index.c | 2 | ||||
-rw-r--r-- | src/iterator.c | 71 | ||||
-rw-r--r-- | src/iterator.h | 6 | ||||
-rw-r--r-- | src/pathspec.c | 2 |
7 files changed, 94 insertions, 9 deletions
diff --git a/src/checkout.c b/src/checkout.c index 8203c39ea..44e2f3b27 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -2242,6 +2242,7 @@ cleanup: int git_checkout_iterator( git_iterator *target, + git_index *index, const git_checkout_options *opts) { int error = 0; @@ -2278,7 +2279,7 @@ int git_checkout_iterator( if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir_ext( - &workdir, data.repo, data.opts.target_directory, + &workdir, data.repo, data.opts.target_directory, index, NULL, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_tree( @@ -2388,7 +2389,7 @@ int git_checkout_index( GIT_REFCOUNT_INC(index); if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL))) - error = git_checkout_iterator(index_i, opts); + error = git_checkout_iterator(index_i, index, opts); if (owned) GIT_REFCOUNT_OWN(index, NULL); @@ -2405,6 +2406,7 @@ int git_checkout_tree( const git_checkout_options *opts) { int error; + git_index *index; git_tree *tree = NULL; git_iterator *tree_i = NULL; @@ -2439,10 +2441,14 @@ int git_checkout_tree( } } + if ((error = git_repository_index(&index, repo)) < 0) + return error; + if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL))) - error = git_checkout_iterator(tree_i, opts); + error = git_checkout_iterator(tree_i, index, opts); git_iterator_free(tree_i); + git_index_free(index); git_tree_free(tree); return error; diff --git a/src/checkout.h b/src/checkout.h index f1fe69628..60aa29b26 100644 --- a/src/checkout.h +++ b/src/checkout.h @@ -19,6 +19,7 @@ */ extern int git_checkout_iterator( git_iterator *target, + git_index *index, const git_checkout_options *opts); #endif diff --git a/src/diff.c b/src/diff.c index 375d4cb13..89b3b77f3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1214,7 +1214,7 @@ int git_diff_index_to_workdir( DIFF_FROM_ITERATORS( git_iterator_for_index(&a, index, 0, pfx, pfx), git_iterator_for_workdir( - &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX)) @@ -1230,15 +1230,20 @@ int git_diff_tree_to_workdir( const git_diff_options *opts) { int error = 0; + git_index *index; assert(diff && repo); + if ((error = git_repository_index(&index, repo))) + return error; + DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), git_iterator_for_workdir( - &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + &b, repo, index, old_tree, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); + git_index_free(index); return error; } diff --git a/src/index.c b/src/index.c index 8a5bf61c1..d3bc081a5 100644 --- a/src/index.c +++ b/src/index.c @@ -2441,7 +2441,7 @@ int git_index_add_all( goto cleanup; if ((error = git_iterator_for_workdir( - &wditer, repo, 0, ps.prefix, ps.prefix)) < 0) + &wditer, repo, NULL, NULL, 0, ps.prefix, ps.prefix)) < 0) goto cleanup; while (!(error = git_iterator_advance(&wd, wditer))) { diff --git a/src/iterator.c b/src/iterator.c index c664f17cd..d8a17a716 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1268,6 +1268,16 @@ typedef struct { fs_iterator fi; git_ignores ignores; int is_ignored; + + /* + * We may have a tree or the index+snapshot to compare against + * when checking for submodules. + */ + git_tree *tree; + git_index *index; + git_vector index_snapshot; + git_vector_cmp entry_srch; + } workdir_iterator; GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) @@ -1289,6 +1299,49 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) return (len == 4 || path->ptr[len - 5] == '/'); } +/** + * Figure out if an entry is a submodule. + * + * We consider it a submodule if the path is listed as a submodule in + * either the tree or the index. + */ +static int is_submodule(workdir_iterator *wi, git_path_with_stat *ie) +{ + int error, is_submodule = 0; + + if (wi->tree) { + git_tree_entry *e; + + /* remove the trailing slash for finding */ + ie->path[ie->path_len-1] = '\0'; + error = git_tree_entry_bypath(&e, wi->tree, ie->path); + ie->path[ie->path_len-1] = '/'; + if (error < 0 && error != GIT_ENOTFOUND) + return 0; + if (!error) { + is_submodule = e->attr == GIT_FILEMODE_COMMIT; + git_tree_entry_free(e); + } + } + + if (!is_submodule && wi->index) { + git_index_entry *e; + size_t pos; + + error = git_index_snapshot_find(&pos, &wi->index_snapshot, wi->entry_srch, ie->path, ie->path_len-1, 0); + if (error < 0 && error != GIT_ENOTFOUND) + return 0; + + if (!error) { + e = git_vector_get(&wi->index_snapshot, pos); + + is_submodule = e->mode == GIT_FILEMODE_COMMIT; + } + } + + return is_submodule; +} + static int workdir_iterator__enter_dir(fs_iterator *fi) { workdir_iterator *wi = (workdir_iterator *)fi; @@ -1321,7 +1374,7 @@ static int workdir_iterator__enter_dir(fs_iterator *fi) if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path)) continue; - if (git_submodule__is_submodule(fi->base.repo, entry->path)) { + if (is_submodule(wi, entry)) { entry->st.st_mode = GIT_FILEMODE_COMMIT; entry->path_len--; entry->path[entry->path_len] = '\0'; @@ -1363,6 +1416,8 @@ static int workdir_iterator__update_entry(fs_iterator *fi) static void workdir_iterator__free(git_iterator *self) { workdir_iterator *wi = (workdir_iterator *)self; + if (wi->index) + git_index_snapshot_release(&wi->index_snapshot, wi->index); fs_iterator__free(self); git_ignore__free(&wi->ignores); } @@ -1371,6 +1426,8 @@ int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, const char *repo_workdir, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end) @@ -1402,6 +1459,18 @@ int git_iterator_for_workdir_ext( return error; } + if (tree && (error = git_object_dup((git_object **)&wi->tree, (git_object *)tree)) < 0) + return error; + + wi->index = index; + if (index && (error = git_index_snapshot_new(&wi->index_snapshot, index)) < 0) { + git_iterator_free((git_iterator *)wi); + return error; + } + wi->entry_srch = iterator__ignore_case(wi) ? + git_index_entry_isrch : git_index_entry_srch; + + /* try to look up precompose and set flag if appropriate */ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0) giterr_clear(); diff --git a/src/iterator.h b/src/iterator.h index d88ad5191..1520bffc2 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -86,6 +86,8 @@ extern int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, const char *repo_workdir, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end); @@ -96,11 +98,13 @@ extern int git_iterator_for_workdir_ext( GIT_INLINE(int) git_iterator_for_workdir( git_iterator **out, git_repository *repo, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end) { - return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end); + return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, flags, start, end); } /* for filesystem iterators, you have to explicitly pass in the ignore_case diff --git a/src/pathspec.c b/src/pathspec.c index a01d74f07..8b469f717 100644 --- a/src/pathspec.c +++ b/src/pathspec.c @@ -524,7 +524,7 @@ int git_pathspec_match_workdir( assert(repo); if (!(error = git_iterator_for_workdir( - &iter, repo, pathspec_match_iter_flags(flags), NULL, NULL))) { + &iter, repo, NULL, NULL, pathspec_match_iter_flags(flags), NULL, NULL))) { error = pathspec_match_from_iterator(out, iter, flags, ps); |