Merge branch 'jk/initialization-fix-to-add-submodule-odb'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Nov 2015 23:13:04 +0000 (15:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Nov 2015 23:13:04 +0000 (15:13 -0800)
We peek objects from submodule's object store by linking it to the
list of alternate object databases, but the code to do so forgot to
correctly initialize the list.

* jk/initialization-fix-to-add-submodule-odb:
add_submodule_odb: initialize alt_odb list earlier

1  2 
submodule.c
diff --combined submodule.c
index 5879cfb158f0b581a54a56de5de44af5c2d0a700,30e1d5bc8365b8320903c34c4e7a91c444d556f2..88af54c633851d440f46e21d65b1362b6360d2bb
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "submodule-config.h"
  #include "submodule.h"
  #include "dir.h"
  #include "diff.h"
  #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 struct string_list config_ignore_for_name;
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static struct string_list changed_submodule_paths;
  static int initialized_fetch_ref_tips;
@@@ -29,117 -30,35 +29,118 @@@ static struct sha1_array ref_tips_after
   */
  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;
 +      const struct submodule *submodule;
 +
 +      if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
 +              return -1;
 +
 +      if (gitmodules_is_unmerged)
 +              die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
 +
 +      submodule = submodule_from_path(null_sha1, oldpath);
 +      if (!submodule || !submodule->name) {
 +              warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
 +              return -1;
 +      }
 +      strbuf_addstr(&entry, "submodule.");
 +      strbuf_addstr(&entry, submodule->name);
 +      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;
 +      const struct submodule *submodule;
 +
 +      if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
 +              return -1;
 +
 +      if (gitmodules_is_unmerged)
 +              die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
 +
 +      submodule = submodule_from_path(null_sha1, path);
 +      if (!submodule || !submodule->name) {
 +              warning(_("Could not find section in .gitmodules where path=%s"), path);
 +              return -1;
 +      }
 +      strbuf_addstr(&sect, "submodule.");
 +      strbuf_addstr(&sect, submodule->name);
 +      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)
 +{
 +      if (add_file_to_cache(".gitmodules", 0))
 +              die(_("staging updated .gitmodules failed"));
 +}
 +
  static int add_submodule_odb(const char *path)
  {
        struct strbuf objects_directory = STRBUF_INIT;
        struct alternate_object_database *alt_odb;
        int ret = 0;
 -      const char *git_dir;
 +      int alloc;
  
 -      strbuf_addf(&objects_directory, "%s/.git", path);
 -      git_dir = read_gitfile(objects_directory.buf);
 -      if (git_dir) {
 -              strbuf_reset(&objects_directory);
 -              strbuf_addstr(&objects_directory, git_dir);
 -      }
 -      strbuf_addstr(&objects_directory, "/objects/");
 +      strbuf_git_path_submodule(&objects_directory, path, "objects/");
        if (!is_directory(objects_directory.buf)) {
                ret = -1;
                goto done;
        }
        /* avoid adding it twice */
+       prepare_alt_odb();
        for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next)
                if (alt_odb->name - alt_odb->base == objects_directory.len &&
                                !strncmp(alt_odb->base, objects_directory.buf,
                                        objects_directory.len))
                        goto done;
  
 -      alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
 +      alloc = objects_directory.len + 42; /* for "12/345..." sha1 */
 +      alt_odb = xmalloc(sizeof(*alt_odb) + alloc);
        alt_odb->next = alt_odb_list;
 -      strcpy(alt_odb->base, objects_directory.buf);
 +      xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
        alt_odb->name = alt_odb->base + objects_directory.len;
        alt_odb->name[2] = '/';
        alt_odb->name[40] = '\0';
  
        /* add possible alternates from the submodule */
        read_info_alternates(objects_directory.buf, 0);
