]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Sync with 2.17.3
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 4 Dec 2019 21:21:20 +0000 (22:21 +0100)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 6 Dec 2019 15:29:15 +0000 (16:29 +0100)
* maint-2.17: (32 commits)
  Git 2.17.3
  Git 2.16.6
  test-drop-caches: use `has_dos_drive_prefix()`
  Git 2.15.4
  Git 2.14.6
  mingw: handle `subst`-ed "DOS drives"
  mingw: refuse to access paths with trailing spaces or periods
  mingw: refuse to access paths with illegal characters
  unpack-trees: let merged_entry() pass through do_add_entry()'s errors
  quote-stress-test: offer to test quoting arguments for MSYS2 sh
  t6130/t9350: prepare for stringent Win32 path validation
  quote-stress-test: allow skipping some trials
  quote-stress-test: accept arguments to test via the command-line
  tests: add a helper to stress test argument quoting
  mingw: fix quoting of arguments
  Disallow dubiously-nested submodule git directories
  protect_ntfs: turn on NTFS protection by default
  path: also guard `.gitmodules` against NTFS Alternate Data Streams
  is_ntfs_dotgit(): speed it up
  mingw: disallow backslash characters in tree objects' file names
  ...

29 files changed:
1  2 
Documentation/git-fast-import.txt
Documentation/gitmodules.txt
builtin/clone.c
builtin/submodule--helper.c
compat/mingw.c
config.mak.uname
connect.c
environment.c
fast-import.c
fsck.c
git-compat-util.h
git-submodule.sh
path.c
read-cache.c
submodule-config.c
submodule.c
submodule.h
t/helper/test-drop-caches.c
t/helper/test-path-utils.c
t/helper/test-run-command.c
t/t0060-path-utils.sh
t/t1450-fsck.sh
t/t7406-submodule-update.sh
t/t7415-submodule-names.sh
t/t9300-fast-import.sh
t/t9350-fast-export.sh
transport-helper.c
tree-walk.c
unpack-trees.c

Simple merge
Simple merge
diff --cc builtin/clone.c
Simple merge
index 1902b6c3199e104416b430d5a1ae2ac06d7c899d,cd75c7f61c874809661a51f138becb1d2a53baaa..05e5ed32b042676dc0ea4264f607584d5d046bff
@@@ -17,7 -16,7 +17,8 @@@
  #include "revision.h"
  #include "diffcore.h"
  #include "diff.h"
 +#include "object-store.h"
+ #include "dir.h"
  
  #define OPT_QUIET (1 << 0)
  #define OPT_CACHED (1 << 1)
