Skip to content

Commit f88da9f

Browse files
committed
Merge branch 'prepare-the-test-suite-for-symlink-support-on-windows'
Git for Windows has supported symbolic links for quite some time: In git-for-windows#156, this support was introduced already into Git for Windows v2.4.2.windows.1 in May 2015. However, the Git for Windows CI never ran the test suite with symbolic link support because the MSYS2 runtime (i.e. the POSIX emulation layer required to run Git's test suite because the latter is written in Unix shell script) does not support symbolic links right out of the box. This is for historical reasons: Symbolic link support was introduced in Windows 7, but these links could only be created by administrators by default, and it took until Windows 10 Build 14972 that at least in Developer Mode, non-administrators would be permitted to create them. The MSYS2 runtime _does_ have some sort of support for symbolic links, although with caveats: seeing as it expects the inputs as Unix-like paths, but the outputs need to be Win32 symbolic links pointing to Win32 paths, some normalization has to be performed in the process. This leads to sometimes surprising behavior e.g. when a link target like `a/b/..` is normalized to `a`. It has been a minute or three since the time when Windows versions without symbolic link support were common, therefore there are plans to turn on that support in the MSYS2 runtime on these Windows versions by default, see msys2/msys2-runtime#114 for more details about this. To prepare for this, I am working toward upstreaming Git for Windows' own support for symbolic links. And to prepare for that, in turn, I am hereby contributing preemptively the fixes required to eventually let Git's test suite pass when both MSYS2 runtime and Git support symbolic links. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 2912d8e + 4889158 commit f88da9f

File tree

10 files changed

+58
-20
lines changed

10 files changed

+58
-20
lines changed

apply.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3779,7 +3779,7 @@ static int check_preimage(struct apply_state *state,
37793779
if (*ce && !(*ce)->ce_mode)
37803780
BUG("ce_mode == 0 for path '%s'", old_name);
37813781

3782-
if (trust_executable_bit)
3782+
if (trust_executable_bit || !S_ISREG(st->st_mode))
37833783
st_mode = ce_mode_from_stat(*ce, st->st_mode);
37843784
else if (*ce)
37853785
st_mode = (*ce)->ce_mode;

compat/mingw.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ int mingw_open (const char *filename, int oflags, ...)
858858
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
859859
wchar_t wfilename[MAX_LONG_PATH];
860860
open_fn_t open_fn;
861+
WIN32_FILE_ATTRIBUTE_DATA fdata;
861862

862863
DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, RtlGetLastNtStatus, void);
863864

@@ -891,6 +892,19 @@ int mingw_open (const char *filename, int oflags, ...)
891892
else if (xutftowcs_long_path(wfilename, filename) < 0)
892893
return -1;
893894

895+
/*
896+
* When `symlink` exists and is a symbolic link pointing to a
897+
* non-existing file, `_wopen(symlink, O_CREAT | O_EXCL)` would
898+
* create that file. Not what we want: Linux would say `EEXIST`
899+
* in that instance, which is therefore what Git expects.
900+
*/
901+
if (create &&
902+
GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata) &&
903+
(fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
904+
errno = EEXIST;
905+
return -1;
906+
}
907+
894908
fd = open_fn(wfilename, oflags, mode);
895909

