Merge branch 'sb/submodule-core-worktree'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Jul 2018 19:20:28 +0000 (12:20 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Jul 2018 19:20:28 +0000 (12:20 -0700)
"git submodule" did not correctly adjust core.worktree setting that
indicates whether/where a submodule repository has its associated
working tree across various state transitions, which has been
corrected.

* sb/submodule-core-worktree:
submodule deinit: unset core.worktree
submodule: ensure core.worktree is set after update
submodule: unset core.worktree if no working tree is present

1  2 
builtin/submodule--helper.c
git-submodule.sh
submodule.c
t/t7400-submodule-basic.sh
index 20ae9191ca376841ca2540502d85a3dc150d3417,19480902681833b115c2cdf535068533efdbf2d1..216e3daf5c9ea95d174efc89be2f42942d85472e
@@@ -55,7 -55,7 +55,7 @@@ static char *get_default_remote(void
  
  static int print_default_remote(int argc, const char **argv, const char *prefix)
  {
 -      const char *remote;
 +      char *remote;
  
        if (argc != 1)
                die(_("submodule--helper print-default-remote takes no arguments"));
@@@ -64,7 -64,6 +64,7 @@@
        if (remote)
                printf("%s\n", remote);
  
 +      free(remote);
        return 0;
  }
  
@@@ -441,149 -440,6 +441,149 @@@ static void for_each_listed_submodule(c
                fn(list->entries[i], cb_data);
  }
  
 +struct cb_foreach {
 +      int argc;
 +      const char **argv;
 +      const char *prefix;
 +      int quiet;
 +      int recursive;
 +};
 +#define CB_FOREACH_INIT { 0 }
 +
 +static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
 +                                     void *cb_data)
 +{
 +      struct cb_foreach *info = cb_data;
 +      const char *path = list_item->name;
 +      const struct object_id *ce_oid = &list_item->oid;
 +
 +      const struct submodule *sub;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      char *displaypath;
 +
 +      displaypath = get_submodule_displaypath(path, info->prefix);
 +
 +      sub = submodule_from_path(the_repository, &null_oid, path);
 +
 +      if (!sub)
 +              die(_("No url found for submodule path '%s' in .gitmodules"),
 +                      displaypath);
 +
 +      if (!is_submodule_populated_gently(path, NULL))
 +              goto cleanup;
 +
 +      prepare_submodule_repo_env(&cp.env_array);
 +
 +      /*
 +       * For the purpose of executing <command> in the submodule,
 +       * separate shell is used for the purpose of running the
 +       * child process.
 +       */
 +      cp.use_shell = 1;
 +      cp.dir = path;
 +
 +      /*
 +       * NEEDSWORK: the command currently has access to the variables $name,
 +       * $sm_path, $displaypath, $sha1 and $toplevel only when the command
 +       * contains a single argument. This is done for maintaining a faithful
 +       * translation from shell script.
 +       */
 +      if (info->argc == 1) {
 +              char *toplevel = xgetcwd();
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              argv_array_pushf(&cp.env_array, "name=%s", sub->name);
 +              argv_array_pushf(&cp.env_array, "sm_path=%s", path);
 +              argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
 +              argv_array_pushf(&cp.env_array, "sha1=%s",
 +                              oid_to_hex(ce_oid));
 +              argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
 +
 +              /*
 +               * Since the path variable was accessible from the script
 +               * before porting, it is also made available after porting.
 +               * The environment variable "PATH" has a very special purpose
 +               * on windows. And since environment variables are
 +               * case-insensitive in windows, it interferes with the
 +               * existing PATH variable. Hence, to avoid that, we expose
 +               * path via the args argv_array and not via env_array.
 +               */
 +              sq_quote_buf(&sb, path);
 +              argv_array_pushf(&cp.args, "path=%s; %s",
 +                               sb.buf, info->argv[0]);
 +              strbuf_release(&sb);
 +              free(toplevel);
 +      } else {
 +              argv_array_pushv(&cp.args, info->argv);
 +      }
 +
 +      if (!info->quiet)
 +              printf(_("Entering '%s'\n"), displaypath);
 +
 +      if (info->argv[0] && run_command(&cp))
 +              die(_("run_command returned non-zero status for %s\n."),
 +                      displaypath);
 +
 +      if (info->recursive) {
 +              struct child_process cpr = CHILD_PROCESS_INIT;
 +
 +              cpr.git_cmd = 1;
 +              cpr.dir = path;
 +              prepare_submodule_repo_env(&cpr.env_array);
 +
 +              argv_array_pushl(&cpr.args, "--super-prefix", NULL);
 +              argv_array_pushf(&cpr.args, "%s/", displaypath);
 +              argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
 +                              NULL);
 +
 +              if (info->quiet)
 +                      argv_array_push(&cpr.args, "--quiet");
 +
 +              argv_array_pushv(&cpr.args, info->argv);
 +
 +              if (run_command(&cpr))
 +                      die(_("run_command returned non-zero status while"
 +                              "recursing in the nested submodules of %s\n."),
 +                              displaypath);
 +      }
 +
 +cleanup:
 +      free(displaypath);
 +}
 +
 +static int module_foreach(int argc, const char **argv, const char *prefix)
 +{
 +      struct cb_foreach info = CB_FOREACH_INIT;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +
 +      struct option module_foreach_options[] = {
 +              OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
 +              OPT_BOOL(0, "recursive", &info.recursive,
 +                       N_("Recurse into nested submodules")),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_foreach_options,
 +                           git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
 +
 +      if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
 +              return 1;
 +
 +      info.argc = argc;
 +      info.argv = argv;
 +      info.prefix = prefix;
 +
 +      for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
 +
 +      return 0;
 +}
 +
  struct init_cb {
        const char *prefix;
        unsigned int flags;
@@@ -1124,6 -980,8 +1124,8 @@@ static void deinit_submodule(const cha
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
  
+               submodule_unset_core_worktree(sub);
                strbuf_release(&sb_rm);
        }
  
@@@ -2004,6 -1862,29 +2006,29 @@@ static int check_name(int argc, const c
        return 0;
  }
  
+ static int connect_gitdir_workingtree(int argc, const char **argv, const char *prefix)
+ {
+       struct strbuf sb = STRBUF_INIT;
+       const char *name, *path;
+       char *sm_gitdir;
+       if (argc != 3)
+               BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+       name = argv[1];
+       path = argv[2];
+       strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+       sm_gitdir = absolute_pathdup(sb.buf);
+       connect_work_tree_and_git_dir(path, sm_gitdir, 0);
+       strbuf_release(&sb);
+       free(sm_gitdir);
+       return 0;
+ }
  #define SUPPORT_SUPER_PREFIX (1<<0)
  
  struct cmd_struct {
@@@ -2017,10 -1898,10 +2042,11 @@@ static struct cmd_struct commands[] = 
        {"name", module_name, 0},
        {"clone", module_clone, 0},
        {"update-clone", update_clone, 0},
+       {"connect-gitdir-workingtree", connect_gitdir_workingtree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
 +      {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
        {"print-default-remote", print_default_remote, 0},
diff --combined git-submodule.sh
index 5f9d9f6ea3713be458c006501c503d8d660e9ddf,6bd0db02b331291cf3acd60cf66bc1abc7844368..8b5ad59bdee39eba4fc56b28d6771a291dac55f0
@@@ -335,7 -335,45 +335,7 @@@ cmd_foreach(
                shift
        done
  
 -      toplevel=$(pwd)
 -
 -      # dup stdin so that it can be restored when running the external
 -      # command in the subshell (and a recursive call to this function)
 -      exec 3<&0
 -
 -      {
 -              git submodule--helper list --prefix "$wt_prefix" ||
 -              echo "#unmatched" $?
 -      } |
 -      while read -r mode sha1 stage sm_path
 -      do
 -              die_if_unmatched "$mode" "$sha1"
 -              if test -e "$sm_path"/.git
 -              then
 -                      displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
 -                      say "$(eval_gettext "Entering '\$displaypath'")"
 -                      name=$(git submodule--helper name "$sm_path")
 -                      (
 -                              prefix="$prefix$sm_path/"
 -                              sanitize_submodule_env
 -                              cd "$sm_path" &&
 -                              sm_path=$(git submodule--helper relative-path "$sm_path" "$wt_prefix") &&
 -                              # we make $path available to scripts ...
 -                              path=$sm_path &&
 -                              if test $# -eq 1
 -                              then
 -                                      eval "$1"
 -                              else
 -                                      "$@"
 -                              fi &&
 -                              if test -n "$recursive"
 -                              then
 -                                      cmd_foreach "--recursive" "$@"
 -                              fi
 -                      ) <&3 3<&- ||
 -                      die "$(eval_gettext "Stopping at '\$displaypath'; script returned non-zero status.")"
 -              fi
 -      done
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
  }
  
  #
@@@ -577,6 -615,11 +577,11 @@@ cmd_update(
                        die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
                fi
  
+               if ! $(git config -f "$(git rev-parse --git-common-dir)/modules/$name/config" core.worktree) 2>/dev/null
+               then
+                       git submodule--helper connect-gitdir-workingtree "$name" "$sm_path"
+               fi
                if test "$subsha1" != "$sha1" || test -n "$force"
                then
                        subforce=$force
diff --combined submodule.c
index 0998ea2345800449c08bca12fbedd236f6b55eb6,e127c074b0430eae47bddd2778e521587ce08122..d3a9aab83dc90b0c7797cc4b83d0a83bf6c196b7
@@@ -740,14 -740,12 +740,14 @@@ static void collect_changed_submodules_
                else {
                        name = default_name_or_path(p->two->path);
                        /* make sure name does not collide with existing one */
 -                      submodule = submodule_from_name(the_repository, commit_oid, name);
 +                      if (name)
 +                              submodule = submodule_from_name(the_repository,
 +                                                              commit_oid, name);
                        if (submodule) {
                                warning("Submodule in commit %s at path: "
                                        "'%s' collides with a submodule named "
                                        "the same. Skipping it.",
 -                                      oid_to_hex(commit_oid), name);
 +                                      oid_to_hex(commit_oid), p->two->path);
                                name = NULL;
                        }
                }
        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();
@@@ -1699,6 -1709,8 +1711,8 @@@ int submodule_move_head(const char *pat
  
                        if (is_empty_dir(path))
                                rmdir_or_warn(path);
+                       submodule_unset_core_worktree(sub);
                }
        }
  out:
index 812db137b8db2f6f8457ccb5d6ab27e0215dbe1a,1a33040d94d628d1c6a2f3edcac6ab4f00b7bbf7..48fd14fae6e9ac04f2edac3592bfb3ee4504cab1
@@@ -126,10 -126,8 +126,10 @@@ test_expect_success 'submodule add' 
        test_cmp empty untracked
  '
  
 -test_create_repo parent &&
 -test_commit -C parent one
 +test_expect_success 'setup parent and one repository' '
 +      test_create_repo parent &&
 +      test_commit -C parent one
 +'
  
  test_expect_success 'redirected submodule add does not show progress' '
        git -C addtest submodule add "file://$submodurl/parent" submod-redirected \
@@@ -993,6 -991,11 +993,11 @@@ test_expect_success 'submodule deinit s
        rmdir init
  '
  
+ test_expect_success 'submodule deinit should unset core.worktree' '
+       test_path_is_file .git/modules/example/config &&
+       test_must_fail git config -f .git/modules/example/config core.worktree
+ '
  test_expect_success 'submodule deinit from subdirectory' '
        git submodule update --init &&
        git config submodule.example.foo bar &&