-       prepare_alt_odb();
  done:
        strbuf_release(&objects_directory);
        return ret;
  void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                                             const char *path)
  {
 -      struct string_list_item *path_option, *ignore_option;
 -      path_option = unsorted_string_list_lookup(&config_name_for_path, path);
 -      if (path_option) {
 -              ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
 -              if (ignore_option)
 -                      handle_ignore_submodules_arg(diffopt, ignore_option->util);
 +      const struct submodule *submodule = submodule_from_path(null_sha1, path);
 +      if (submodule) {
 +              if (submodule->ignore)
 +                      handle_ignore_submodules_arg(diffopt, submodule->ignore);
                else if (gitmodules_is_unmerged)
                        DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        }
  
  int submodule_config(const char *var, const char *value, void *cb)
  {
 -      if (!prefixcmp(var, "submodule."))
 +      if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
                config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
@@@ -196,11 -116,6 +196,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)
        }
  }
  
 -int parse_submodule_config_option(const char *var, const char *value)
 -{
 -      int len;
 -      struct string_list_item *config;
 -      struct strbuf submodname = STRBUF_INIT;
 -
 -      var += 10;              /* Skip "submodule." */
 -
 -      len = strlen(var);
 -      if ((len > 5) && !strcmp(var + len - 5, ".path")) {
 -              strbuf_add(&submodname, var, len - 5);
 -              config = unsorted_string_list_lookup(&config_name_for_path, value);
 -              if (config)
 -                      free(config->util);
 -              else
 -                      config = string_list_append(&config_name_for_path, xstrdup(value));
 -              config->util = strbuf_detach(&submodname, NULL);
 -              strbuf_release(&submodname);
 -      } else if ((len > 23) && !strcmp(var + len - 23, ".fetchrecursesubmodules")) {
 -              strbuf_add(&submodname, var, len - 23);
 -              config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, submodname.buf);
 -              if (!config)
 -                      config = string_list_append(&config_fetch_recurse_submodules_for_name,
 -                                                  strbuf_detach(&submodname, NULL));
 -              config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
 -              strbuf_release(&submodname);
 -      } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
 -              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);
 -                      return 0;
 -              }
 -
 -              strbuf_add(&submodname, var, len - 7);
 -              config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
 -              if (config)
 -                      free(config->util);
 -              else
 -                      config = string_list_append(&config_ignore_for_name,
 -                                                  strbuf_detach(&submodname, NULL));
 -              strbuf_release(&submodname);
 -              config->util = xstrdup(value);
 -              return 0;
 -      }
 -      return 0;
 -}
 -
  void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
  {
@@@ -239,7 -201,7 +239,7 @@@ static int prepare_submodule_summary(st
        left->object.flags |= SYMMETRIC_LEFT;
        add_pending_object(rev, &left->object, path);
        add_pending_object(rev, &right->object, path);
 -      merge_bases = get_merge_bases(left, right, 1);
 +      merge_bases = get_merge_bases(left, right);
        if (merge_bases) {
                if (merge_bases->item == left)
                        *fast_forward = 1;
  }
  
  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);
        strbuf_release(&sb);
  }
  
 -int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
 -{
 -      switch (git_config_maybe_bool(opt, arg)) {
 -      case 1:
 -              return RECURSE_SUBMODULES_ON;
 -      case 0:
 -              return RECURSE_SUBMODULES_OFF;
 -      default:
 -              if (!strcmp(arg, "on-demand"))
 -                      return RECURSE_SUBMODULES_ON_DEMAND;
 -              die("bad %s argument: %s", opt, arg);
 -      }
 -}
 -
  void show_submodule_summary(FILE *f, const char *path,
 +              const char *line_prefix,
                unsigned char one[20], unsigned char two[20],
 -              unsigned dirty_submodule,
 +              unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset)
  {
        struct rev_info rev;
 -      struct commit *left = left, *right = right;
 +      struct commit *left = NULL, *right = NULL;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
        int fast_forward = 0, fast_backward = 0;
        else if (!(left = lookup_commit_reference(one)) ||
                 !(right = lookup_commit_reference(two)))
                message = "(commits not present)";
 -
 -      if (!message &&
 -          prepare_submodule_summary(&rev, path, left, right,
 -                                      &fast_forward, &fast_backward))
 +      else if (prepare_submodule_summary(&rev, path, left, right,
 +                                         &fast_forward, &fast_backward))
                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, "Submodule %s %s..", 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, '.');
        strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
        if (message)
 -              strbuf_addf(&sb, " %s\n", message);
 +              strbuf_addf(&sb, " %s%s\n", message, reset);
        else
 -              strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
 +              strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
        fwrite(sb.buf, sb.len, 1, f);
  
 -      if (!message) {
 -              print_submodule_summary(&rev, f, del, add, reset);
 +      if (!message) /* only NULL if we succeeded in setting up the walk */
 +              print_submodule_summary(&rev, f, line_prefix, del, add, reset);
 +      if (left)
                clear_commit_marks(left, ~0);
 +      if (right)
                clear_commit_marks(right, ~0);
 -      }
  
        strbuf_release(&sb);
  }
@@@ -346,8 -317,7 +346,8 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
 -static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 +static int has_remote(const char *refname, const struct object_id *oid,
 +                    int flags, void *cb_data)
  {
        return 1;
  }
