Merge branch 'sb/submodule-url-to-absolute'
authorJunio C Hamano <gitster@pobox.com>
Tue, 6 Nov 2018 06:50:19 +0000 (15:50 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 6 Nov 2018 06:50:19 +0000 (15:50 +0900)
Some codepaths failed to form a proper URL when .gitmodules record
the URL to a submodule repository as relative to the repository of
superproject, which has been corrected.

* sb/submodule-url-to-absolute:
submodule helper: convert relative URL to absolute URL if needed

1  2 
builtin/submodule--helper.c
index 8e1db55e136c368f06c4029b4cd2c888400bb81a,31b641ced64f7979edb471027a9e0afb5c2fdedd..676175b9befa23c70e56e12c00746b8faf899b8c
@@@ -584,6 -584,26 +584,26 @@@ static int module_foreach(int argc, con
        return 0;
  }
  
+ static char *compute_submodule_clone_url(const char *rel_url)
+ {
+       char *remoteurl, *relurl;
+       char *remote = get_default_remote();
+       struct strbuf remotesb = STRBUF_INIT;
+       strbuf_addf(&remotesb, "remote.%s.url", remote);
+       if (git_config_get_string(remotesb.buf, &remoteurl)) {
+               warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
+               remoteurl = xgetcwd();
+       }
+       relurl = relative_url(remoteurl, rel_url, NULL);
+       free(remote);
+       free(remoteurl);
+       strbuf_release(&remotesb);
+       return relurl;
+ }
  struct init_cb {
        const char *prefix;
        unsigned int flags;
@@@ -634,21 -654,9 +654,9 @@@ static void init_submodule(const char *
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
-                       char *remoteurl, *relurl;
-                       char *remote = get_default_remote();
-                       struct strbuf remotesb = STRBUF_INIT;
-                       strbuf_addf(&remotesb, "remote.%s.url", remote);
-                       free(remote);
-                       if (git_config_get_string(remotesb.buf, &remoteurl)) {
-                               warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
-                               remoteurl = xgetcwd();
-                       }
-                       relurl = relative_url(remoteurl, url, NULL);
-                       strbuf_release(&remotesb);
-                       free(remoteurl);
-                       free(url);
-                       url = relurl;
+                       char *oldurl = url;
+                       url = compute_submodule_clone_url(oldurl);
+                       free(oldurl);
                }
  
                if (git_config_set_gently(sb.buf, url))
@@@ -792,7 -800,7 +800,7 @@@ static void status_submodule(const cha
                         path, NULL);
  
        git_config(git_diff_basic_config, NULL);
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        diff_files_args.argc = setup_revisions(diff_files_args.argc,
                                               diff_files_args.argv,
@@@ -1444,70 -1452,6 +1452,70 @@@ static int module_clone(int argc, cons
        return 0;
  }
  
 +static void determine_submodule_update_strategy(struct repository *r,
 +                                              int just_cloned,
 +                                              const char *path,
 +                                              const char *update,
 +                                              struct submodule_update_strategy *out)
 +{
 +      const struct submodule *sub = submodule_from_path(r, &null_oid, path);
 +      char *key;
 +      const char *val;
 +
 +      key = xstrfmt("submodule.%s.update", sub->name);
 +
 +      if (update) {
 +              if (parse_submodule_update_strategy(update, out) < 0)
 +                      die(_("Invalid update mode '%s' for submodule path '%s'"),
 +                              update, path);
 +      } else if (!repo_config_get_string_const(r, key, &val)) {
 +              if (parse_submodule_update_strategy(val, out) < 0)
 +                      die(_("Invalid update mode '%s' configured for submodule path '%s'"),
 +                              val, path);
 +      } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
 +              out->type = sub->update_strategy.type;
 +              out->command = sub->update_strategy.command;
 +      } else
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      if (just_cloned &&
 +          (out->type == SM_UPDATE_MERGE ||
 +           out->type == SM_UPDATE_REBASE ||
 +           out->type == SM_UPDATE_NONE))
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      free(key);
 +}
 +
 +static int module_update_module_mode(int argc, const char **argv, const char *prefix)
 +{
 +      const char *path, *update = NULL;
 +      int just_cloned;
 +      struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
 +
 +      if (argc < 3 || argc > 4)
 +              die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
 +
 +      just_cloned = git_config_int("just_cloned", argv[1]);
 +      path = argv[2];
 +
 +      if (argc == 4)
 +              update = argv[3];
 +
 +      determine_submodule_update_strategy(the_repository,
 +                                          just_cloned, path, update,
 +                                          &update_strategy);
 +      fputs(submodule_strategy_to_string(&update_strategy), stdout);
 +
 +      return 0;
 +}
 +
 +struct update_clone_data {
 +      const struct submodule *sub;
 +      struct object_id oid;
 +      unsigned just_cloned;
 +};
 +
  struct submodule_update_clone {
        /* index into 'list', the list of submodules to look into for cloning */
        int current;
        const char *recursive_prefix;
        const char *prefix;
  
 -      /* Machine-readable status lines to be consumed by git-submodule.sh */
 -      struct string_list projectlines;
 +      /* to be consumed by git-submodule.sh */
 +      struct update_clone_data *update_clone;
 +      int update_clone_nr; int update_clone_alloc;
  
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
        /* failed clones to be retried again */
        const struct cache_entry **failed_clones;
        int failed_clones_nr, failed_clones_alloc;
 +
 +      int max_jobs;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
        NULL, NULL, NULL, \
 -      STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 +      NULL, 0, 0, 0, NULL, 0, 0, 0}
  
  
  static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@@ -1582,6 -1523,7 +1590,7 @@@ static int prepare_to_clone_next_submod
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
        int needs_cloning = 0;
+       int need_free_url = 0;
  
        if (ce_stage(ce)) {
                if (suc->recursive_prefix)
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
-       if (repo_config_get_string_const(the_repository, sb.buf, &url))
-               url = sub->url;
+       if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
+               if (starts_with_dot_slash(sub->url) ||
+                   starts_with_dot_dot_slash(sub->url)) {
+                       url = compute_submodule_clone_url(sub->url);
+                       need_free_url = 1;
+               } else
+                       url = sub->url;
+       }
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
  
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
 -                      oid_to_hex(&ce->oid), ce_stage(ce),
 -                      needs_cloning, ce->name);
 -      string_list_append(&suc->projectlines, sb.buf);
 +      ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
 +                 suc->update_clone_alloc);
 +      oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
 +      suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
 +      suc->update_clone[suc->update_clone_nr].sub = sub;
 +      suc->update_clone_nr++;
  
        if (!needs_cloning)
                goto cleanup;
  cleanup:
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
+       if (need_free_url)
+               free((void*)url);
  
        return needs_cloning;
  }
