Merge branch 'jk/maint-fetch-submodule-check-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Sun, 16 Oct 2011 03:46:38 +0000 (20:46 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 16 Oct 2011 03:46:38 +0000 (20:46 -0700)
* jk/maint-fetch-submodule-check-fix:
fetch: avoid quadratic loop checking for updated submodules

1  2 
submodule.c
diff --combined submodule.c
index ad86534ba1e1e1726fd6427c65182ba954957904,9431c42dfbce3ae6101314e653721bfb3c5cc172..08756387e207700861cfa6dac039334ecd53c7a1
@@@ -8,12 -8,17 +8,17 @@@
  #include "diffcore.h"
  #include "refs.h"
  #include "string-list.h"
+ #include "sha1-array.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;
+ static struct sha1_array ref_tips_before_fetch;
+ static struct sha1_array ref_tips_after_fetch;
  /*
   * The following flag is set if the .gitmodules file is unmerged. We then
   * disable recursion for all submodules where .git/config doesn't have a
@@@ -32,7 -37,7 +37,7 @@@ static int add_submodule_odb(const cha
        const char *git_dir;
  
        strbuf_addf(&objects_directory, "%s/.git", path);
 -      git_dir = read_gitfile_gently(objects_directory.buf);
 +      git_dir = read_gitfile(objects_directory.buf);
        if (git_dir) {
                strbuf_reset(&objects_directory);
                strbuf_addstr(&objects_directory, git_dir);
@@@ -308,114 -313,6 +313,114 @@@ 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)
 +{
 +      return 1;
 +}
 +
 +static int submodule_needs_pushing(const char *path, const unsigned char sha1[20])
 +{
 +      if (add_submodule_odb(path) || !lookup_commit_reference(sha1))
 +              return 0;
 +
 +      if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 +              struct child_process cp;
 +              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;
 +              cp.no_stdin = 1;
 +              cp.out = -1;
 +              cp.dir = path;
 +              if (start_command(&cp))
 +                      die("Could not run 'git rev-list %s --not --remotes -n 1' command in submodule %s",
 +                              sha1_to_hex(sha1), path);
 +              if (strbuf_read(&buf, cp.out, 41))
 +                      needs_pushing = 1;
 +              finish_command(&cp);
 +              close(cp.out);
 +              strbuf_release(&buf);
 +              return needs_pushing;
 +      }
 +
 +      return 0;
 +}
 +
 +static void collect_submodules_from_diff(struct diff_queue_struct *q,
 +                                       struct diff_options *options,
 +                                       void *data)
 +{
 +      int i;
 +      int *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;
 +              }
 +      }
 +}
 +
 +
 +static void commit_need_pushing(struct commit *commit, struct commit_list *parent, int *needs_pushing)
 +{
 +      const unsigned char (*parents)[20];
 +      unsigned int i, n;
 +      struct rev_info rev;
 +
 +      n = commit_list_count(parent);
 +      parents = xmalloc(n * sizeof(*parents));
 +
 +      for (i = 0; i < n; i++) {
 +              hashcpy((unsigned char *)(parents + i), parent->item->object.sha1);
 +              parent = parent->next;
 +      }
 +
 +      init_revisions(&rev, NULL);
 +      rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
 +      rev.diffopt.format_callback = collect_submodules_from_diff;
 +      rev.diffopt.format_callback_data = needs_pushing;
 +      diff_tree_combined(commit->object.sha1, parents, n, 1, &rev);
 +
 +      free(parents);
 +}
 +
 +int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
 +{
 +      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);
 +      init_revisions(&rev, NULL);
 +      sha1_copy = xstrdup(sha1_to_hex(new_sha1));
 +      argv[1] = sha1_copy;
 +      argv[3] = remotes_arg.buf;
 +      setup_revisions(argc, argv, &rev, NULL);
 +      if (prepare_revision_walk(&rev))
 +              die("revision walk setup failed");
 +
 +      while ((commit = get_revision(&rev)) && !needs_pushing)
 +              commit_need_pushing(commit, commit->parents, &needs_pushing);
 +
 +      free(sha1_copy);
 +      strbuf_release(&remotes_arg);
 +
 +      return needs_pushing;
 +}
 +
  static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
  {
        int is_present = 0;
@@@ -474,20 -371,72 +479,76 @@@ static void submodule_collect_changed_c
        }
  }
  
+ static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
+                            int flags, void *data)
+ {
+       sha1_array_append(data, sha1);
+       return 0;
+ }
  void check_for_new_submodule_commits(unsigned char new_sha1[20])
+ {
+       if (!initialized_fetch_ref_tips) {
+               for_each_ref(add_sha1_to_array, &ref_tips_before_fetch);
+               initialized_fetch_ref_tips = 1;
+       }
+       sha1_array_append(&ref_tips_after_fetch, new_sha1);
+ }
+ struct argv_array {
+       const char **argv;
+       unsigned int argc;
+       unsigned int alloc;
+ };
+ static void init_argv(struct argv_array *array)
+ {
+       array->argv = NULL;
+       array->argc = 0;
+       array->alloc = 0;
+ }
+ static void push_argv(struct argv_array *array, const char *value)
+ {
+       ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
+       array->argv[array->argc++] = xstrdup(value);
+       array->argv[array->argc] = NULL;
+ }
+ static void clear_argv(struct argv_array *array)
+ {
+       int i;
+       for (i = 0; i < array->argc; i++)
+               free((char **)array->argv[i]);
+       free(array->argv);
+       init_argv(array);
+ }
+ static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
+ {
+       push_argv(data, sha1_to_hex(sha1));
+ }
+ static void calculate_changed_submodule_paths(void)
  {
        struct rev_info rev;
        struct commit *commit;
-       const char *argv[] = {NULL, NULL, "--not", "--all", NULL};
-       int argc = ARRAY_SIZE(argv) - 1;
+       struct argv_array argv;
  
 +      /* No need to check if there are no submodules configured */
 +      if (!config_name_for_path.nr)
 +              return;
 +
        init_revisions(&rev, NULL);
-       argv[1] = xstrdup(sha1_to_hex(new_sha1));
-       setup_revisions(argc, argv, &rev, NULL);
+       init_argv(&argv);
+       push_argv(&argv, "--"); /* argv[0] program name */
+       sha1_array_for_each_unique(&ref_tips_after_fetch,
+                                  add_sha1_to_argv, &argv);
+       push_argv(&argv, "--not");
+       sha1_array_for_each_unique(&ref_tips_before_fetch,
+                                  add_sha1_to_argv, &argv);
+       setup_revisions(argv.argc, argv.argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
                while (parent) {
                        struct diff_options diff_opts;
                        diff_setup(&diff_opts);
 +                      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)
                        parent = parent->next;
                }
        }
-       free((char *)argv[1]);
+       clear_argv(&argv);
+       sha1_array_clear(&ref_tips_before_fetch);
+       sha1_array_clear(&ref_tips_after_fetch);
+       initialized_fetch_ref_tips = 0;
  }
  
  int fetch_populated_submodules(int num_options, const char **options,
        cp.git_cmd = 1;
        cp.no_stdin = 1;
  
+       calculate_changed_submodule_paths();
        for (i = 0; i < active_nr; i++) {
                struct strbuf submodule_path = STRBUF_INIT;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
                strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
                strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
 -              git_dir = read_gitfile_gently(submodule_git_dir.buf);
 +              git_dir = read_gitfile(submodule_git_dir.buf);
                if (!git_dir)
                        git_dir = submodule_git_dir.buf;
                if (is_directory(git_dir)) {
@@@ -629,7 -583,7 +696,7 @@@ unsigned is_submodule_modified(const ch
        const char *git_dir;
  
        strbuf_addf(&buf, "%s/.git", path);
 -      git_dir = read_gitfile_gently(buf.buf);
 +      git_dir = read_gitfile(buf.buf);
        if (!git_dir)
                git_dir = buf.buf;
        if (!is_directory(git_dir)) {