@@@ -1203,7 -1192,7 +1204,7 @@@ static int module_clone(int argc, cons
        char *p, *path = NULL, *sm_gitdir;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
-       int dissociate = 0;
 -      int require_init = 0;
++      int dissociate = 0, require_init = 0;
        char *sm_alternate = NULL, *error_strategy = NULL;
  
        struct option module_clone_options[] = {
@@@ -1315,7 -1311,7 +1324,8 @@@ struct submodule_update_clone 
        int quiet;
        int recommend_shallow;
        struct string_list references;
 +      int dissociate;
+       unsigned require_init;
        const char *depth;
        const char *recursive_prefix;
        const char *prefix;
        int failed_clones_nr, failed_clones_alloc;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
--      SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
++      SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
        NULL, NULL, NULL, \
        STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
  
diff --cc compat/mingw.c
Simple merge
Simple merge
diff --cc connect.c
Simple merge
diff --cc environment.c
Simple merge
diff --cc fast-import.c
index 4d55910ab9a0634a553c01643035ef9afe6034c7,35119c7a5dfe380c18f71f435fe44581701121e9..82a289d7a5a0acebedd18497eadefb46ff61dd68
@@@ -3425,8 -3478,22 +3442,22 @@@ int cmd_main(int argc, const char **arg
        atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
        branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
 -      marks = pool_calloc(1, sizeof(struct mark_set));
 +      marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
  
+       /*
+        * We don't parse most options until after we've seen the set of
+        * "feature" lines at the start of the stream (which allows the command
+        * line to override stream data). But we must do an early parse of any
+        * command-line options that impact how we interpret the feature lines.
+        */
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (*arg != '-' || !strcmp(arg, "--"))
+                       break;
+               if (!strcmp(arg, "--allow-unsafe-features"))
+                       allow_unsafe_features = 1;
+       }
        global_argc = argc;
        global_argv = argv;
  
diff --cc fsck.c
Simple merge
Simple merge
index 78073cd87d1b1775bb4f4264557aba18c73c5911,73594e0e35d094a329fecbe3e59e2aac6630ad6c..74f5fe6a86e02550aa227d6121edd0486f894d18
@@@ -562,10 -557,10 +567,11 @@@ cmd_update(
                ${prefix:+--recursive-prefix "$prefix"} \
                ${update:+--update "$update"} \
                ${reference:+"$reference"} \
 +              ${dissociate:+"--dissociate"} \
                ${depth:+--depth "$depth"} \
 -              ${recommend_shallow:+"$recommend_shallow"} \
 -              ${jobs:+$jobs} \
+               ${require_init:+--require-init} \
 +              $recommend_shallow \
 +              $jobs \
                "$@" || echo "#unmatched" $?
        } | {
        err=
diff --cc path.c
Simple merge
diff --cc read-cache.c
Simple merge
Simple merge
diff --cc submodule.c
index 939d6870ecd7cfbc62f9fa3fb867e5c7362ed9bd,e3aa8b642fc87cf504e208d3922bfd5da5e8470e..893083cbf043fd91def350cc6d2617b8cad4bc15
        return ret;
  }
  
 -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 = OBJECT_ARRAY_INIT;
 -      struct commit *commit;
 -      int contains_another;
 -
 -      char merged_revision[42];
 -      const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
 -                                 "--all", merged_revision, NULL };
 -      struct rev_info revs;
 -      struct setup_revision_opt rev_opts;
 -
 -      memset(result, 0, sizeof(struct object_array));
 -      memset(&rev_opts, 0, sizeof(rev_opts));
 -
 -      /* get all revisions that merge commit a */
 -      xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
 -                      oid_to_hex(&a->object.oid));
 -      init_revisions(&revs, NULL);
 -      rev_opts.submodule = path;
 -      /* FIXME: can't handle linked worktrees in submodules yet */
 -      revs.single_worktree = path != NULL;
 -      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))
 -                      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
 -       * result.
 -       */
 -      for (i = 0; i < merges.nr; i++) {
 -              struct commit *m1 = (struct commit *) merges.objects[i].item;
 -
 -              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)) {
 -                              contains_another = 1;
 -                              break;
 -                      }
 -              }
 -
 -              if (!contains_another)
 -                      add_object_array(merges.objects[i].item, NULL, result);
 -      }
 -
 -      object_array_clear(&merges);
 -      return result->nr;
 -}
 -
 -static void print_commit(struct commit *commit)
 -{
 -      struct strbuf sb = STRBUF_INIT;
 -      struct pretty_print_context ctx = {0};
 -      ctx.date_mode.type = DATE_NORMAL;
 -      format_commit_message(commit, " %h: %m %s", &sb, &ctx);
 -      fprintf(stderr, "%s\n", sb.buf);
 -      strbuf_release(&sb);
 -}
 -
 -#define MERGE_WARNING(path, msg) \
 -      warning("Failed to merge submodule %s (%s)", path, msg);
 -
 -int merge_submodule(struct object_id *result, const char *path,
 -                  const struct object_id *base, const struct object_id *a,
 -                  const struct object_id *b, int search)
 -{
 -      struct commit *commit_base, *commit_a, *commit_b;
 -      int parent_count;
 -      struct object_array merges;
 -
 -      int i;
 -
 -      /* store a in result in case we fail */
 -      oidcpy(result, a);
 -
 -      /* we can not handle deletion conflicts */
 -      if (is_null_oid(base))
 -              return 0;
 -      if (is_null_oid(a))
 -              return 0;
 -      if (is_null_oid(b))
 -              return 0;
 -
 -      if (add_submodule_odb(path)) {
 -              MERGE_WARNING(path, "not checked out");
 -              return 0;
 -      }
 -
 -      if (!(commit_base = lookup_commit_reference(base)) ||
 -          !(commit_a = lookup_commit_reference(a)) ||
 -          !(commit_b = lookup_commit_reference(b))) {
 -              MERGE_WARNING(path, "commits not present");
 -              return 0;
 -      }
 -
 -      /* check whether both changes are forward */
 -      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)) {
 -              oidcpy(result, b);
 -              return 1;
 -      }
 -      if (in_merge_bases(commit_b, commit_a)) {
 -              oidcpy(result, a);
 -              return 1;
 -      }
 -
 -      /*
 -       * Case #2: There are one or more merges that contain a and b in
 -       * the submodule. If there is only one, then present it as a
 -       * suggestion to the user, but leave it marked unmerged so the
 -       * user needs to confirm the resolution.
 -       */
 -
 -      /* Skip the search if makes no sense to the calling context.  */
 -      if (!search)
 -              return 0;
 -
 -      /* find commit which merges them */
 -      parent_count = find_first_merges(&merges, path, commit_a, commit_b);
 -      switch (parent_count) {
 -      case 0:
 -              MERGE_WARNING(path, "merge following commits not found");
 -              break;
 -
 -      case 1:
 -              MERGE_WARNING(path, "not fast-forward");
 -              fprintf(stderr, "Found a possible merge resolution "
 -                              "for the submodule:\n");
 -              print_commit((struct commit *) merges.objects[0].item);
 -              fprintf(stderr,
 -                      "If this is correct simply add it to the index "
 -                      "for example\n"
 -                      "by using:\n\n"
 -                      "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
 -                      "which will accept this suggestion.\n",
 -                      oid_to_hex(&merges.objects[0].item->oid), path);
 -              break;
 -
 -      default:
 -              MERGE_WARNING(path, "multiple merges found");
 -              for (i = 0; i < merges.nr; i++)
 -                      print_commit((struct commit *) merges.objects[i].item);
 -      }
 -
 -      object_array_clear(&merges);
 -      return 0;
 -}
 -
+ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
+ {
+       size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
+       char *p;
+       int ret = 0;
+       if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
+           strcmp(p, submodule_name))
+               BUG("submodule name '%s' not a suffix of git dir '%s'",
+                   submodule_name, git_dir);
+       /*
+        * We prevent the contents of sibling submodules' git directories to
+        * clash.
+        *
+        * Example: having a submodule named `hippo` and another one named
+        * `hippo/hooks` would result in the git directories
+        * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
+        * but the latter directory is already designated to contain the hooks
+        * of the former.
+        */
+       for (; *p; p++) {
+               if (is_dir_sep(*p)) {
+                       char c = *p;
+                       *p = '\0';
+                       if (is_git_directory(git_dir))
+                               ret = -1;
+                       *p = c;
+                       if (ret < 0)
+                               return error(_("submodule git dir '%s' is "
+                                              "inside git dir '%.*s'"),
+                                            git_dir,
+                                            (int)(p - git_dir), git_dir);
+               }
+       }
+       return 0;
+ }
  /*
   * Embeds a single submodules git directory into the superprojects git dir,
   * non recursively.
diff --cc submodule.h
Simple merge
Simple merge
index ae091d9b3e63cb506b2530217d40048b301faaa3,8b3ce07860d4d5dfeca06041b811282a59ef40bb..e737a941d3bc1d58c768b2e2819d0759e17f83c2
@@@ -177,7 -176,100 +177,100 @@@ static int is_dotgitmodules(const char 
        return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
  }
  
 -int cmd_main(int argc, const char **argv)
+ /*
+  * A very simple, reproducible pseudo-random generator. Copied from
+  * `test-genrandom.c`.
+  */
+ static uint64_t my_random_value = 1234;
+ static uint64_t my_random(void)
+ {
+       my_random_value = my_random_value * 1103515245 + 12345;
+       return my_random_value;
+ }
+ /*
+  * A fast approximation of the square root, without requiring math.h.
+  *
+  * It uses Newton's method to approximate the solution of 0 = x^2 - value.
+  */
+ static double my_sqrt(double value)
+ {
+       const double epsilon = 1e-6;
+       double x = value;
+       if (value == 0)
+               return 0;
+       for (;;) {
+               double delta = (value / x - x) / 2;
+               if (delta < epsilon && delta > -epsilon)
+                       return x + delta;
+               x += delta;
+       }
+ }
+ static int protect_ntfs_hfs_benchmark(int argc, const char **argv)
+ {
+       size_t i, j, nr, min_len = 3, max_len = 20;
+       char **names;
+       int repetitions = 15, file_mode = 0100644;
+       uint64_t begin, end;
+       double m[3][2], v[3][2];
+       uint64_t cumul;
+       double cumul2;
+       if (argc > 1 && !strcmp(argv[1], "--with-symlink-mode")) {
+               file_mode = 0120000;
+               argc--;
+               argv++;
+       }
+       nr = argc > 1 ? strtoul(argv[1], NULL, 0) : 1000000;
+       ALLOC_ARRAY(names, nr);
+       if (argc > 2) {
+               min_len = strtoul(argv[2], NULL, 0);
+               if (argc > 3)
+                       max_len = strtoul(argv[3], NULL, 0);
+               if (min_len > max_len)
+                       die("min_len > max_len");
+       }
+       for (i = 0; i < nr; i++) {
+               size_t len = min_len + (my_random() % (max_len + 1 - min_len));
+               names[i] = xmallocz(len);
+               while (len > 0)
+                       names[i][--len] = (char)(' ' + (my_random() % ('\x7f' - ' ')));
+       }
+       for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
+               for (protect_hfs = 0; protect_hfs < 2; protect_hfs++) {
+                       cumul = 0;
+                       cumul2 = 0;
+                       for (i = 0; i < repetitions; i++) {
+                               begin = getnanotime();
+                               for (j = 0; j < nr; j++)
+                                       verify_path(names[j], file_mode);
+                               end = getnanotime();
+                               printf("protect_ntfs = %d, protect_hfs = %d: %lfms\n", protect_ntfs, protect_hfs, (end-begin) / (double)1e6);
+                               cumul += end - begin;
+                               cumul2 += (end - begin) * (end - begin);
+                       }
+                       m[protect_ntfs][protect_hfs] = cumul / (double)repetitions;
+                       v[protect_ntfs][protect_hfs] = my_sqrt(cumul2 / (double)repetitions - m[protect_ntfs][protect_hfs] * m[protect_ntfs][protect_hfs]);
+                       printf("mean: %lfms, stddev: %lfms\n", m[protect_ntfs][protect_hfs] / (double)1e6, v[protect_ntfs][protect_hfs] / (double)1e6);
+               }
+       for (protect_ntfs = 0; protect_ntfs < 2; protect_ntfs++)
+               for (protect_hfs = 0; protect_hfs < 2; protect_hfs++)
+                       printf("ntfs=%d/hfs=%d: %lf%% slower\n", protect_ntfs, protect_hfs, (m[protect_ntfs][protect_hfs] - m[0][0]) * 100 / m[0][0]);
+       return 0;
+ }
 +int cmd__path_utils(int argc, const char **argv)
  {
        if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
                char *buf = xmallocz(strlen(argv[2]));
index 2cc93bb69c522d99491cd8a9e02e211b2c3df807,a701689bd5cb316afaba3b615c409d8a26cef88f..8579b1f7d13394b89dfff161b6f6558d498ac2f3
@@@ -50,7 -49,135 +50,135 @@@ static int task_finished(int result
        return 1;
  }
  
 -              "test-run-command quote-stress-test <options>",
+ static uint64_t my_random_next = 1234;
+ static uint64_t my_random(void)
+ {
+       uint64_t res = my_random_next;
+       my_random_next = my_random_next * 1103515245 + 12345;
+       return res;
+ }
+ static int quote_stress_test(int argc, const char **argv)
+ {
+       /*
+        * We are running a quote-stress test.
+        * spawn a subprocess that runs quote-stress with a
+        * special option that echoes back the arguments that
+        * were passed in.
+        */
+       char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
+       int i, j, k, trials = 100, skip = 0, msys2 = 0;
+       struct strbuf out = STRBUF_INIT;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT_INTEGER('n', "trials", &trials, "Number of trials"),
+               OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
+               OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
+               OPT_END()
+       };
+       const char * const usage[] = {
 -                      argv_array_pushl(&args, "test-run-command",
++              "test-tool run-command quote-stress-test <options>",
+               NULL
+       };
+       argc = parse_options(argc, argv, NULL, options, usage, 0);
+       setenv("MSYS_NO_PATHCONV", "1", 0);
+       for (i = 0; i < trials; i++) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               size_t arg_count, arg_offset;
+               int ret = 0;
+               argv_array_clear(&args);
+               if (msys2)
+                       argv_array_pushl(&args, "sh", "-c",
+                                        "printf %s\\\\0 \"$@\"", "skip", NULL);
+               else
 -int cmd_main(int argc, const char **argv)
++                      argv_array_pushl(&args, "test-tool", "run-command",
+                                        "quote-echo", NULL);
+               arg_offset = args.argc;
+               if (argc > 0) {
+                       trials = 1;
+                       arg_count = argc;
+                       for (j = 0; j < arg_count; j++)
+                               argv_array_push(&args, argv[j]);
+               } else {
+                       arg_count = 1 + (my_random() % 5);
+                       for (j = 0; j < arg_count; j++) {
+                               char buf[20];
+                               size_t min_len = 1;
+                               size_t arg_len = min_len +
+                                       (my_random() % (ARRAY_SIZE(buf) - min_len));
+                               for (k = 0; k < arg_len; k++)
+                                       buf[k] = special[my_random() %
+                                               ARRAY_SIZE(special)];
+                               buf[arg_len] = '\0';
+                               argv_array_push(&args, buf);
+                       }
+               }
+               if (i < skip)
+                       continue;
+               cp.argv = args.argv;
+               strbuf_reset(&out);
+               if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
+                       return error("Failed to spawn child process");
+               for (j = 0, k = 0; j < arg_count; j++) {
+                       const char *arg = args.argv[j + arg_offset];
+                       if (strcmp(arg, out.buf + k))
+                               ret = error("incorrectly quoted arg: '%s', "
+                                           "echoed back as '%s'",
+                                            arg, out.buf + k);
+                       k += strlen(out.buf + k) + 1;
+               }
+               if (k != out.len)
+                       ret = error("got %d bytes, but consumed only %d",
+                                    (int)out.len, (int)k);
+               if (ret) {
+                       fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
+                       for (j = 0; j < arg_count; j++)
+                               fprintf(stderr, "arg #%d: '%s'\n",
+                                       (int)j, args.argv[j + arg_offset]);
+                       strbuf_release(&out);
+                       argv_array_clear(&args);
+                       return ret;
+               }
+               if (i && (i % 100) == 0)
+                       fprintf(stderr, "Trials completed: %d\n", (int)i);
+       }
+       strbuf_release(&out);
+       argv_array_clear(&args);
+       return 0;
+ }
+ static int quote_echo(int argc, const char **argv)
+ {
+       while (argc > 1) {
+               fwrite(argv[1], strlen(argv[1]), 1, stdout);
+               fputc('\0', stdout);
+               argv++;
+               argc--;
+       }
+       return 0;
+ }
 +int cmd__run_command(int argc, const char **argv)
  {
        struct child_process proc = CHILD_PROCESS_INIT;
        int jobs;
index 21a8b531322ca75695ca16c6e8ec7e0ba1056f10,40db3e1e1adad57721087ee69af35224aa6eaad4..f7ab3d5d2f916824bff09994f9b2a2201a24a7ba
@@@ -162,11 -162,20 +162,20 @@@ test_expect_success 'strip_path_suffix
  '
  
  test_expect_success 'absolute path rejects the empty string' '
 -      test_must_fail test-path-utils absolute_path ""
 +      test_must_fail test-tool path-utils absolute_path ""
  '
  
 -              absolute="$(test-path-utils absolute_path "$path")" &&
+ test_expect_success MINGW '<drive-letter>:\\abc is an absolute path' '
+       for letter in : \" C Z 1 รค
+       do
+               path=$letter:\\abc &&
++              absolute="$(test-tool path-utils absolute_path "$path")" &&
+               test "$path" = "$absolute" || return 1
+       done
+ '
  test_expect_success 'real path rejects the empty string' '
 -      test_must_fail test-path-utils real_path ""
 +      test_must_fail test-tool path-utils real_path ""
  '
  
  test_expect_success POSIX 'real path works on absolute paths 1' '
@@@ -432,7 -444,25 +444,25 @@@ test_expect_success 'match .gitmodules
                \
                GI7EB~1 \
                GI7EB~01 \
-               GI7EB~1X
+               GI7EB~1X \
+               \
+               .gitmodules,:\$DATA
+ '
+ test_expect_success MINGW 'is_valid_path() on Windows' '
 -       test-path-utils is_valid_path \
++       test-tool path-utils is_valid_path \
+               win32 \
+               "win32 x" \
+               ../hello.txt \
+               C:\\git \
+               \
+               --not \
+               "win32 "  \
+               "win32 /x "  \
+               "win32."  \
+               "win32 . ." \
+               .../hello.txt \
+               colon:test
  '
  
  test_done
diff --cc t/t1450-fsck.sh
Simple merge
Simple merge
index b68c5f5e8510ddbf4e10f6cb608d9e789bef09b4,717d708bcdba1bdd78a3a3f31b33e8b6a8068af3..31d1e2d51242bb5b2dcd822bceb032c5b23ad019
@@@ -158,22 -151,60 +158,78 @@@ test_expect_success 'fsck detects symli
        )
  '
  
 +test_expect_success 'fsck detects non-blob .gitmodules' '
 +      git init non-blob &&
 +      (
 +              cd non-blob &&
 +
 +              # As above, make the funny tree directly to avoid index
 +              # restrictions.
 +              mkdir subdir &&
 +              cp ../.gitmodules subdir/file &&
 +              git add subdir/file &&
 +              git commit -m ok &&
 +              git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree &&
 +
 +              test_must_fail git fsck 2>output &&
 +              grep gitmodulesBlob output
 +      )
 +'
 +
+ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+       git init squatting &&
+       (
+               cd squatting &&
+               mkdir a &&
+               touch a/..git &&
+               git add a/..git &&
+               test_tick &&
+               git commit -m initial &&
+               modules="$(test_write_lines \
+                       "[submodule \"b.\"]" "url = ." "path = c" \
+                       "[submodule \"b\"]" "url = ." "path = d\\\\a" |
+                       git hash-object -w --stdin)" &&
+               rev="$(git rev-parse --verify HEAD)" &&
+               hash="$(echo x | git hash-object -w --stdin)" &&
+               git -c core.protectNTFS=false update-index --add \
+                       --cacheinfo 100644,$modules,.gitmodules \
+                       --cacheinfo 160000,$rev,c \
+                       --cacheinfo 160000,$rev,d\\a \
+                       --cacheinfo 100644,$hash,d./a/x \
+                       --cacheinfo 100644,$hash,d./a/..git &&
+               test_tick &&
+               git -c core.protectNTFS=false commit -m "module" &&
+               test_must_fail git show HEAD: 2>err &&
+               test_i18ngrep backslash err
+       ) &&
+       test_must_fail git -c core.protectNTFS=false \
+               clone --recurse-submodules squatting squatting-clone 2>err &&
+       test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
+       ! grep gitdir squatting-clone/d/a/git~2
+ '
+ test_expect_success 'git dirs of sibling submodules must not be nested' '
+       git init nested &&
+       test_commit -C nested nested &&
+       (
+               cd nested &&
+               cat >.gitmodules <<-EOF &&
+               [submodule "hippo"]
+                       url = .
+                       path = thing1
+               [submodule "hippo/hooks"]
+                       url = .
+                       path = thing2
+               EOF
+               git clone . thing1 &&
+               git clone . thing2 &&
+               git add .gitmodules thing1 thing2 &&
+               test_tick &&
+               git commit -m nested
+       ) &&
+       test_must_fail git clone --recurse-submodules nested clone 2>err &&
+       test_i18ngrep "is inside git dir" err
+ '
  test_done
Simple merge
Simple merge
Simple merge
diff --cc tree-walk.c
Simple merge
diff --cc unpack-trees.c
Simple merge