diff --git a/apply.c b/apply.c index a2ceb3fb40d3b5..de5750354ad2f8 100644 --- a/apply.c +++ b/apply.c @@ -3779,7 +3779,7 @@ static int check_preimage(struct apply_state *state, if (*ce && !(*ce)->ce_mode) BUG("ce_mode == 0 for path '%s'", old_name); - if (trust_executable_bit) + if (trust_executable_bit || !S_ISREG(st->st_mode)) st_mode = ce_mode_from_stat(*ce, st->st_mode); else if (*ce) st_mode = (*ce)->ce_mode; diff --git a/compat/mingw.c b/compat/mingw.c index 90ba5cea9d3ace..7215b127cc8f88 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -629,6 +629,7 @@ int mingw_open (const char *filename, int oflags, ...) int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL); wchar_t wfilename[MAX_PATH]; open_fn_t open_fn; + WIN32_FILE_ATTRIBUTE_DATA fdata; DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, RtlGetLastNtStatus, void); @@ -653,6 +654,19 @@ int mingw_open (const char *filename, int oflags, ...) else if (xutftowcs_path(wfilename, filename) < 0) return -1; + /* + * When `symlink` exists and is a symbolic link pointing to a + * non-existing file, `_wopen(symlink, O_CREAT | O_EXCL)` would + * create that file. Not what we want: Linux would say `EEXIST` + * in that instance, which is therefore what Git expects. + */ + if (create && + GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata) && + (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + errno = EEXIST; + return -1; + } + fd = open_fn(wfilename, oflags, mode); /* @@ -1237,18 +1251,16 @@ char *mingw_getcwd(char *pointer, int len) { wchar_t cwd[MAX_PATH], wpointer[MAX_PATH]; DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd); + HANDLE hnd; 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; + 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) { ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0); CloseHandle(hnd); if (!ret || ret >= ARRAY_SIZE(wpointer)) @@ -1257,13 +1269,11 @@ char *mingw_getcwd(char *pointer, int len) return NULL; return pointer; } - if (!ret || ret >= ARRAY_SIZE(wpointer)) - return NULL; - if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) { + if (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) { errno = ENOENT; return NULL; } - if (xwcstoutf(pointer, wpointer, len) < 0) + if (xwcstoutf(pointer, cwd, len) < 0) return NULL; convert_slashes(pointer); return pointer; diff --git a/environment.c b/environment.c index a770b5921d9546..b65b85a01f18cf 100644 --- a/environment.c +++ b/environment.c @@ -324,8 +324,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char * return (current & ~negative) | positive; } -static int git_default_core_config(const char *var, const char *value, - const struct config_context *ctx, void *cb) +int git_default_core_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) { /* This needs a better name */ if (!strcmp(var, "core.filemode")) { diff --git a/environment.h b/environment.h index 51898c99cd1e45..e61f843fdbb637 100644 --- a/environment.h +++ b/environment.h @@ -106,6 +106,8 @@ const char *strip_namespace(const char *namespaced_ref); int git_default_config(const char *, const char *, const struct config_context *, void *); +int git_default_core_config(const char *var, const char *value, + const struct config_context *ctx, void *cb); /* * TODO: All the below state either explicitly or implicitly relies on diff --git a/lockfile.c b/lockfile.c index 1d5ed016828746..67082a9caaeb18 100644 --- a/lockfile.c +++ b/lockfile.c @@ -19,14 +19,14 @@ static void trim_last_path_component(struct strbuf *path) int i = path->len; /* back up past trailing slashes, if any */ - while (i && path->buf[i - 1] == '/') + while (i && is_dir_sep(path->buf[i - 1])) i--; /* * then go backwards until a slash, or the beginning of the * string */ - while (i && path->buf[i - 1] != '/') + while (i && !is_dir_sep(path->buf[i - 1])) i--; strbuf_setlen(path, i); diff --git a/setup.c b/setup.c index 7086741e6c2d1f..42e4e7a690ec3c 100644 --- a/setup.c +++ b/setup.c @@ -2611,7 +2611,7 @@ int init_db(const char *git_dir, const char *real_git_dir, * have set up the repository format such that we can evaluate * includeIf conditions correctly in the case of re-initialization. */ - repo_config(the_repository, platform_core_config, NULL); + repo_config(the_repository, git_default_core_config, NULL); safe_create_dir(the_repository, git_dir, 0); diff --git a/strbuf.c b/strbuf.c index 6c3851a7f84d72..fa4e30f112bb09 100644 --- a/strbuf.c +++ b/strbuf.c @@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f) return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0; } -#define STRBUF_MAXLINK (2*PATH_MAX) - int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) { size_t oldalloc = sb->alloc; @@ -575,15 +573,15 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) if (hint < 32) hint = 32; - while (hint < STRBUF_MAXLINK) { + for (;;) { ssize_t len; - strbuf_grow(sb, hint); - len = readlink(path, sb->buf, hint); + strbuf_grow(sb, hint + 1); + len = readlink(path, sb->buf, hint + 1); if (len < 0) { if (errno != ERANGE) break; - } else if (len < hint) { + } else if (len <= hint) { strbuf_setlen(sb, len); return 0; } diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 618da080dc9ea9..e4d32bb4d259f6 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -425,7 +425,11 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' ' git init --separate-git-dir ../realgitdir ) && echo "gitdir: $(pwd)/realgitdir" >expected && - test_cmp expected newdir/.git && + case "$GIT_TEST_CMP" in + # `git diff --no-index` does not resolve symlinks + *--no-index*) cmp expected newdir/.git;; + *) test_cmp expected newdir/.git;; + esac && test_cmp expected newdir/here && test_path_is_dir realgitdir/refs ' diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index dc30289f7539ee..6f7cfd9e33f633 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -123,7 +123,8 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to rmdir \"\$HOME/dir/\" && rm \"\$HOME/.git-credential-cache\" " && - mkdir -p -m 700 "$HOME/dir/" && + mkdir -p "$HOME/dir/" && + chmod 700 "$HOME/dir/" && ln -s "$HOME/dir" "$HOME/.git-credential-cache" && check approve cache <<-\EOF && protocol=https diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh index b11126ed478129..74bfa2e9ba060d 100755 --- a/t/t0600-reffiles-backend.sh +++ b/t/t0600-reffiles-backend.sh @@ -467,7 +467,7 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' ' esac ' -test_expect_success SYMLINKS 'symref transaction supports symlinks' ' +test_expect_success SYMLINKS,!MINGW 'symref transaction supports symlinks' ' test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" && git update-ref refs/heads/new @ && test_config core.prefersymlinkrefs true && diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 1f61b666a7d382..0eee3bb8781b30 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -1048,18 +1048,28 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for out- echo .. >>expect && echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual && test_cmp expect actual && - echo symlink 3 >expect && - echo ../ >>expect && + if test_have_prereq MINGW,SYMLINKS + then + test_write_lines "symlink 2" .. + else + test_write_lines "symlink 3" ../ + fi >expect && echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual && test_cmp expect actual ' test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' ' - echo HEAD: | git cat-file --batch-check >expect && - echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual && - test_cmp expect actual && - echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual && - test_cmp expect actual && + if test_have_prereq !MINGW + then + # The `up-down` and `up-down-trailing` symlinks are normalized + # in MSYS in `winsymlinks` mode and are therefore in a + # different shape than Git expects them. + echo HEAD: | git cat-file --batch-check >expect && + echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual && + test_cmp expect actual && + echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual && + test_cmp expect actual + fi && echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual && test_cmp found actual && echo symlink 7 >expect && diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index 8ff2b0c232b485..6e51f892f320bb 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -286,7 +286,7 @@ test_expect_success SYMLINKS 'conditional include, relative path with symlinks' ) ' -test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' ' +test_expect_success SYMLINKS,!MINGW 'conditional include, gitdir matching symlink' ' ln -s foo bar && ( cd bar && @@ -298,7 +298,7 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' ' ) ' -test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icase' ' +test_expect_success SYMLINKS,!MINGW 'conditional include, gitdir matching symlink, icase' ' ( cd bar && echo "[includeIf \"gitdir/i:BAR/\"]path=bar8" >>.git/config && diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh index 533ac85dc83409..53535a8ebfc393 100755 --- a/t/t6423-merge-rename-directories.sh +++ b/t/t6423-merge-rename-directories.sh @@ -5158,13 +5158,18 @@ test_setup_12m () { git switch B && git rm dir/subdir/file && mkdir dir && - ln -s /dev/null dir/subdir && + if test_have_prereq MINGW + then + cmd //c 'mklink dir\subdir NUL' + else + ln -s /dev/null dir/subdir + fi && git add . && git commit -m "B" ) } -test_expect_success '12m: Change parent of renamed-dir to symlink on other side' ' +test_expect_success SYMLINKS '12m: Change parent of renamed-dir to symlink on other side' ' test_setup_12m && ( cd 12m && diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 9b74db55634b16..bf0f67378dbb23 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -752,11 +752,11 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' c EOF git difftool --symlinks --dir-diff --extcmd ls >output && - grep -v ^/ output >actual && + grep -v ":\$" output >actual && test_cmp expect actual && git difftool --no-symlinks --dir-diff --extcmd ls >output && - grep -v ^/ output >actual && + grep -v ":\$" output >actual && test_cmp expect actual && # The left side contains symlink "c" that points to "b" @@ -786,11 +786,11 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' EOF git difftool --symlinks --dir-diff --extcmd ls >output && - grep -v ^/ output >actual && + grep -v ":\$" output >actual && test_cmp expect actual && git difftool --no-symlinks --dir-diff --extcmd ls >output && - grep -v ^/ output >actual && + grep -v ":\$" output >actual && test_cmp expect actual ' diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 58a9b328d558f3..570b0c5680fc73 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -117,7 +117,12 @@ sub adjust_dirsep { unlink $tmpfile; # paths -is($r->repo_path, $abs_repo_dir . "/.git", "repo_path"); +my $abs_git_dir = $abs_repo_dir . "/.git"; +if ($^O eq 'msys' or $^O eq 'cygwin') { + $abs_git_dir = `cygpath -am "$abs_repo_dir/.git"`; + $abs_git_dir =~ s/\r?\n?$//; +} +is($r->repo_path, $abs_git_dir, "repo_path"); is($r->wc_path, $abs_repo_dir . "/", "wc_path"); is($r->wc_subdir, "", "wc_subdir initial"); $r->wc_chdir("directory1"); @@ -127,7 +132,7 @@ sub adjust_dirsep { # Object generation in sub directory chdir("directory2"); my $r2 = Git->repository(); -is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)"); +is($r2->repo_path, $abs_git_dir, "repo_path (2)"); is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)"); is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");