summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-05-15 14:54:02 -0700
committerRussell Belfer <rb@github.com>2013-05-15 14:54:02 -0700
commitdcb0f7c061554de06db5879361b22eab3517a4ee (patch)
tree2221b740034cf3bf6236daa60242bb966d7c76cd
parent55d3a39098bfc513b12ad6cb56658cb2f87e6a91 (diff)
downloadlibgit2-dcb0f7c061554de06db5879361b22eab3517a4ee.tar.gz
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.
-rw-r--r--include/git2/index.h2
-rw-r--r--src/checkout.c57
-rw-r--r--src/index.c2
3 files changed, 39 insertions, 22 deletions
diff --git a/include/git2/index.h b/include/git2/index.h
index d23c3a8ea..bde38a9dd 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 4d019dbd1..5820f626a 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 53edb4874..bdbe545bf 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)