Merge pull request #2226 from libgit2/rb/submodule-sorting-fix Fix submodule sort order during iteration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
diff --git a/src/iterator.c b/src/iterator.c
index e9ec652..a7a4491 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -10,7 +10,7 @@
#include "index.h"
#include "ignore.h"
#include "buffer.h"
-#include "git2/submodule.h"
+#include "submodule.h"
#include <ctype.h>
#define ITERATOR_SET_CB(P,NAME_LC) do { \
@@ -1275,14 +1275,38 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
static int workdir_iterator__enter_dir(fs_iterator *fi)
{
+ fs_iterator_frame *ff = fi->stack;
+ size_t pos;
+ git_path_with_stat *entry;
+ bool found_submodules = false;
+
/* only push new ignores if this is not top level directory */
- if (fi->stack->next != NULL) {
+ if (ff->next != NULL) {
workdir_iterator *wi = (workdir_iterator *)fi;
ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
(void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
}
+ /* convert submodules to GITLINK and remove trailing slashes */
+ git_vector_foreach(&ff->entries, pos, entry) {
+ if (S_ISDIR(entry->st.st_mode) &&
+ git_submodule__is_submodule(fi->base.repo, entry->path))
+ {
+ entry->st.st_mode = GIT_FILEMODE_COMMIT;
+ entry->path_len--;
+ entry->path[entry->path_len] = '\0';
+ found_submodules = true;
+ }
+ }
+
+ /* if we renamed submodules, re-sort and re-seek to start */
+ if (found_submodules) {
+ git_vector_set_sorted(&ff->entries, 0);
+ git_vector_sort(&ff->entries);
+ fs_iterator__seek_frame_start(fi, ff);
+ }
+
return 0;
}
@@ -1295,7 +1319,6 @@ static int workdir_iterator__leave_dir(fs_iterator *fi)
static int workdir_iterator__update_entry(fs_iterator *fi)
{
- int error = 0;
workdir_iterator *wi = (workdir_iterator *)fi;
/* skip over .git entries */
@@ -1305,20 +1328,6 @@ static int workdir_iterator__update_entry(fs_iterator *fi)
/* reset is_ignored since we haven't checked yet */
wi->is_ignored = -1;
- /* check if apparent tree entries are actually submodules */
- if (fi->entry.mode != GIT_FILEMODE_TREE)
- return 0;
-
- error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
- if (error < 0)
- giterr_clear();
-
- /* mark submodule as GITLINK and remove slash */
- if (!error) {
- fi->entry.mode = S_IFGITLINK;
- fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
- }
-
return 0;
}
diff --git a/src/merge_file.c b/src/merge_file.c
index ab9ca41..ff03644 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -117,7 +117,7 @@ static int git_merge_file__from_inputs(
memset(out, 0x0, sizeof(git_merge_file_result));
- merge_file_normalize_opts(&options, given_opts);
+ merge_file_normalize_opts(&options, given_opts);
memset(&xmparam, 0x0, sizeof(xmparam_t));
@@ -165,7 +165,7 @@ static int git_merge_file__from_inputs(
}
out->automergeable = (xdl_result == 0);
- out->ptr = (unsigned char *)mmbuffer.ptr;
+ out->ptr = (const char *)mmbuffer.ptr;
out->len = mmbuffer.size;
out->mode = merge_file_best_mode(ancestor, ours, theirs);
diff --git a/src/submodule.c b/src/submodule.c
index e1500b8..fdcc225 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -135,6 +135,21 @@ static int submodule_lookup(
* PUBLIC APIS
*/
+bool git_submodule__is_submodule(git_repository *repo, const char *name)
+{
+ git_strmap *map;
+
+ if (load_submodule_config(repo, false) < 0) {
+ giterr_clear();
+ return false;
+ }
+
+ if (!(map = repo->submodules))
+ return false;
+
+ return git_strmap_valid_index(map, git_strmap_lookup_index(map, name));
+}
+
int git_submodule_lookup(
git_submodule **out, /* NULL if user only wants to test existence */
git_repository *repo,
diff --git a/src/submodule.h b/src/submodule.h
index 053cb61..1c41897 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -119,6 +119,9 @@ enum {
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
+/* Internal submodule check does not attempt to refresh cached data */
+extern bool git_submodule__is_submodule(git_repository *repo, const char *name);
+
/* Internal status fn returns status and optionally the various OIDs */
extern int git_submodule__status(
unsigned int *out_status,
diff --git a/tests/diff/submodules.c b/tests/diff/submodules.c
index ead5c71..2881f74 100644
--- a/tests/diff/submodules.c
+++ b/tests/diff/submodules.c
@@ -182,6 +182,8 @@ void test_diff_submodules__submod2_index_to_wd(void)
"<UNTRACKED>", /* not */
"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "<UNTRACKED>", /* sm_changed_head- */
+ "<UNTRACKED>", /* sm_changed_head_ */
"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
@@ -190,6 +192,10 @@ void test_diff_submodules__submod2_index_to_wd(void)
g_repo = setup_fixture_submod2();
+ /* bracket existing submodule with similarly named items */
+ cl_git_mkfile("submod2/sm_changed_head-", "hello");
+ cl_git_mkfile("submod2/sm_changed_head_", "hello");
+
opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
opts.old_prefix = "a"; opts.new_prefix = "b";