@@@ -358,12 -328,13 +358,12 @@@ static int submodule_needs_pushing(cons
                return 0;
  
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 -              struct child_process cp;
 +              struct child_process cp = CHILD_PROCESS_INIT;
                const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
                struct strbuf buf = STRBUF_INIT;
                int needs_pushing = 0;
  
                argv[1] = sha1_to_hex(sha1);
 -              memset(&cp, 0, sizeof(cp));
                cp.argv = argv;
                cp.env = local_repo_env;
                cp.git_cmd = 1;
@@@ -389,19 -360,21 +389,19 @@@ static void collect_submodules_from_dif
                                         void *data)
  {
        int i;
 -      int *needs_pushing = data;
 +      struct string_list *needs_pushing = data;
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                if (!S_ISGITLINK(p->two->mode))
                        continue;
 -              if (submodule_needs_pushing(p->two->path, p->two->sha1)) {
 -                      *needs_pushing = 1;
 -                      break;
 -              }
 +              if (submodule_needs_pushing(p->two->path, p->two->sha1))
 +                      string_list_insert(needs_pushing, p->two->path);
        }
  }
  
 -
 -static void commit_need_pushing(struct commit *commit, int *needs_pushing)
 +static void find_unpushed_submodule_commits(struct commit *commit,
 +              struct string_list *needs_pushing)
  {
        struct rev_info rev;
  
        diff_tree_combined_merge(commit, 1, &rev);
  }
  
 -int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
 +int find_unpushed_submodules(unsigned char new_sha1[20],
 +              const char *remotes_name, struct string_list *needs_pushing)
  {
        struct rev_info rev;
        struct commit *commit;
        const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
        int argc = ARRAY_SIZE(argv) - 1;
        char *sha1_copy;
 -      int needs_pushing = 0;
 +
        struct strbuf remotes_arg = STRBUF_INIT;
  
        strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
 -      while ((commit = get_revision(&rev)) && !needs_pushing)
 -              commit_need_pushing(commit, &needs_pushing);
 +      while ((commit = get_revision(&rev)) != NULL)
 +              find_unpushed_submodule_commits(commit, needs_pushing);
  
 +      reset_revision_walk();
        free(sha1_copy);
        strbuf_release(&remotes_arg);
  
 -      return needs_pushing;
 +      return needs_pushing->nr;
 +}
 +
 +static int push_submodule(const char *path)
 +{
 +      if (add_submodule_odb(path))
 +              return 1;
 +
 +      if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 +              struct child_process cp = CHILD_PROCESS_INIT;
 +              const char *argv[] = {"push", NULL};
 +
 +              cp.argv = argv;
 +              cp.env = local_repo_env;
 +              cp.git_cmd = 1;
 +              cp.no_stdin = 1;
 +              cp.dir = path;
 +              if (run_command(&cp))
 +                      return 0;
 +              close(cp.out);
 +      }
 +
 +      return 1;
 +}
 +
 +int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 +{
 +      int i, ret = 1;
 +      struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 +
 +      if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
 +              return 1;
 +
 +      for (i = 0; i < needs_pushing.nr; i++) {
 +              const char *path = needs_pushing.items[i].string;
 +              fprintf(stderr, "Pushing submodule '%s'\n", path);
 +              if (!push_submodule(path)) {
 +                      fprintf(stderr, "Unable to push submodule '%s'\n", path);
 +                      ret = 0;
 +              }
 +      }
 +
 +      string_list_clear(&needs_pushing, 0);
 +
 +      return ret;
  }
  
  static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
        if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
                /* Even if the submodule is checked out and the commit is
                 * present, make sure it is reachable from a ref. */
 -              struct child_process cp;
 +              struct child_process cp = CHILD_PROCESS_INIT;
                const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
                struct strbuf buf = STRBUF_INIT;
  
                argv[3] = sha1_to_hex(sha1);
 -              memset(&cp, 0, sizeof(cp));
                cp.argv = argv;
                cp.env = local_repo_env;
                cp.git_cmd = 1;
                cp.no_stdin = 1;
 -              cp.out = -1;
                cp.dir = path;
 -              if (!run_command(&cp) && !strbuf_read(&buf, cp.out, 1024))
 +              if (!capture_command(&cp, &buf, 1024) && !buf.len)
                        is_present = 1;
  
 -              close(cp.out);
                strbuf_release(&buf);
        }
        return is_present;
@@@ -541,10 -471,10 +541,10 @@@ static void submodule_collect_changed_c
        }
  }
  
 -static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
 +static int add_sha1_to_array(const char *ref, const struct object_id *oid,
                             int flags, void *data)
  {
 -      sha1_array_append(data, sha1);
 +      sha1_array_append(data, oid->hash);
        return 0;
  }
  
