#include "worktree.h"
#include "parse-options.h"
#include "object-store.h"
+#include "commit-reach.h"
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
return 0;
}
+/*
+ * Check if the .gitmodules file is safe to write.
+ *
+ * Writing to the .gitmodules file requires that the file exists in the
+ * working tree or, if it doesn't, that a brand new .gitmodules file is going
+ * to be created (i.e. it's neither in the index nor in the current branch).
+ *
+ * It is not safe to write to .gitmodules if it's not in the working tree but
+ * it is in the index or in the current branch, because writing new values
+ * (and staging them) would blindly overwrite ALL the old content.
+ */
+int is_writing_gitmodules_ok(void)
+{
+ struct object_id oid;
+ return file_exists(GITMODULES_FILE) ||
+ (get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
+}
+
/*
* Check if the .gitmodules file has unstaged modifications. This must be
* checked before allowing modifications to the .gitmodules file with the
{
struct strbuf entry = STRBUF_INIT;
const struct submodule *submodule;
+ int ret;
if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
return -1;
strbuf_addstr(&entry, "submodule.");
strbuf_addstr(&entry, submodule->name);
strbuf_addstr(&entry, ".path");
- if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
- /* Maybe the user already did that, don't error out here */
- warning(_("Could not update .gitmodules entry %s"), entry.buf);
- strbuf_release(&entry);
- return -1;
- }
+ ret = config_set_in_gitmodules_file_gently(entry.buf, newpath);
strbuf_release(&entry);
- return 0;
+ return ret;
}
/*
{
struct commit_list *list;
- init_revisions(rev, NULL);
+ repo_init_revisions(the_repository, rev, NULL);
setup_revisions(0, NULL, rev, NULL);
rev->left_right = 1;
rev->first_parent_only = 1;
fast_backward = 1;
}
- if (!oidcmp(one, two)) {
+ if (oideq(one, two)) {
strbuf_release(&sb);
return;
}
}
struct collect_changed_submodules_cb_data {
+ struct repository *repo;
struct string_list *changed;
const struct object_id *commit_oid;
};
if (!S_ISGITLINK(p->two->mode))
continue;
- submodule = submodule_from_path(the_repository,
+ submodule = submodule_from_path(me->repo,
commit_oid, p->two->path);
if (submodule)
name = submodule->name;
name = default_name_or_path(p->two->path);
/* make sure name does not collide with existing one */
if (name)
- submodule = submodule_from_name(the_repository,
+ submodule = submodule_from_name(me->repo,
commit_oid, name);
if (submodule) {
warning("Submodule in commit %s at path: "
* have a corresponding 'struct oid_array' (in the 'util' field) which lists
* what the submodule pointers were updated to during the change.
*/
-static void collect_changed_submodules(struct string_list *changed,
+static void collect_changed_submodules(struct repository *r,
+ struct string_list *changed,
struct argv_array *argv)
{
struct rev_info rev;
const struct commit *commit;
- init_revisions(&rev, NULL);
+ repo_init_revisions(r, &rev, NULL);
setup_revisions(argv->argc, argv->argv, &rev, NULL);
if (prepare_revision_walk(&rev))
die("revision walk setup failed");
while ((commit = get_revision(&rev))) {
struct rev_info diff_rev;
struct collect_changed_submodules_cb_data data;
+ data.repo = r;
data.changed = changed;
data.commit_oid = &commit->object.oid;
- init_revisions(&diff_rev, NULL);
+ repo_init_revisions(r, &diff_rev, NULL);
diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
diff_rev.diffopt.format_callback_data = &data;
}
struct has_commit_data {
+ struct repository *repo;
int result;
const char *path;
};
{
struct has_commit_data *cb = data;
- enum object_type type = oid_object_info(the_repository, oid, NULL);
+ enum object_type type = oid_object_info(cb->repo, oid, NULL);
switch (type) {
case OBJ_COMMIT:
}
}
-static int submodule_has_commits(const char *path, struct oid_array *commits)
+static int submodule_has_commits(struct repository *r,
+ const char *path,
+ struct oid_array *commits)
{
- struct has_commit_data has_commit = { 1, path };
+ struct has_commit_data has_commit = { r, 1, path };
/*
* Perform a cheap, but incorrect check for the existence of 'commits'.
return has_commit.result;
}
-static int submodule_needs_pushing(const char *path, struct oid_array *commits)
+static int submodule_needs_pushing(struct repository *r,
+ const char *path,
+ struct oid_array *commits)
{
- if (!submodule_has_commits(path, commits))
+ if (!submodule_has_commits(r, path, commits))
/*
* NOTE: We do consider it safe to return "no" here. The
* correct answer would be "We do not know" instead of
return 0;
}
-int find_unpushed_submodules(struct oid_array *commits,
- const char *remotes_name, struct string_list *needs_pushing)
+int find_unpushed_submodules(struct repository *r,
+ struct oid_array *commits,
+ const char *remotes_name,
+ struct string_list *needs_pushing)
{
struct string_list submodules = STRING_LIST_INIT_DUP;
struct string_list_item *name;
argv_array_push(&argv, "--not");
argv_array_pushf(&argv, "--remotes=%s", remotes_name);
- collect_changed_submodules(&submodules, &argv);
+ collect_changed_submodules(r, &submodules, &argv);
for_each_string_list_item(name, &submodules) {
struct oid_array *commits = name->util;
const struct submodule *submodule;
const char *path = NULL;
- submodule = submodule_from_name(the_repository, &null_oid, name->string);
+ submodule = submodule_from_name(r, &null_oid, name->string);
if (submodule)
path = submodule->path;
else
if (!path)
continue;
- if (submodule_needs_pushing(path, commits))
+ if (submodule_needs_pushing(r, path, commits))
string_list_insert(needs_pushing, path);
}
die("process for submodule '%s' failed", path);
}
-int push_unpushed_submodules(struct oid_array *commits,
+int push_unpushed_submodules(struct repository *r,
+ struct oid_array *commits,
const struct remote *remote,
const struct refspec *rs,
const struct string_list *push_options,
int i, ret = 1;
struct string_list needs_pushing = STRING_LIST_INIT_DUP;
- if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
+ if (!find_unpushed_submodules(r, commits,
+ remote->name, &needs_pushing))
return 1;
/*
oid_array_append(&ref_tips_after_fetch, oid);
}
-static void calculate_changed_submodule_paths(void)
+static void calculate_changed_submodule_paths(struct repository *r)
{
struct argv_array argv = ARGV_ARRAY_INIT;
struct string_list changed_submodules = STRING_LIST_INIT_DUP;
const struct string_list_item *name;
/* No need to check if there are no submodules configured */
- if (!submodule_from_path(the_repository, NULL, NULL))
+ if (!submodule_from_path(r, NULL, NULL))
return;
argv_array_push(&argv, "--"); /* argv[0] program name */
* Collect all submodules (whether checked out or not) for which new
* commits have been recorded upstream in "changed_submodule_names".
*/
- collect_changed_submodules(&changed_submodules, &argv);
+ collect_changed_submodules(r, &changed_submodules, &argv);
for_each_string_list_item(name, &changed_submodules) {
struct oid_array *commits = name->util;
const struct submodule *submodule;
const char *path = NULL;
- submodule = submodule_from_name(the_repository, &null_oid, name->string);
+ submodule = submodule_from_name(r, &null_oid, name->string);
if (submodule)
path = submodule->path;
else
if (!path)
continue;
- if (!submodule_has_commits(path, commits))
+ if (!submodule_has_commits(r, path, commits))
string_list_append(&changed_submodule_names, name->string);
}
initialized_fetch_ref_tips = 0;
}
-int submodule_touches_in_range(struct object_id *excl_oid,
+int submodule_touches_in_range(struct repository *r,
+ struct object_id *excl_oid,
struct object_id *incl_oid)
{
struct string_list subs = STRING_LIST_INIT_DUP;
int ret;
/* No need to check if there are no submodules configured */
- if (!submodule_from_path(the_repository, NULL, NULL))
+ if (!submodule_from_path(r, NULL, NULL))
return 0;
argv_array_push(&args, "--"); /* args[0] program name */
argv_array_push(&args, oid_to_hex(excl_oid));
}
- collect_changed_submodules(&subs, &args);
+ collect_changed_submodules(r, &subs, &args);
ret = subs.nr;
argv_array_clear(&args);
argv_array_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
- calculate_changed_submodule_paths();
+ calculate_changed_submodule_paths(r);
run_processes_parallel(max_parallel_jobs,
get_next_submodule,
fetch_start_failure,
return ret;
}
+void submodule_unset_core_worktree(const struct submodule *sub)
+{
+ char *config_path = xstrfmt("%s/modules/%s/config",
+ get_git_common_dir(), sub->name);
+
+ if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+ warning(_("Could not unset core.worktree setting in submodule '%s'"),
+ sub->path);
+
+ free(config_path);
+}
+
static const char *get_super_prefix_or_empty(void)
{
const char *s = get_super_prefix();
if (is_empty_dir(path))
rmdir_or_warn(path);
+
+ submodule_unset_core_worktree(sub);
}
}
out: