Merge branch 'js/normalize-path-copy-ceil'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:37 +0000 (14:45 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:37 +0000 (14:45 -0800)
A pathname that begins with "//" or "\\" on Windows is special but
path normalization logic was unaware of it.

* js/normalize-path-copy-ceil:
normalize_path_copy(): fix pushing to //server/share/dir on Windows

1  2 
path.c
diff --combined path.c
index 52d889c88ea8483b7a78937804bb1b789d2daa1e,f4e5ffb44bc2c4ad67c8fdec4581206c75ed2950..efcedafba6f2c0cae218f2d32425c13272df8dea
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -6,7 -6,6 +6,7 @@@
  #include "string-list.h"
  #include "dir.h"
  #include "worktree.h"
 +#include "submodule-config.h"
  
  static int get_st_mode_bits(const char *path, int *mode)
  {
@@@ -25,8 -24,7 +25,8 @@@ static struct strbuf *get_pathname(void
                STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
        };
        static int index;
 -      struct strbuf *sb = &pathname_array[3 & ++index];
 +      struct strbuf *sb = &pathname_array[index];
 +      index = (index + 1) % ARRAY_SIZE(pathname_array);
        strbuf_reset(sb);
        return sb;
  }
@@@ -382,8 -380,6 +382,8 @@@ static void adjust_git_path(struct strb
                              get_index_file(), strlen(get_index_file()));
        else if (git_db_env && dir_prefix(base, "objects"))
                replace_dir(buf, git_dir_len + 7, get_object_directory());
 +      else if (git_hooks_path && dir_prefix(base, "hooks"))
 +              replace_dir(buf, git_dir_len + 5, git_hooks_path);
        else if (git_common_dir_env)
                update_common_dir(buf, git_dir_len, NULL);
  }
@@@ -470,16 -466,12 +470,16 @@@ const char *worktree_git_path(const str
        return pathname->buf;
  }
  
 -static void do_submodule_path(struct strbuf *buf, const char *path,
 -                            const char *fmt, va_list args)
 +/* Returns 0 on success, negative on failure. */
 +#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
 +static int do_submodule_path(struct strbuf *buf, const char *path,
 +                           const char *fmt, va_list args)
  {
        const char *git_dir;
        struct strbuf git_submodule_common_dir = STRBUF_INIT;
        struct strbuf git_submodule_dir = STRBUF_INIT;
 +      const struct submodule *sub;
 +      int err = 0;
  
        strbuf_addstr(buf, path);
        strbuf_complete(buf, '/');
                strbuf_reset(buf);
                strbuf_addstr(buf, git_dir);
        }
 +      if (!is_git_directory(buf->buf)) {
 +              gitmodules_config();
 +              sub = submodule_from_path(null_sha1, path);
 +              if (!sub) {
 +                      err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
 +                      goto cleanup;
 +              }
 +              strbuf_reset(buf);
 +              strbuf_git_path(buf, "%s/%s", "modules", sub->name);
 +      }
 +
        strbuf_addch(buf, '/');
        strbuf_addbuf(&git_submodule_dir, buf);
  
  
        strbuf_cleanup_path(buf);
  
 +cleanup:
        strbuf_release(&git_submodule_dir);
        strbuf_release(&git_submodule_common_dir);
 +
 +      return err;
  }
  
  char *git_pathdup_submodule(const char *path, const char *fmt, ...)
  {
 +      int err;
        va_list args;
        struct strbuf buf = STRBUF_INIT;
        va_start(args, fmt);
 -      do_submodule_path(&buf, path, fmt, args);
 +      err = do_submodule_path(&buf, path, fmt, args);
        va_end(args);
 +      if (err) {
 +              strbuf_release(&buf);
 +              return NULL;
 +      }
        return strbuf_detach(&buf, NULL);
  }
  
 -void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 -                             const char *fmt, ...)
 +int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 +                            const char *fmt, ...)
  {
 +      int err;
        va_list args;
        va_start(args, fmt);
 -      do_submodule_path(buf, path, fmt, args);
 +      err = do_submodule_path(buf, path, fmt, args);
        va_end(args);
 +
 +      return err;
  }
  
  static void do_git_common_path(struct strbuf *buf,
@@@ -991,7 -961,7 +991,7 @@@ const char *remove_leading_path(const c
   *
   * Performs the following normalizations on src, storing the result in dst:
   * - Ensures that components are separated by '/' (Windows only)
-  * - Squashes sequences of '/'.
+  * - Squashes sequences of '/' except "//server/share" on Windows
   * - Removes "." components.
   * - Removes ".." components, and the components the precede them.
   * Returns failure (non-zero) if a ".." component appears as first path
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
  {
        char *dst0;
-       int i;
+       const char *end;
  
-       for (i = has_dos_drive_prefix(src); i > 0; i--)
-               *dst++ = *src++;
+       /*
+        * Copy initial part of absolute path: "/", "C:/", "//server/share/".
+        */
+       end = src + offset_1st_component(src);
+       while (src < end) {
+               char c = *src++;
+               if (is_dir_sep(c))
+                       c = '/';
+               *dst++ = c;
+       }
        dst0 = dst;
  
-       if (is_dir_sep(*src)) {
-               *dst++ = '/';
-               while (is_dir_sep(*src))
-                       src++;
-       }
+       while (is_dir_sep(*src))
+               src++;
  
        for (;;) {
                char c = *src;