]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'tb/use-common-win32-pathfuncs-on-cygwin'
authorJunio C Hamano <gitster@pobox.com>
Mon, 14 Jan 2019 23:29:32 +0000 (15:29 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 14 Jan 2019 23:29:32 +0000 (15:29 -0800)
Cygwin update.

* tb/use-common-win32-pathfuncs-on-cygwin:
  git clone <url> C:\cygwin\home\USER\repo' is working (again)

1  2 
compat/mingw.c
compat/mingw.h
config.mak.uname
git-compat-util.h
t/t5601-clone.sh

diff --combined compat/mingw.c
index 34b3880b29d57eee6d6ae0afbb786d7980e7fa3e,76cad1fe21fb00d0ca3399e2bda34bca4f0fa75c..b459e1a291ab0fe906c9a53a0fbcc61ad2c7e153
@@@ -5,8 -5,6 +5,8 @@@
  #include "../strbuf.h"
  #include "../run-command.h"
  #include "../cache.h"
 +#include "win32/lazyload.h"
 +#include "../config.h"
  
  #define HCAST(type, handle) ((type)(intptr_t)handle)
  
@@@ -204,60 -202,6 +204,60 @@@ static int ask_yes_no_if_possible(cons
        }
  }
  
 +/* Windows only */
 +enum hide_dotfiles_type {
 +      HIDE_DOTFILES_FALSE = 0,
 +      HIDE_DOTFILES_TRUE,
 +      HIDE_DOTFILES_DOTGITONLY
 +};
 +
 +static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 +static char *unset_environment_variables;
 +
 +int mingw_core_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcmp(var, "core.hidedotfiles")) {
 +              if (value && !strcasecmp(value, "dotgitonly"))
 +                      hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 +              else
 +                      hide_dotfiles = git_config_bool(var, value);
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "core.unsetenvvars")) {
 +              free(unset_environment_variables);
 +              unset_environment_variables = xstrdup(value);
 +              return 0;
 +      }
 +
 +      return 0;
 +}
 +
 +/* Normalizes NT paths as returned by some low-level APIs. */
 +static wchar_t *normalize_ntpath(wchar_t *wbuf)
 +{
 +      int i;
 +      /* fix absolute path prefixes */
 +      if (wbuf[0] == '\\') {
 +              /* strip NT namespace prefixes */
 +              if (!wcsncmp(wbuf, L"\\??\\", 4) ||
 +                  !wcsncmp(wbuf, L"\\\\?\\", 4))
 +                      wbuf += 4;
 +              else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
 +                      wbuf += 12;
 +              /* replace remaining '...UNC\' with '\\' */
 +              if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
 +                      wbuf += 2;
 +                      *wbuf = '\\';
 +              }
 +      }
 +      /* convert backslashes to slashes */
 +      for (i = 0; wbuf[i]; i++)
 +              if (wbuf[i] == '\\')
 +                      wbuf[i] = '/';
 +      return wbuf;
 +}
 +
  int mingw_unlink(const char *pathname)
  {
        int ret, tries = 0;
@@@ -350,7 -294,7 +350,7 @@@ static inline int needs_hiding(const ch
                return 0;
  
        /* We cannot use basename(), as it would remove trailing slashes */
-       mingw_skip_dos_drive_prefix((char **)&path);
+       win32_skip_dos_drive_prefix((char **)&path);
        if (!*path)
                return 0;
  
@@@ -648,11 -592,9 +648,11 @@@ static inline long long filetime_to_hns
        return winTime - 116444736000000000LL;
  }
  
 -static inline time_t filetime_to_time_t(const FILETIME *ft)
 +static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
  {
 -      return (time_t)(filetime_to_hnsec(ft) / 10000000);
 +      long long hnsec = filetime_to_hnsec(ft);
 +      ts->tv_sec = (time_t)(hnsec / 10000000);
 +      ts->tv_nsec = (hnsec % 10000000) * 100;
  }
  
  /**
@@@ -711,9 -653,9 +711,9 @@@ static int do_lstat(int follow, const c
                buf->st_size = fdata.nFileSizeLow |
                        (((off_t)fdata.nFileSizeHigh)<<32);
                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 -              buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 -              buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 -              buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
 +              filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 +              filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 +              filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
                        WIN32_FIND_DATAW findbuf;
                        HANDLE handle = FindFirstFileW(wfilename, &findbuf);
@@@ -794,29 -736,6 +794,29 @@@ static int do_stat_internal(int follow
        return do_lstat(follow, alt_name, buf);
  }
  
 +static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
 +{
 +      BY_HANDLE_FILE_INFORMATION fdata;
 +
 +      if (!GetFileInformationByHandle(hnd, &fdata)) {
 +              errno = err_win_to_posix(GetLastError());
 +              return -1;
 +      }
 +
 +      buf->st_ino = 0;
 +      buf->st_gid = 0;
 +      buf->st_uid = 0;
 +      buf->st_nlink = 1;
 +      buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 +      buf->st_size = fdata.nFileSizeLow |
 +              (((off_t)fdata.nFileSizeHigh)<<32);
 +      buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 +      filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 +      filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 +      filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
 +      return 0;
 +}
 +
  int mingw_lstat(const char *file_name, struct stat *buf)
  {
        return do_stat_internal(0, file_name, buf);
@@@ -829,31 -748,32 +829,31 @@@ int mingw_stat(const char *file_name, s
  int mingw_fstat(int fd, struct stat *buf)
  {
        HANDLE fh = (HANDLE)_get_osfhandle(fd);
 -      BY_HANDLE_FILE_INFORMATION fdata;
 +      DWORD avail, type = GetFileType(fh) & ~FILE_TYPE_REMOTE;
  
 -      if (fh == INVALID_HANDLE_VALUE) {
 -              errno = EBADF;
 -              return -1;
 -      }
 -      /* direct non-file handles to MS's fstat() */
 -      if (GetFileType(fh) != FILE_TYPE_DISK)
 -              return _fstati64(fd, buf);
 +      switch (type) {
 +      case FILE_TYPE_DISK:
 +              return get_file_info_by_handle(fh, buf);
  
 -      if (GetFileInformationByHandle(fh, &fdata)) {
 -              buf->st_ino = 0;
 -              buf->st_gid = 0;
 -              buf->st_uid = 0;
 +      case FILE_TYPE_CHAR:
 +      case FILE_TYPE_PIPE:
 +              /* initialize stat fields */
 +              memset(buf, 0, sizeof(*buf));
                buf->st_nlink = 1;
 -              buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 -              buf->st_size = fdata.nFileSizeLow |
 -                      (((off_t)fdata.nFileSizeHigh)<<32);
 -              buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 -              buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 -              buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 -              buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
 +
 +              if (type == FILE_TYPE_CHAR) {
 +                      buf->st_mode = _S_IFCHR;
 +              } else {
 +                      buf->st_mode = _S_IFIFO;
 +                      if (PeekNamedPipe(fh, NULL, 0, NULL, &avail, NULL))
 +                              buf->st_size = avail;
 +              }
                return 0;
 +
 +      default:
 +              errno = EBADF;
 +              return -1;
        }
 -      errno = EBADF;
 -      return -1;
  }
  
  static inline void time_t_to_filetime(time_t t, FILETIME *ft)
@@@ -997,29 -917,8 +997,29 @@@ struct tm *localtime_r(const time_t *ti
  
  char *mingw_getcwd(char *pointer, int len)
  {
 -      wchar_t wpointer[MAX_PATH];
 -      if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
 +      wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
 +      DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
 +
 +      if (!ret || ret >= ARRAY_SIZE(cwd)) {
 +              errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
 +              return NULL;
 +      }
 +      ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
 +      if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
 +              HANDLE hnd = CreateFileW(cwd, 0,
 +                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 +                      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 +              if (hnd == INVALID_HANDLE_VALUE)
 +                      return NULL;
 +              ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
 +              CloseHandle(hnd);
 +              if (!ret || ret >= ARRAY_SIZE(wpointer))
 +                      return NULL;
 +              if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
 +                      return NULL;
 +              return pointer;
 +      }
 +      if (!ret || ret >= ARRAY_SIZE(wpointer))
                return NULL;
        if (xwcstoutf(pointer, wpointer, len) < 0)
                return NULL;
  }
  
  /*
 - * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
 - * (Parsing C++ Command-Line Arguments)
 + * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
 + * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
   */
  static const char *quote_arg(const char *arg)
  {
@@@ -1171,142 -1070,44 +1171,142 @@@ static char *path_lookup(const char *cm
        return prog;
  }
  
 -static int do_putenv(char **env, const char *name, int size, int free_old);
 +static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
 +{
 +      while (*s && *s != c)
 +              s++;
 +      return s;
 +}
  
 -/* used number of elements of environ array, including terminating NULL */
 -static int environ_size = 0;
 -/* allocated size of environ array, in bytes */
 -static int environ_alloc = 0;
 +/* Compare only keys */
 +static int wenvcmp(const void *a, const void *b)
 +{
 +      wchar_t *p = *(wchar_t **)a, *q = *(wchar_t **)b;
 +      size_t p_len, q_len;
 +
 +      /* Find the keys */
 +      p_len = wcschrnul(p, L'=') - p;
 +      q_len = wcschrnul(q, L'=') - q;
 +
 +      /* If the length differs, include the shorter key's NUL */
 +      if (p_len < q_len)
 +              p_len++;
 +      else if (p_len > q_len)
 +              p_len = q_len + 1;
 +
 +      return _wcsnicmp(p, q, p_len);
 +}
 +
 +/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
 +#ifndef INTERNAL_QSORT
 +#include "qsort.c"
 +#endif
  
  /*
 - * Create environment block suitable for CreateProcess. Merges current
 - * process environment and the supplied environment changes.
 + * Build an environment block combining the inherited environment
 + * merged with the given list of settings.
 + *
 + * Values of the form "KEY=VALUE" in deltaenv override inherited values.
 + * Values of the form "KEY" in deltaenv delete inherited values.
 + *
 + * Multiple entries in deltaenv for the same key are explicitly allowed.
 + *
 + * We return a contiguous block of UNICODE strings with a final trailing
 + * zero word.
   */
  static wchar_t *make_environment_block(char **deltaenv)
  {
 -      wchar_t *wenvblk = NULL;
 -      char **tmpenv;
 -      int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0;
 +      wchar_t *wenv = GetEnvironmentStringsW(), *wdeltaenv, *result, *p;
 +      size_t wlen, s, delta_size, size;
 +
 +      wchar_t **array = NULL;
 +      size_t alloc = 0, nr = 0, i;
 +
 +      size = 1; /* for extra NUL at the end */
 +
 +      /* If there is no deltaenv to apply, simply return a copy. */
 +      if (!deltaenv || !*deltaenv) {
 +              for (p = wenv; p && *p; ) {
 +                      size_t s = wcslen(p) + 1;
 +                      size += s;
 +                      p += s;
 +              }
 +
 +              ALLOC_ARRAY(result, size);
 +              memcpy(result, wenv, size * sizeof(*wenv));
 +              FreeEnvironmentStringsW(wenv);
 +              return result;
 +      }
 +
 +      /*
 +       * If there is a deltaenv, let's accumulate all keys into `array`,
 +       * sort them using the stable git_qsort() and then copy, skipping
 +       * duplicate keys
 +       */
 +      for (p = wenv; p && *p; ) {
 +              ALLOC_GROW(array, nr + 1, alloc);
 +              s = wcslen(p) + 1;
 +              array[nr++] = p;
 +              p += s;
 +              size += s;
 +      }
 +
 +      /* (over-)assess size needed for wchar version of deltaenv */
 +      for (delta_size = 0, i = 0; deltaenv[i]; i++)
 +              delta_size += strlen(deltaenv[i]) * 2 + 1;
 +      ALLOC_ARRAY(wdeltaenv, delta_size);
 +
 +      /* convert the deltaenv, appending to array */
 +      for (i = 0, p = wdeltaenv; deltaenv[i]; i++) {
 +              ALLOC_GROW(array, nr + 1, alloc);
 +              wlen = xutftowcs(p, deltaenv[i], wdeltaenv + delta_size - p);
 +              array[nr++] = p;
 +              p += wlen + 1;
 +      }
 +
 +      git_qsort(array, nr, sizeof(*array), wenvcmp);
 +      ALLOC_ARRAY(result, size + delta_size);
  
 -      while (deltaenv && deltaenv[i])
 -              i++;
 +      for (p = result, i = 0; i < nr; i++) {
 +              /* Skip any duplicate keys; last one wins */
 +              while (i + 1 < nr && !wenvcmp(array + i, array + i + 1))
 +                     i++;
  
 -      /* copy the environment, leaving space for changes */
 -      ALLOC_ARRAY(tmpenv, size + i);
 -      memcpy(tmpenv, environ, size * sizeof(char*));
 +              /* Skip "to delete" entry */
 +              if (!wcschr(array[i], L'='))
 +                      continue;
 +
 +              size = wcslen(array[i]) + 1;
 +              memcpy(p, array[i], size * sizeof(*p));
 +              p += size;
 +      }
 +      *p = L'\0';
 +
 +      free(array);
 +      free(wdeltaenv);
 +      FreeEnvironmentStringsW(wenv);
 +      return result;
 +}
  
 -      /* merge supplied environment changes into the temporary environment */
 -      for (i = 0; deltaenv && deltaenv[i]; i++)
 -              size = do_putenv(tmpenv, deltaenv[i], size, 0);
 +static void do_unset_environment_variables(void)
 +{
 +      static int done;
 +      char *p = unset_environment_variables;
  
 -      /* create environment block from temporary environment */
 -      for (i = 0; tmpenv[i]; i++) {
 -              size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */
 -              ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz);
 -              wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1;
 +      if (done || !p)
 +              return;
 +      done = 1;
 +
 +      for (;;) {
 +              char *comma = strchr(p, ',');
 +
 +              if (comma)
 +                      *comma = '\0';
 +              unsetenv(p);
 +              if (!comma)
 +                      break;
 +              p = comma + 1;
        }
 -      /* add final \0 terminator */
 -      wenvblk[wenvpos] = 0;
 -      free(tmpenv);
 -      return wenvblk;
  }
  
  struct pinfo_t {
@@@ -1327,12 -1128,9 +1327,12 @@@ static pid_t mingw_spawnve_fd(const cha
        wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
 +      HANDLE cons;
 +
 +      do_unset_environment_variables();
  
        /* Determine whether or not we are associated to a console */
 -      HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE,
 +      cons = CreateFile("CONOUT$", GENERIC_WRITE,
                        FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL, NULL);
        if (cons == INVALID_HANDLE_VALUE) {
@@@ -1551,83 -1349,87 +1551,83 @@@ int mingw_kill(pid_t pid, int sig
  }
  
  /*
 - * Compare environment entries by key (i.e. stopping at '=' or '\0').
 + * UTF-8 versions of getenv(), putenv() and unsetenv().
 + * Internally, they use the CRT's stock UNICODE routines
 + * to avoid data loss.
   */
 -static int compareenv(const void *v1, const void *v2)
 +char *mingw_getenv(const char *name)
  {
 -      const char *e1 = *(const char**)v1;
 -      const char *e2 = *(const char**)v2;
 +#define GETENV_MAX_RETAIN 30
 +      static char *values[GETENV_MAX_RETAIN];
 +      static int value_counter;
 +      int len_key, len_value;
 +      wchar_t *w_key;
 +      char *value;
 +      wchar_t w_value[32768];
  
 -      for (;;) {
 -              int c1 = *e1++;
 -              int c2 = *e2++;
 -              c1 = (c1 == '=') ? 0 : tolower(c1);
 -              c2 = (c2 == '=') ? 0 : tolower(c2);
 -              if (c1 > c2)
 -                      return 1;
 -              if (c1 < c2)
 -                      return -1;
 -              if (c1 == 0)
 -                      return 0;
 -      }
 -}
 +      if (!name || !*name)
 +              return NULL;
  
 -static int bsearchenv(char **env, const char *name, size_t size)
 -{
 -      unsigned low = 0, high = size;
 -      while (low < high) {
 -              unsigned mid = low + ((high - low) >> 1);
 -              int cmp = compareenv(&env[mid], &name);
 -              if (cmp < 0)
 -                      low = mid + 1;
 -              else if (cmp > 0)
 -                      high = mid;
 -              else
 -                      return mid;
 +      len_key = strlen(name) + 1;
 +      /* We cannot use xcalloc() here because that uses getenv() itself */
 +      w_key = calloc(len_key, sizeof(wchar_t));
 +      if (!w_key)
 +              die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
 +      xutftowcs(w_key, name, len_key);
 +      len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
 +      if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
 +              free(w_key);
 +              return NULL;
        }
 -      return ~low; /* not found, return 1's complement of insert position */
 +      free(w_key);
 +
 +      len_value = len_value * 3 + 1;
 +      /* We cannot use xcalloc() here because that uses getenv() itself */
 +      value = calloc(len_value, sizeof(char));
 +      if (!value)
 +              die("Out of memory, (tried to allocate %u bytes)", len_value);
 +      xwcstoutf(value, w_value, len_value);
 +
 +      /*
 +       * We return `value` which is an allocated value and the caller is NOT
 +       * expecting to have to free it, so we keep a round-robin array,
 +       * invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
 +       */
 +      free(values[value_counter]);
 +      values[value_counter++] = value;
 +      if (value_counter >= ARRAY_SIZE(values))
 +              value_counter = 0;
 +
 +      return value;
  }
  
 -/*
 - * If name contains '=', then sets the variable, otherwise it unsets it
 - * Size includes the terminating NULL. Env must have room for size + 1 entries
 - * (in case of insert). Returns the new size. Optionally frees removed entries.
 - */
 -static int do_putenv(char **env, const char *name, int size, int free_old)
 +int mingw_putenv(const char *namevalue)
  {
 -      int i = bsearchenv(env, name, size - 1);
 +      int size;
 +      wchar_t *wide, *equal;
 +      BOOL result;
  
 -      /* optionally free removed / replaced entry */
 -      if (i >= 0 && free_old)
 -              free(env[i]);
 +      if (!namevalue || !*namevalue)
 +              return 0;
  
 -      if (strchr(name, '=')) {
 -              /* if new value ('key=value') is specified, insert or replace entry */
 -              if (i < 0) {
 -                      i = ~i;
 -                      memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*));
 -                      size++;
 -              }
 -              env[i] = (char*) name;
 -      } else if (i >= 0) {
 -              /* otherwise ('key') remove existing entry */
 -              size--;
 -              memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*));
 +      size = strlen(namevalue) * 2 + 1;
 +      wide = calloc(size, sizeof(wchar_t));
 +      if (!wide)
 +              die("Out of memory, (tried to allocate %u wchar_t's)", size);
 +      xutftowcs(wide, namevalue, size);
 +      equal = wcschr(wide, L'=');
 +      if (!equal)
 +              result = SetEnvironmentVariableW(wide, NULL);
 +      else {
 +              *equal = L'\0';
 +              result = SetEnvironmentVariableW(wide, equal + 1);
        }
 -      return size;
 -}
 +      free(wide);
  
 -char *mingw_getenv(const char *name)
 -{
 -      char *value;
 -      int pos = bsearchenv(environ, name, environ_size - 1);
 -      if (pos < 0)
 -              return NULL;
 -      value = strchr(environ[pos], '=');
 -      return value ? &value[1] : NULL;
 -}
 +      if (!result)
 +              errno = err_win_to_posix(GetLastError());
  
 -int mingw_putenv(const char *namevalue)
 -{
 -      ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
 -      environ_size = do_putenv(environ, namevalue, environ_size, 1);
 -      return 0;
 +      return result ? 0 : -1;
  }
  
  /*
@@@ -1775,8 -1577,7 +1775,8 @@@ static void ensure_socket_initializatio
                        WSAGetLastError());
  
        for (name = libraries; *name; name++) {
 -              ipv6_dll = LoadLibrary(*name);
 +              ipv6_dll = LoadLibraryExA(*name, NULL,
 +                                        LOAD_LIBRARY_SEARCH_SYSTEM32);
                if (!ipv6_dll)
                        continue;
  
@@@ -1997,63 -1798,18 +1997,63 @@@ int mingw_getpagesize(void
        return si.dwAllocationGranularity;
  }
  
 +/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
 +enum EXTENDED_NAME_FORMAT {
 +      NameDisplay = 3,
 +      NameUserPrincipal = 8
 +};
 +
 +static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
 +{
 +      DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
 +              enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
 +      static wchar_t wbuffer[1024];
 +      DWORD len;
 +
 +      if (!INIT_PROC_ADDR(GetUserNameExW))
 +              return NULL;
 +
 +      len = ARRAY_SIZE(wbuffer);
 +      if (GetUserNameExW(type, wbuffer, &len)) {
 +              char *converted = xmalloc((len *= 3));
 +              if (xwcstoutf(converted, wbuffer, len) >= 0)
 +                      return converted;
 +              free(converted);
 +      }
 +
 +      return NULL;
 +}
 +
 +char *mingw_query_user_email(void)
 +{
 +      return get_extended_user_info(NameUserPrincipal);
 +}
 +
  struct passwd *getpwuid(int uid)
  {
 +      static unsigned initialized;
        static char user_name[100];
 -      static struct passwd p;
 +      static struct passwd *p;
 +      DWORD len;
  
 -      DWORD len = sizeof(user_name);
 -      if (!GetUserName(user_name, &len))
 +      if (initialized)
 +              return p;
 +
 +      len = sizeof(user_name);
 +      if (!GetUserName(user_name, &len)) {
 +              initialized = 1;
                return NULL;
 -      p.pw_name = user_name;
 -      p.pw_gecos = "unknown";
 -      p.pw_dir = NULL;
 -      return &p;
 +      }
 +
 +      p = xmalloc(sizeof(*p));
 +      p->pw_name = user_name;
 +      p->pw_gecos = get_extended_user_info(NameDisplay);
 +      if (!p->pw_gecos)
 +              p->pw_gecos = "unknown";
 +      p->pw_dir = NULL;
 +
 +      initialized = 1;
 +      return p;
  }
  
  static HANDLE timer_event;
@@@ -2211,12 -1967,24 +2211,12 @@@ int mingw_raise(int sig
  
  int link(const char *oldpath, const char *newpath)
  {
 -      typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
 -      static T create_hard_link = NULL;
        wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
        if (xutftowcs_path(woldpath, oldpath) < 0 ||
                xutftowcs_path(wnewpath, newpath) < 0)
                return -1;
  
 -      if (!create_hard_link) {
 -              create_hard_link = (T) GetProcAddress(
 -                      GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
 -              if (!create_hard_link)
 -                      create_hard_link = (T)-1;
 -      }
 -      if (create_hard_link == (T)-1) {
 -              errno = ENOSYS;
 -              return -1;
 -      }
 -      if (!create_hard_link(wnewpath, woldpath, NULL)) {
 +      if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
                errno = err_win_to_posix(GetLastError());
                return -1;
        }
@@@ -2275,33 -2043,6 +2275,6 @@@ pid_t waitpid(pid_t pid, int *status, i
        return -1;
  }
  
- int mingw_skip_dos_drive_prefix(char **path)
- {
-       int ret = has_dos_drive_prefix(*path);
-       *path += ret;
-       return ret;
- }
- int mingw_offset_1st_component(const char *path)
- {
-       char *pos = (char *)path;
-       /* unc paths */
-       if (!skip_dos_drive_prefix(&pos) &&
-                       is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
-               /* skip server name */
-               pos = strpbrk(pos + 2, "\\/");
-               if (!pos)
-                       return 0; /* Error: malformed unc path */
-               do {
-                       pos++;
-               } while (*pos && !is_dir_sep(*pos));
-       }
-       return pos + is_dir_sep(*pos) - path;
- }
  int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
  {
        int upos = 0, wpos = 0;
@@@ -2523,6 -2264,17 +2496,6 @@@ void mingw_startup(void
        maxlen = wcslen(wargv[0]);
        for (i = 1; i < argc; i++)
                maxlen = max(maxlen, wcslen(wargv[i]));
 -      for (i = 0; wenv[i]; i++)
 -              maxlen = max(maxlen, wcslen(wenv[i]));
 -
 -      /*
 -       * nedmalloc can't free CRT memory, allocate resizable environment
 -       * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot
 -       * use it while initializing the environment itself.
 -       */
 -      environ_size = i + 1;
 -      environ_alloc = alloc_nr(environ_size * sizeof(char*));
 -      environ = malloc_startup(environ_alloc);
  
        /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
        maxlen = 3 * maxlen + 1;
        /* convert command line arguments and environment to UTF-8 */
        for (i = 0; i < argc; i++)
                __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
 -      for (i = 0; wenv[i]; i++)
 -              environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen);
 -      environ[i] = NULL;
        free(buffer);
  
 -      /* sort environment for O(log n) getenv / putenv */
 -      qsort(environ, i, sizeof(char*), compareenv);
 -
        /* fix Windows specific environment settings */
        setup_windows_environment();
  
 +      unset_environment_variables = xstrdup("PERL5LIB");
 +
        /* initialize critical section for waitpid pinfo_t list */
        InitializeCriticalSection(&pinfo_cs);
  
diff --combined compat/mingw.h
index 8c24ddaa3efc20e4454ebc87c51fa30316f64a22,3387c3faeb34cf053f5ff5d80e4956949690de98..30d9fb3e36274657e5d2a63ef2f5eb3e1c55ce61
@@@ -11,9 -11,6 +11,9 @@@ typedef _sigset_t sigset_t
  #undef _POSIX_THREAD_SAFE_FUNCTIONS
  #endif
  
 +extern int mingw_core_config(const char *var, const char *value, void *cb);
 +#define platform_core_config mingw_core_config
 +
  /*
   * things that are not available in header files
   */
@@@ -260,35 -257,11 +260,35 @@@ char *mingw_mktemp(char *template)
  char *mingw_getcwd(char *pointer, int len);
  #define getcwd mingw_getcwd
  
 +#ifdef NO_UNSETENV
 +#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
 +#endif
 +
 +/*
 + * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
 + * These talk to the CRT using UNICODE/wchar_t, but maintain the original
 + * narrow-char API.
 + *
 + * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
 + * routines and stores both versions of each environment variable in parallel
 + * (and secretly updates both when you set one or the other), but it uses CP_ACP
 + * to do the conversion rather than CP_UTF8.
 + *
 + * Since everything in the git code base is UTF8, we define the mingw_ routines
 + * to access the CRT using the UNICODE routines and manually convert them to
 + * UTF8.  This also avoids round-trip problems.
 + *
 + * This also helps with our linkage, since "_wenviron" is publicly exported
 + * from the CRT.  But to access "_environ" we would have to statically link
 + * to the CRT (/MT).
 + *
 + * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
 + */
 +#define getenv       mingw_getenv
 +#define putenv       mingw_putenv
 +#define unsetenv     mingw_putenv
  char *mingw_getenv(const char *name);
 -#define getenv mingw_getenv
 -int mingw_putenv(const char *namevalue);
 -#define putenv mingw_putenv
 -#define unsetenv mingw_putenv
 +int   mingw_putenv(const char *name);
  
  int mingw_gethostname(char *host, int namelen);
  #define gethostname mingw_gethostname
@@@ -354,41 -327,18 +354,41 @@@ static inline int getrlimit(int resourc
  }
  
  /*
 - * Use mingw specific stat()/lstat()/fstat() implementations on Windows.
 + * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
 + * including our own struct stat with 64 bit st_size and nanosecond-precision
 + * file times.
   */
  #ifndef __MINGW64_VERSION_MAJOR
  #define off_t off64_t
  #define lseek _lseeki64
 +struct timespec {
 +      time_t tv_sec;
 +      long tv_nsec;
 +};
  #endif
  
 -/* use struct stat with 64 bit st_size */
 +struct mingw_stat {
 +    _dev_t st_dev;
 +    _ino_t st_ino;
 +    _mode_t st_mode;
 +    short st_nlink;
 +    short st_uid;
 +    short st_gid;
 +    _dev_t st_rdev;
 +    off64_t st_size;
 +    struct timespec st_atim;
 +    struct timespec st_mtim;
 +    struct timespec st_ctim;
 +};
 +
 +#define st_atime st_atim.tv_sec
 +#define st_mtime st_mtim.tv_sec
 +#define st_ctime st_ctim.tv_sec
 +
  #ifdef stat
  #undef stat
  #endif
 -#define stat _stati64
 +#define stat mingw_stat
  int mingw_lstat(const char *file_name, struct stat *buf);
  int mingw_stat(const char *file_name, struct stat *buf);
  int mingw_fstat(int fd, struct stat *buf);
  #endif
  #define lstat mingw_lstat
  
 -#ifndef _stati64
 -# define _stati64(x,y) mingw_stat(x,y)
 -#elif defined (_USE_32BIT_TIME_T)
 -# define _stat32i64(x,y) mingw_stat(x,y)
 -#else
 -# define _stat64(x,y) mingw_stat(x,y)
 -#endif
  
  int mingw_utime(const char *file_name, const struct utimbuf *times);
  #define utime mingw_utime
@@@ -433,9 -390,6 +433,9 @@@ int mingw_raise(int sig)
  int winansi_isatty(int fd);
  #define isatty winansi_isatty
  
 +int winansi_dup2(int oldfd, int newfd);
 +#define dup2 winansi_dup2
 +
  void winansi_init(void);
  HANDLE winansi_get_osfhandle(int fd);
  
   * git specific compatibility
   */
  
- #define has_dos_drive_prefix(path) \
-       (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
- int mingw_skip_dos_drive_prefix(char **path);
- #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
- static inline int mingw_is_dir_sep(int c)
- {
-       return c == '/' || c == '\\';
- }
- #define is_dir_sep mingw_is_dir_sep
- static inline char *mingw_find_last_dir_sep(const char *path)
- {
-       char *ret = NULL;
-       for (; *path; ++path)
-               if (is_dir_sep(*path))
-                       ret = (char *)path;
-       return ret;
- }
  static inline void convert_slashes(char *path)
  {
        for (; *path; path++)
                if (*path == '\\')
                        *path = '/';
  }
- #define find_last_dir_sep mingw_find_last_dir_sep
- int mingw_offset_1st_component(const char *path);
- #define offset_1st_component mingw_offset_1st_component
  #define PATH_SEP ';'
 +extern char *mingw_query_user_email(void);
 +#define query_user_email mingw_query_user_email
  #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
  #define PRIuMAX "I64u"
  #define PRId64 "I64d"
diff --combined config.mak.uname
index 378ca0a582b642f3b32b840788d62a047baf6525,d63f41d6964a95416de344efe22f442d79af7d84..e3914310418dac0ac5f6401ecc51fd848f068f5a
@@@ -187,7 -187,7 +187,7 @@@ ifeq ($(uname_O),Cygwin
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
-       COMPAT_OBJS += compat/cygwin.o
+       COMPAT_OBJS += compat/win32/path-utils.o
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
  endif
  ifeq ($(uname_S),FreeBSD)
@@@ -233,7 -233,6 +233,7 @@@ ifeq ($(uname_S),OpenBSD
        HAVE_BSD_SYSCTL = YesPlease
        HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
        PROCFS_EXECUTABLE_PATH = /proc/curproc/file
 +      FREAD_READS_DIRECTORIES = UnfortunatelyYes
  endif
  ifeq ($(uname_S),MirBSD)
        NO_STRCASESTR = YesPlease
@@@ -371,6 -370,7 +371,6 @@@ ifeq ($(uname_S),Windows
        RUNTIME_PREFIX = YesPlease
        HAVE_WPGMPTR = YesWeDo
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 -      NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        # USE_NED_ALLOCATOR = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
        ETAGS_TARGET = ETAGS
 -      NO_INET_PTON = YesPlease
 -      NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        NATIVE_CRLF = YesPlease
        DEFAULT_HELP_FORMAT = html
@@@ -431,6 -433,8 +431,6 @@@ ifeq ($(uname_S),Minix
        NO_NSEC = YesPlease
        NEEDS_LIBGEN =
        NEEDS_CRYPTO_WITH_SSL = YesPlease
 -      NEEDS_IDN_WITH_CURL = YesPlease
 -      NEEDS_SSL_WITH_CURL = YesPlease
        NEEDS_RESOLV =
        NO_HSTRERROR = YesPlease
        NO_MMAP = YesPlease
@@@ -456,6 -460,7 +456,6 @@@ ifeq ($(uname_S),NONSTOP_KERNEL
        # Missdetected, hence commented out, see below.
        #NO_CURL = YesPlease
        # Added manually, see above.
 -      NEEDS_SSL_WITH_CURL = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_LIBICONV = YesPlease
@@@ -515,6 -520,7 +515,6 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        RUNTIME_PREFIX = YesPlease
        HAVE_WPGMPTR = YesWeDo
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 -      NO_NSEC = YesPlease
        USE_WIN32_MMAP = YesPlease
        MMAP_PREVENTS_DELETE = UnfortunatelyYes
        USE_NED_ALLOCATOR = YesPlease
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        ETAGS_TARGET = ETAGS
 -      NO_INET_PTON = YesPlease
 -      NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
        DEFAULT_HELP_FORMAT = html
        COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+               compat/win32/path-utils.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
        BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
diff --combined git-compat-util.h
index 09b0102cae8c8c0e39dc239003ca599a896730cf,925a8ae72b265ecbf8537ca5a2628490eff7f375..5702556c89689e5ff72f8373802eaa154e8525fc
  #define _SGI_SOURCE 1
  
  #if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
 -# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
 -#  define _WIN32_WINNT 0x0502
 +# if !defined(_WIN32_WINNT)
 +#  define _WIN32_WINNT 0x0600
  # endif
  #define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
  #include <winsock2.h>
  #include <regex.h>
  #include <utime.h>
  #include <syslog.h>
 -#ifndef NO_SYS_POLL_H
 +#if !defined(NO_POLL_H)
 +#include <poll.h>
 +#elif !defined(NO_SYS_POLL_H)
  #include <sys/poll.h>
  #else
 +/* Pull the compat stuff */
  #include <poll.h>
  #endif
  #ifdef HAVE_BSD_SYSCTL
  #endif
  
  #if defined(__CYGWIN__)
- #include "compat/cygwin.h"
+ #include "compat/win32/path-utils.h"
  #endif
  #if defined(__MINGW32__)
  /* pull in Windows compatibility stuff */
+ #include "compat/win32/path-utils.h"
  #include "compat/mingw.h"
  #elif defined(_MSC_VER)
  #include "compat/msvc.h"
@@@ -345,14 -343,6 +346,14 @@@ typedef uintmax_t timestamp_t
  #define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
  #endif
  
 +#ifndef platform_core_config
 +static inline int noop_core_config(const char *var, const char *value, void *cb)
 +{
 +      return 0;
 +}
 +#define platform_core_config noop_core_config
 +#endif
 +
  #ifndef has_dos_drive_prefix
  static inline int git_has_dos_drive_prefix(const char *path)
  {
@@@ -393,10 -383,6 +394,10 @@@ static inline char *git_find_last_dir_s
  #define find_last_dir_sep git_find_last_dir_sep
  #endif
  
 +#ifndef query_user_email
 +#define query_user_email() NULL
 +#endif
 +
  #if defined(__HP_cc) && (__HP_cc >= 61000)
  #define NORETURN __attribute__((noreturn))
  #define NORETURN_PTR
  #define LAST_ARG_MUST_BE_NULL
  #endif
  
 +#define MAYBE_UNUSED __attribute__((__unused__))
 +
  #include "compat/bswap.h"
  
  #include "wildmatch.h"
@@@ -861,7 -845,6 +862,7 @@@ extern FILE *fopen_or_warn(const char *
  #define FREE_AND_NULL(p) do { free(p); (p) = NULL; } while (0)
  
  #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
 +#define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x)));
  #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
  
  #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
diff --combined t/t5601-clone.sh
index 8bbc7068acbd1eab9f0499ff1151abd58a87079c,f83a637194e977a6db358476887db28c040d5e05..d6948cbdab03cf827d86511ade26366e89fda149
@@@ -487,7 -487,7 +487,7 @@@ test_clone_url () 
        expect_ssh "$@"
  }
  
- test_expect_success !MINGW 'clone c:temp is ssl' '
+ test_expect_success !MINGW,!CYGWIN 'clone c:temp is ssl' '
        test_clone_url c:temp c temp
  '
  
@@@ -624,16 -624,10 +624,16 @@@ test_expect_success 'clone on case-inse
                        git hash-object -w -t tree --stdin) &&
                c=$(git commit-tree -m bogus $t) &&
                git update-ref refs/heads/bogus $c &&
 -              git clone -b bogus . bogus
 +              git clone -b bogus . bogus 2>warning
        )
  '
  
 +test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
 +      grep X icasefs/warning &&
 +      grep x icasefs/warning &&
 +      test_i18ngrep "the following paths have collided" icasefs/warning
 +'
 +
  partial_clone () {
               SERVER="$1" &&
               URL="$2" &&