Stefan Beller <sbeller@xxxxxxxxxx> writes: > Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> > --- > entry.c | 30 ++++++++++++++++++++++++++++++ > 1 file changed, 30 insertions(+) > > diff --git a/entry.c b/entry.c > index c6eea240b6..d2b512da90 100644 > --- a/entry.c > +++ b/entry.c > @@ -2,6 +2,7 @@ > #include "blob.h" > #include "dir.h" > #include "streaming.h" > +#include "submodule.h" > > static void create_directories(const char *path, int path_len, > const struct checkout *state) > @@ -146,6 +147,7 @@ static int write_entry(struct cache_entry *ce, > unsigned long size; > size_t wrote, newsize = 0; > struct stat st; > + const struct submodule *sub; > > if (ce_mode_s_ifmt == S_IFREG) { > struct stream_filter *filter = get_stream_filter(ce->name, > @@ -203,6 +205,10 @@ static int write_entry(struct cache_entry *ce, > return error("cannot create temporary submodule %s", path); > if (mkdir(path, 0777) < 0) > return error("cannot create submodule directory %s", path); > + sub = submodule_from_ce(ce); > + if (sub) > + return submodule_move_head(ce->name, > + NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE); > break; > default: > return error("unknown file mode for %s in index", path); > @@ -259,7 +265,31 @@ int checkout_entry(struct cache_entry *ce, > strbuf_add(&path, ce->name, ce_namelen(ce)); > > if (!check_path(path.buf, path.len, &st, state->base_dir_len)) { > + const struct submodule *sub; > unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); > + /* > + * Needs to be checked before !changed returns early, > + * as the possibly empty directory was not changed > + */ > + sub = submodule_from_ce(ce); > + if (sub) { > + int err; > + if (!is_submodule_populated_gently(ce->name, &err)) { > + struct stat sb; > + if (lstat(ce->name, &sb)) > + die(_("could not stat file '%s'"), ce->name); > + if (!(st.st_mode & S_IFDIR)) > + unlink_or_warn(ce->name); We found that the path ce->name is supposed to be a submodule that is checked out, we found that the submodule is not yet populated, we tried to make sure what is on that path, and its failure would cause us to die(). All that is sensible. Then we want to make sure the filesystem entity at the path currently is a directory and otherwise unlink (i.e. we may have an unrelated file that is not tracked there, or perhaps the user earlier decided that replacing the submodule with a single file is a good idea, but then getting rid of the change with "checkout -f" before doing "git add" on that file). That is also sensible. But if that unlink fails, shouldn't we die, just like the case where we cannot tell what is at the path ce->name? And if that unlink succeeds, instead of having an empty directory, we start the "move-head" call to switch from NULL to ce->oid without having any directory. Wouldn't we want to mkdir() here (and remove mkdir() in submodule_move_head() if there is one---if there isn't then I do not think this codepath would work)? If we had a directory here, but if that directory is not empty, should we proceed? I am assuming (I haven't carefully read "move-head") that it is OK because the "run an equivalent of 'checkout --detach ce->oid'" done by "move-head" would notice a possible information loss to overwrite an untracked path (everything is "untracked" as the head moves from NULL to ce->oid in this case), and prevent it from happening. Am I reading the design correctly? > + return submodule_move_head(ce->name, > + NULL, oid_to_hex(&ce->oid), > + SUBMODULE_MOVE_HEAD_FORCE); > + } else > + return submodule_move_head(ce->name, > + "HEAD", oid_to_hex(&ce->oid), > + SUBMODULE_MOVE_HEAD_FORCE); > + } > + > if (!changed) > return 0; > if (!state->force) {