Fix checkout of submodules with no .gitmodules It is possible for there to be a submodule in a repository with no .gitmodules file (for example, if the user forgot to commit the .gitmodules file). In this case, core Git will just create an empty directory as a placeholder for the submodule but otherwise ignore it. We were generating an error and stopping the checkout. This makes our behavior match that of core git.
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
diff --git a/include/git2/index.h b/include/git2/index.h
index d23c3a8..bde38a9 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -57,6 +57,8 @@ GIT_BEGIN_DECL
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
+#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
+
/** Time used in a git index entry */
typedef struct {
git_time_t seconds;
diff --git a/src/checkout.c b/src/checkout.c
index 4d019db..5820f62 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -820,6 +820,31 @@ static int checkout_update_index(
return git_index_add(data->index, &entry);
}
+static int checkout_submodule_update_index(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ struct stat st;
+
+ /* update the index unless prevented */
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
+ return 0;
+
+ git_buf_truncate(&data->path, data->workdir_len);
+ if (git_buf_puts(&data->path, file->path) < 0)
+ return -1;
+
+ if (p_stat(git_buf_cstr(&data->path), &st) < 0) {
+ giterr_set(
+ GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
+ return GIT_ENOTFOUND;
+ }
+
+ st.st_mode = GIT_FILEMODE_COMMIT;
+
+ return checkout_update_index(data, file, &st);
+}
+
static int checkout_submodule(
checkout_data *data,
const git_diff_file *file)
@@ -836,8 +861,17 @@ static int checkout_submodule(
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
return error;
- if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0)
+ if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) {
+ /* I've observed repos with submodules in the tree that do not
+ * have a .gitmodules - core Git just makes an empty directory
+ */
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ return checkout_submodule_update_index(data, file);
+ }
+
return error;
+ }
/* TODO: Support checkout_strategy options. Two circumstances:
* 1 - submodule already checked out, but we need to move the HEAD
@@ -848,26 +882,7 @@ static int checkout_submodule(
* command should probably be able to. Do we need a submodule callback?
*/
- /* update the index unless prevented */
- if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) {
- struct stat st;
-
- git_buf_truncate(&data->path, data->workdir_len);
- if (git_buf_puts(&data->path, file->path) < 0)
- return -1;
-
- if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) {
- giterr_set(
- GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
- return error;
- }
-
- st.st_mode = GIT_FILEMODE_COMMIT;
-
- error = checkout_update_index(data, file, &st);
- }
-
- return error;
+ return checkout_submodule_update_index(data, file);
}
static void report_progress(
diff --git a/src/index.c b/src/index.c
index 53edb48..bdbe545 100644
--- a/src/index.c
+++ b/src/index.c
@@ -106,7 +106,7 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc);
GIT_INLINE(int) index_entry_stage(const git_index_entry *entry)
{
- return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
+ return GIT_IDXENTRY_STAGE(entry);
}
static int index_srch(const void *key, const void *array_member)