@@@ -1783,44 -1732,11 +1800,44 @@@ static int git_update_clone_config(cons
        return 0;
  }
  
 +static void update_submodule(struct update_clone_data *ucd)
 +{
 +      fprintf(stdout, "dummy %s %d\t%s\n",
 +              oid_to_hex(&ucd->oid),
 +              ucd->just_cloned,
 +              ucd->sub->path);
 +}
 +
 +static int update_submodules(struct submodule_update_clone *suc)
 +{
 +      int i;
 +
 +      run_processes_parallel(suc->max_jobs,
 +                             update_clone_get_next_task,
 +                             update_clone_start_failure,
 +                             update_clone_task_finished,
 +                             suc);
 +
 +      /*
 +       * We saved the output and put it out all at once now.
 +       * That means:
 +       * - the listener does not have to interleave their (checkout)
 +       *   work with our fetching.  The writes involved in a
 +       *   checkout involve more straightforward sequential I/O.
 +       * - the listener can avoid doing any work if fetching failed.
 +       */
 +      if (suc->quickstop)
 +              return 1;
 +
 +      for (i = 0; i < suc->update_clone_nr; i++)
 +              update_submodule(&suc->update_clone[i]);
 +
 +      return 0;
 +}
 +
  static int update_clone(int argc, const char **argv, const char *prefix)
  {
        const char *update = NULL;
 -      int max_jobs = 1;
 -      struct string_list_item *item;
        struct pathspec pathspec;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
  
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                              "specified number of revisions")),
 -              OPT_INTEGER('j', "jobs", &max_jobs,
 +              OPT_INTEGER('j', "jobs", &suc.max_jobs,
                            N_("parallel jobs")),
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
        };
        suc.prefix = prefix;
  
 -      update_clone_config_from_gitmodules(&max_jobs);
 -      git_config(git_update_clone_config, &max_jobs);
 +      update_clone_config_from_gitmodules(&suc.max_jobs);
 +      git_config(git_update_clone_config, &suc.max_jobs);
  
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
  
 -      run_processes_parallel(max_jobs,
 -                             update_clone_get_next_task,
 -                             update_clone_start_failure,
 -                             update_clone_task_finished,
 -                             &suc);
 -
 -      /*
 -       * We saved the output and put it out all at once now.
 -       * That means:
 -       * - the listener does not have to interleave their (checkout)
 -       *   work with our fetching.  The writes involved in a
 -       *   checkout involve more straightforward sequential I/O.
 -       * - the listener can avoid doing any work if fetching failed.
 -       */
 -      if (suc.quickstop)
 -              return 1;
 -
 -      for_each_string_list_item(item, &suc.projectlines)
 -              fprintf(stdout, "%s", item->string);
 -
 -      return 0;
 +      return update_submodules(&suc);
  }
  
  static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@@ -2020,45 -1956,6 +2037,45 @@@ static int push_check(int argc, const c
        return 0;
  }
  
 +static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
 +{
 +      const struct submodule *sub;
 +      const char *path;
 +      char *cw;
 +      struct repository subrepo;
 +
 +      if (argc != 2)
 +              BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
 +
 +      path = argv[1];
 +
 +      sub = submodule_from_path(the_repository, &null_oid, path);
 +      if (!sub)
 +              BUG("We could get the submodule handle before?");
 +
 +      if (repo_submodule_init(&subrepo, the_repository, path))
 +              die(_("could not get a repository handle for submodule '%s'"), path);
 +
 +      if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
 +              char *cfg_file, *abs_path;
 +              const char *rel_path;
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              cfg_file = repo_git_path(&subrepo, "config");
 +
 +              abs_path = absolute_pathdup(path);
 +              rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
 +
 +              git_config_set_in_file(cfg_file, "core.worktree", rel_path);
 +
 +              free(cfg_file);
 +              free(abs_path);
 +              strbuf_release(&sb);
 +      }
 +
 +      return 0;
 +}
 +
  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
  {
        int i;
@@@ -2136,9 -2033,7 +2153,9 @@@ static struct cmd_struct commands[] = 
        {"list", module_list, 0},
        {"name", module_name, 0},
        {"clone", module_clone, 0},
 +      {"update-module-mode", module_update_module_mode, 0},
        {"update-clone", update_clone, 0},
 +      {"ensure-core-worktree", ensure_core_worktree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},