896910
/*

t/t0001-init.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,10 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
425425
git init --separate-git-dir ../realgitdir
426426
) &&
427427
echo "gitdir: $(pwd)/realgitdir" >expected &&
428-
test_cmp expected newdir/.git &&
428+
case "$GIT_TEST_CMP" in
429+
*--no-index*) ;; # git diff --no-index does not resolve symlinks
430+
*) test_cmp expected newdir/.git;;
431+
esac &&
429432
test_cmp expected newdir/here &&
430433
test_path_is_dir realgitdir/refs
431434
'

t/t0301-credential-cache.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to
123123
rmdir \"\$HOME/dir/\" &&
124124
rm \"\$HOME/.git-credential-cache\"
125125
" &&
126-
mkdir -p -m 700 "$HOME/dir/" &&
126+
mkdir -p "$HOME/dir/" &&
127+
chmod 700 "$HOME/dir/" &&
127128
ln -s "$HOME/dir" "$HOME/.git-credential-cache" &&
128129
check approve cache <<-\EOF &&
129130
protocol=https

t/t0600-reffiles-backend.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
467467
esac
468468
'
469469

470-
test_expect_success SYMLINKS 'symref transaction supports symlinks' '
470+
test_expect_success SYMLINKS,!MINGW 'symref transaction supports symlinks' '
471471
test_when_finished "git symbolic-ref -d TEST_SYMREF_HEAD" &&
472472
git update-ref refs/heads/new @ &&
473473
test_config core.prefersymlinkrefs true &&

t/t1006-cat-file.sh

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,18 +1048,28 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-
10481048
echo .. >>expect &&
10491049
echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
10501050
test_cmp expect actual &&
1051-
echo symlink 3 >expect &&
1052-
echo ../ >>expect &&
1051+
if test_have_prereq MINGW,SYMLINKS
1052+
then
1053+
test_write_lines "symlink 2" ..
1054+
else
1055+
test_write_lines "symlink 3" ../
1056+
fi >expect &&
10531057
echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
10541058
test_cmp expect actual
10551059
'
10561060

10571061
test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
1058-
echo HEAD: | git cat-file --batch-check >expect &&
1059-
echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
1060-
test_cmp expect actual &&
1061-
echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
1062-
test_cmp expect actual &&
1062+
if test_have_prereq !MINGW
1063+
then
1064+
# The `up-down` and `up-down-trailing` symlinks are normalized
1065+
# in MSYS in `winsymlinks` mode and are therefore in a
1066+
# different shape than Git expects them.
1067+
echo HEAD: | git cat-file --batch-check >expect &&
1068+
echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
1069+
test_cmp expect actual &&
1070+
echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
1071+
test_cmp expect actual
1072+
fi &&
10631073
echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
10641074
test_cmp found actual &&
10651075
echo symlink 7 >expect &&

t/t1305-config-include.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ test_expect_success SYMLINKS 'conditional include, relative path with symlinks'
286286
)
287287
'
288288

289-
test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' '
289+
test_expect_success SYMLINKS,!MINGW 'conditional include, gitdir matching symlink' '
290290
ln -s foo bar &&
291291
(
292292
cd bar &&
@@ -298,7 +298,7 @@ test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' '
298298
)
299299
'
300300

301-
test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icase' '
301+
test_expect_success SYMLINKS,!MINGW 'conditional include, gitdir matching symlink, icase' '
302302
(
303303
cd bar &&
304304
echo "[includeIf \"gitdir/i:BAR/\"]path=bar8" >>.git/config &&

t/t6423-merge-rename-directories.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5158,7 +5158,12 @@ test_setup_12m () {
51585158
git switch B &&
51595159
git rm dir/subdir/file &&
51605160
mkdir dir &&
5161-
ln -s /dev/null dir/subdir &&
5161+
if test_have_prereq MINGW
5162+
then
5163+
cmd //c 'mklink dir\subdir NUL'
5164+
else
5165+
ln -s /dev/null dir/subdir
5166+
fi &&
51625167
git add . &&
51635168
git commit -m "B"
51645169
)

t/t7800-difftool.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -752,11 +752,11 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
752752
c
753753
EOF
754754
git difftool --symlinks --dir-diff --extcmd ls >output &&
755-
grep -v ^/ output >actual &&
755+
grep -v ":\$" output >actual &&
756756
test_cmp expect actual &&
757757
758758
git difftool --no-symlinks --dir-diff --extcmd ls >output &&
759-
grep -v ^/ output >actual &&
759+
grep -v ":\$" output >actual &&
760760
test_cmp expect actual &&
761761
762762
# The left side contains symlink "c" that points to "b"
@@ -786,11 +786,11 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
786786
787787
EOF
788788
git difftool --symlinks --dir-diff --extcmd ls >output &&
789-
grep -v ^/ output >actual &&
789+
grep -v ":\$" output >actual &&
790790
test_cmp expect actual &&
791791
792792
git difftool --no-symlinks --dir-diff --extcmd ls >output &&
793-
grep -v ^/ output >actual &&
793+
grep -v ":\$" output >actual &&
794794
test_cmp expect actual
795795
'
796796

t/t9700/test.pl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ sub adjust_dirsep {
117117
unlink $tmpfile;
118118

119119
# paths
120-
is($r->repo_path, $abs_repo_dir . "/.git", "repo_path");
120+
my $abs_git_dir = $abs_repo_dir . "/.git";
121+
if ($^O eq 'msys' or $^O eq 'cygwin') {
122+
$abs_git_dir = `cygpath -am "$abs_repo_dir/.git"`;
123+
$abs_git_dir =~ s/\r?\n?$//;
124+
}
125+
is($r->repo_path, $abs_git_dir, "repo_path");
121126
is($r->wc_path, $abs_repo_dir . "/", "wc_path");
122127
is($r->wc_subdir, "", "wc_subdir initial");
123128
$r->wc_chdir("directory1");
@@ -127,7 +132,7 @@ sub adjust_dirsep {
127132
# Object generation in sub directory
128133
chdir("directory2");
129134
my $r2 = Git->repository();
130-
is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)");
135+
is($r2->repo_path, $abs_git_dir, "repo_path (2)");
131136
is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)");
132137
is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");
133138

0 commit comments

Comments
 (0)