@@@ -570,7 -500,7 +570,7 @@@ static void calculate_changed_submodule
        struct argv_array argv = ARGV_ARRAY_INIT;
  
        /* No need to check if there are no submodules configured */
 -      if (!config_name_for_path.nr)
 +      if (!submodule_from_path(NULL, NULL))
                return;
  
        init_revisions(&rev, NULL);
                        DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
 -                      if (diff_setup_done(&diff_opts) < 0)
 -                              die("diff_setup_done failed");
 +                      diff_setup_done(&diff_opts);
                        diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
                        diffcore_std(&diff_opts);
                        diff_flush(&diff_opts);
        initialized_fetch_ref_tips = 0;
  }
  
 -int fetch_populated_submodules(int num_options, const char **options,
 +int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet)
  {
 -      int i, result = 0, argc = 0, default_argc;
 -      struct child_process cp;
 -      const char **argv;
 -      struct string_list_item *name_for_path;
 +      int i, result = 0;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      struct argv_array argv = ARGV_ARRAY_INIT;
        const char *work_tree = get_git_work_tree();
        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");
  
 -      /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */
 -      argv = xcalloc(num_options + 6, sizeof(const char *));
 -      argv[argc++] = "fetch";
 -      for (i = 0; i < num_options; i++)
 -              argv[argc++] = options[i];
 -      argv[argc++] = "--recurse-submodules-default";
 -      default_argc = argc++;
 -      argv[argc++] = "--submodule-prefix";
 +      argv_array_push(&argv, "fetch");
 +      for (i = 0; i < options->argc; i++)
 +              argv_array_push(&argv, options->argv[i]);
 +      argv_array_push(&argv, "--recurse-submodules-default");
 +      /* default value, "--submodule-prefix" and its value are added later */
  
 -      memset(&cp, 0, sizeof(cp));
 -      cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
                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 char *git_dir, *name, *default_argv;
 +              const struct cache_entry *ce = active_cache[i];
 +              const char *git_dir, *default_argv;
 +              const struct submodule *submodule;
  
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
  
 -              name = ce->name;
 -              name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name);
 -              if (name_for_path)
 -                      name = name_for_path->util;
 +              submodule = submodule_from_path(null_sha1, ce->name);
 +              if (!submodule)
 +                      submodule = submodule_from_name(null_sha1, ce->name);
  
                default_argv = "yes";
                if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
 -                      struct string_list_item *fetch_recurse_submodules_option;
 -                      fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
 -                      if (fetch_recurse_submodules_option) {
 -                              if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF)
 +                      if (submodule &&
 +                          submodule->fetch_recurse !=
 +                                              RECURSE_SUBMODULES_NONE) {
 +                              if (submodule->fetch_recurse ==
 +                                              RECURSE_SUBMODULES_OFF)
                                        continue;
 -                              if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) {
 +                              if (submodule->fetch_recurse ==
 +                                              RECURSE_SUBMODULES_ON_DEMAND) {
                                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                                continue;
                                        default_argv = "on-demand";
                        if (!quiet)
                                printf("Fetching submodule %s%s\n", prefix, ce->name);
                        cp.dir = submodule_path.buf;
 -                      argv[default_argc] = default_argv;
 -                      argv[argc] = submodule_prefix.buf;
 +                      argv_array_push(&argv, default_argv);
 +                      argv_array_push(&argv, "--submodule-prefix");
 +                      argv_array_push(&argv, submodule_prefix.buf);
 +                      cp.argv = argv.argv;
                        if (run_command(&cp))
                                result = 1;
 +                      argv_array_pop(&argv);
 +                      argv_array_pop(&argv);
 +                      argv_array_pop(&argv);
                }
                strbuf_release(&submodule_path);
                strbuf_release(&submodule_git_dir);
                strbuf_release(&submodule_prefix);
        }
 -      free(argv);
 +      argv_array_clear(&argv);
  out:
        string_list_clear(&changed_submodule_paths, 1);
        return result;
  unsigned is_submodule_modified(const char *path, int ignore_untracked)
  {
        ssize_t len;
 -      struct child_process cp;
 +      struct child_process cp = CHILD_PROCESS_INIT;
        const char *argv[] = {
                "status",
                "--porcelain",
        if (ignore_untracked)
                argv[2] = "-uno";
  
 -      memset(&cp, 0, sizeof(cp));
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
        return dirty_submodule;
  }
  
 +int submodule_uses_gitfile(const char *path)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      const char *argv[] = {
 +              "submodule",
 +              "foreach",
 +              "--quiet",
 +              "--recursive",
 +              "test -f .git",
 +              NULL,
 +      };
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *git_dir;
 +
 +      strbuf_addf(&buf, "%s/.git", path);
 +      git_dir = read_gitfile(buf.buf);
 +      if (!git_dir) {
 +              strbuf_release(&buf);
 +              return 0;
 +      }
 +      strbuf_release(&buf);
 +
 +      /* Now test that all nested submodules use a gitfile too */
 +      cp.argv = argv;
 +      cp.env = local_repo_env;
 +      cp.git_cmd = 1;
 +      cp.no_stdin = 1;
 +      cp.no_stderr = 1;
 +      cp.no_stdout = 1;
 +      cp.dir = path;
 +      if (run_command(&cp))
 +              return 0;
 +
 +      return 1;
 +}
 +
 +int ok_to_remove_submodule(const char *path)
 +{
 +      ssize_t len;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      const char *argv[] = {
 +              "status",
 +              "--porcelain",
 +              "-u",
 +              "--ignore-submodules=none",
 +              NULL,
 +      };
 +      struct strbuf buf = STRBUF_INIT;
 +      int ok_to_remove = 1;
 +
 +      if (!file_exists(path) || is_empty_dir(path))
 +              return 1;
 +
 +      if (!submodule_uses_gitfile(path))
 +              return 0;
 +
 +      cp.argv = argv;
 +      cp.env = local_repo_env;
 +      cp.git_cmd = 1;
 +      cp.no_stdin = 1;
 +      cp.out = -1;
 +      cp.dir = path;
 +      if (start_command(&cp))
 +              die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path);
 +
 +      len = strbuf_read(&buf, cp.out, 1024);
 +      if (len > 2)
 +              ok_to_remove = 0;
 +      close(cp.out);
 +
 +      if (finish_command(&cp))
 +              die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path);
 +
 +      strbuf_release(&buf);
 +      return ok_to_remove;
 +}
 +
  static int find_first_merges(struct object_array *result, const char *path,
                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));
  
                        sha1_to_hex(a->object.sha1));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
 -      setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
 +      setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
  
        /* save all revisions from the above list that contain b */
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        while ((commit = get_revision(&revs)) != NULL) {
                struct object *o = &(commit->object);
 -              if (in_merge_bases(b, &commit, 1))
 +              if (in_merge_bases(b, commit))
                        add_object_array(o, NULL, &merges);
        }
 +      reset_revision_walk();
  
        /* Now we've got all merges that contain a and b. Prune all
         * merges that contain another found merge and save them in
                contains_another = 0;
                for (j = 0; j < merges.nr; j++) {
                        struct commit *m2 = (struct commit *) merges.objects[j].item;
 -                      if (i != j && in_merge_bases(m2, &m1, 1)) {
 +                      if (i != j && in_merge_bases(m2, m1)) {
                                contains_another = 1;
                                break;
                        }
                }
  
                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);
@@@ -918,7 -774,7 +918,7 @@@ static void print_commit(struct commit 
  {
        struct strbuf sb = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
 -      ctx.date_mode = DATE_NORMAL;
 +      ctx.date_mode.type = DATE_NORMAL;
        format_commit_message(commit, " %h: %m %s", &sb, &ctx);
        fprintf(stderr, "%s\n", sb.buf);
        strbuf_release(&sb);
@@@ -961,18 -817,18 +961,18 @@@ int merge_submodule(unsigned char resul
        }
  
        /* check whether both changes are forward */
 -      if (!in_merge_bases(commit_base, &commit_a, 1) ||
 -          !in_merge_bases(commit_base, &commit_b, 1)) {
 +      if (!in_merge_bases(commit_base, commit_a) ||
 +          !in_merge_bases(commit_base, commit_b)) {
                MERGE_WARNING(path, "commits don't follow merge-base");
                return 0;
        }
  
        /* Case #1: a is contained in b or vice versa */
 -      if (in_merge_bases(commit_a, &commit_b, 1)) {
 +      if (in_merge_bases(commit_a, commit_b)) {
                hashcpy(result, b);
                return 1;
        }
 -      if (in_merge_bases(commit_b, &commit_a, 1)) {
 +      if (in_merge_bases(commit_b, commit_a)) {
                hashcpy(result, a);
                return 1;
        }
        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));
 +
 +      /* Update gitfile */
 +      strbuf_addf(&file_name, "%s/.git", work_tree);
 +      write_file(file_name.buf, "gitdir: %s",
 +                 relative_path(git_dir, real_work_tree, &rel_path));
 +
 +      /* 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);
 +}