sha1_file: perform object replacement in sha1_object_info_extended()
[gitweb.git] / submodule.c
index c0f93c208c11b2dad0d508fcb7adf14873484385..1905d75b2b09ee7e1503b35bf072710a659ea1ef 100644 (file)
@@ -10,6 +10,7 @@
 #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;
@@ -30,6 +31,118 @@ static struct sha1_array ref_tips_after_fetch;
  */
 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(&sect, "submodule.");
+       strbuf_addstr(&sect, 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(&sect);
+               return -1;
+       }
+       strbuf_release(&sect);
+       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;
@@ -116,6 +229,11 @@ void gitmodules_config(void)
                                    !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)
@@ -1010,3 +1128,34 @@ int merge_submodule(unsigned char result[20], const char *path,
        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);
+}