#include "string-list.h"
#include "sha1-array.h"
#include "argv-array.h"
+#include "blob.h"
static struct string_list config_name_for_path;
static struct string_list config_fetch_recurse_submodules_for_name;
*/
static int gitmodules_is_unmerged;
+/*
+ * This flag is set if the .gitmodules file had unstaged modifications on
+ * startup. This must be checked before allowing modifications to the
+ * .gitmodules file with the intention to stage them later, because when
+ * continuing we would stage the modifications the user didn't stage herself
+ * too. That might change in a future version when we learn to stage the
+ * changes we do ourselves without staging any previous modifications.
+ */
+static int gitmodules_is_modified;
+
+
+int is_staging_gitmodules_ok(void)
+{
+ return !gitmodules_is_modified;
+}
+
+/*
+ * Try to update the "path" entry in the "submodule.<name>" section of the
+ * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<oldpath> setting was found and we could update it.
+ */
+int update_path_in_gitmodules(const char *oldpath, const char *newpath)
+{
+ struct strbuf entry = STRBUF_INIT;
+ struct string_list_item *path_option;
+
+ if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+ return -1;
+
+ if (gitmodules_is_unmerged)
+ die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+ path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
+ if (!path_option) {
+ warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
+ return -1;
+ }
+ strbuf_addstr(&entry, "submodule.");
+ strbuf_addstr(&entry, path_option->util);
+ strbuf_addstr(&entry, ".path");
+ if (git_config_set_in_file(".gitmodules", 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;
+ }
+ strbuf_release(&entry);
+ return 0;
+}
+
+/*
+ * Try to remove the "submodule.<name>" section from .gitmodules where the given
+ * path is configured. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<path> setting was found and we could remove it.
+ */
+int remove_path_from_gitmodules(const char *path)
+{
+ struct strbuf sect = STRBUF_INIT;
+ struct string_list_item *path_option;
+
+ if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+ return -1;
+
+ if (gitmodules_is_unmerged)
+ die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+ path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+ if (!path_option) {
+ warning(_("Could not find section in .gitmodules where path=%s"), path);
+ return -1;
+ }
+ strbuf_addstr(§, "submodule.");
+ strbuf_addstr(§, path_option->util);
+ if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
+ /* Maybe the user already did that, don't error out here */
+ warning(_("Could not remove .gitmodules entry for %s"), path);
+ strbuf_release(§);
+ return -1;
+ }
+ strbuf_release(§);
+ return 0;
+}
+
+void stage_updated_gitmodules(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct stat st;
+ int pos;
+ struct cache_entry *ce;
+ int namelen = strlen(".gitmodules");
+
+ pos = cache_name_pos(".gitmodules", namelen);
+ if (pos < 0) {
+ warning(_("could not find .gitmodules in index"));
+ return;
+ }
+ ce = active_cache[pos];
+ ce->ce_flags = namelen;
+ if (strbuf_read_file(&buf, ".gitmodules", 0) < 0)
+ die(_("reading updated .gitmodules failed"));
+ if (lstat(".gitmodules", &st) < 0)
+ die_errno(_("unable to stat updated .gitmodules"));
+ fill_stat_cache_info(ce, &st);
+ ce->ce_mode = ce_mode_from_stat(ce, st.st_mode);
+ if (remove_cache_entry_at(pos) < 0)
+ die(_("unable to remove .gitmodules from index"));
+ if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1))
+ die(_("adding updated .gitmodules failed"));
+ if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
+ die(_("staging updated .gitmodules failed"));
+}
+
static int add_submodule_odb(const char *path)
{
struct strbuf objects_directory = STRBUF_INIT;
!memcmp(ce->name, ".gitmodules", 11))
gitmodules_is_unmerged = 1;
}
+ } else if (pos < active_nr) {
+ struct stat st;
+ if (lstat(".gitmodules", &st) == 0 &&
+ ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)
+ gitmodules_is_modified = 1;
}
if (!gitmodules_is_unmerged)
return 0;
if (!strcmp(key, "path")) {
+ if (!value)
+ return config_error_nonbool(var);
+
config = unsorted_string_list_lookup(&config_name_for_path, value);
if (config)
free(config->util);
} else if (!strcmp(key, "ignore")) {
char *name_cstr;
+ if (!value)
+ return config_error_nonbool(var);
+
if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
strcmp(value, "all") && strcmp(value, "none")) {
warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
}
static void print_submodule_summary(struct rev_info *rev, FILE *f,
+ const char *line_prefix,
const char *del, const char *add, const char *reset)
{
static const char format[] = " %m %s";
while ((commit = get_revision(rev))) {
struct pretty_print_context ctx = {0};
ctx.date_mode = rev->date_mode;
+ ctx.output_encoding = get_log_output_encoding();
strbuf_setlen(&sb, 0);
+ strbuf_addstr(&sb, line_prefix);
if (commit->object.flags & SYMMETRIC_LEFT) {
if (del)
strbuf_addstr(&sb, del);
}
void show_submodule_summary(FILE *f, const char *path,
+ const char *line_prefix,
unsigned char one[20], unsigned char two[20],
unsigned dirty_submodule, const char *meta,
const char *del, const char *add, const char *reset)
message = "(revision walker failed)";
if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
- fprintf(f, "Submodule %s contains untracked content\n", path);
+ fprintf(f, "%sSubmodule %s contains untracked content\n",
+ line_prefix, path);
if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
- fprintf(f, "Submodule %s contains modified content\n", path);
+ fprintf(f, "%sSubmodule %s contains modified content\n",
+ line_prefix, path);
if (!hashcmp(one, two)) {
strbuf_release(&sb);
return;
}
- strbuf_addf(&sb, "%sSubmodule %s %s..", meta, path,
+ strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
find_unique_abbrev(one, DEFAULT_ABBREV));
if (!fast_backward && !fast_forward)
strbuf_addch(&sb, '.');
fwrite(sb.buf, sb.len, 1, f);
if (!message) /* only NULL if we succeeded in setting up the walk */
- print_submodule_summary(&rev, f, del, add, reset);
+ print_submodule_summary(&rev, f, line_prefix, del, add, reset);
if (left)
clear_commit_marks(left, ~0);
if (right)
if (!work_tree)
goto out;
- if (!the_index.initialized)
- if (read_cache() < 0)
- die("index file corrupt");
+ if (read_cache() < 0)
+ die("index file corrupt");
argv_array_push(&argv, "fetch");
for (i = 0; i < options->argc; i++)
struct strbuf submodule_path = STRBUF_INIT;
struct strbuf submodule_git_dir = STRBUF_INIT;
struct strbuf submodule_prefix = STRBUF_INIT;
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
const char *git_dir, *name, *default_argv;
if (!S_ISGITLINK(ce->ce_mode))
struct commit *a, struct commit *b)
{
int i, j;
- struct object_array merges;
+ struct object_array merges = OBJECT_ARRAY_INIT;
struct commit *commit;
int contains_another;
struct rev_info revs;
struct setup_revision_opt rev_opts;
- memset(&merges, 0, sizeof(merges));
memset(result, 0, sizeof(struct object_array));
memset(&rev_opts, 0, sizeof(rev_opts));
}
if (!contains_another)
- add_object_array(merges.objects[i].item,
- merges.objects[i].name, result);
+ add_object_array(merges.objects[i].item, NULL, result);
}
free(merges.objects);
free(merges.objects);
return 0;
}
+
+/* Update gitfile and core.worktree setting to connect work tree and git dir */
+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
+{
+ struct strbuf file_name = STRBUF_INIT;
+ struct strbuf rel_path = STRBUF_INIT;
+ const char *real_work_tree = xstrdup(real_path(work_tree));
+ FILE *fp;
+
+ /* Update gitfile */
+ strbuf_addf(&file_name, "%s/.git", work_tree);
+ fp = fopen(file_name.buf, "w");
+ if (!fp)
+ die(_("Could not create git link %s"), file_name.buf);
+ fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
+ &rel_path));
+ fclose(fp);
+
+ /* Update core.worktree setting */
+ strbuf_reset(&file_name);
+ strbuf_addf(&file_name, "%s/config", git_dir);
+ if (git_config_set_in_file(file_name.buf, "core.worktree",
+ relative_path(real_work_tree, git_dir,
+ &rel_path)))
+ die(_("Could not set core.worktree in %s"),
+ file_name.buf);
+
+ strbuf_release(&file_name);
+ strbuf_release(&rel_path);
+ free((void *)real_work_tree);
+}