Skip to content

Commit

Permalink
Merge pull request #1897 from piscisaureus/symlink-attr
Browse files Browse the repository at this point in the history
Specify symlink type in .gitattributes
  • Loading branch information
dscho committed May 19, 2019
2 parents 71e8bb7 + 0ab67ae commit 6126a54
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 42 deletions.
91 changes: 49 additions & 42 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,54 @@ static void process_phantom_symlinks(void)
LeaveCriticalSection(&phantom_symlinks_cs);
}

static int create_phantom_symlink(wchar_t *wtarget, wchar_t *wlink)
{
int len;

/* create file symlink */
if (!CreateSymbolicLinkW(wlink, wtarget, symlink_file_flags)) {
errno = err_win_to_posix(GetLastError());
return -1;
}

/* convert to directory symlink if target exists */
switch (process_phantom_symlink(wtarget, wlink)) {
case PHANTOM_SYMLINK_RETRY: {
/* if target doesn't exist, add to phantom symlinks list */
wchar_t wfullpath[MAX_LONG_PATH];
struct phantom_symlink_info *psi;

/* convert to absolute path to be independent of cwd */
len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
if (!len || len >= MAX_LONG_PATH) {
errno = err_win_to_posix(GetLastError());
return -1;
}

/* over-allocate and fill phantom_symlink_info structure */
psi = xmalloc(sizeof(struct phantom_symlink_info) +
sizeof(wchar_t) * (len + wcslen(wtarget) + 2));
psi->wlink = (wchar_t *)(psi + 1);
wcscpy(psi->wlink, wfullpath);
psi->wtarget = psi->wlink + len + 1;
wcscpy(psi->wtarget, wtarget);

EnterCriticalSection(&phantom_symlinks_cs);
psi->next = phantom_symlinks;
phantom_symlinks = psi;
LeaveCriticalSection(&phantom_symlinks_cs);
break;
}
case PHANTOM_SYMLINK_DIRECTORY:
/* if we created a dir symlink, process other phantom symlinks */
process_phantom_symlinks();
break;
default:
break;
}
return 0;
}

/* Normalizes NT paths as returned by some low-level APIs. */
static wchar_t *normalize_ntpath(wchar_t *wbuf)
{
Expand Down Expand Up @@ -2376,48 +2424,7 @@ int symlink(const char *target, const char *link)
if (wtarget[len] == '/')
wtarget[len] = '\\';

/* create file symlink */
if (!CreateSymbolicLinkW(wlink, wtarget, symlink_file_flags)) {
errno = err_win_to_posix(GetLastError());
return -1;
}

/* convert to directory symlink if target exists */
switch (process_phantom_symlink(wtarget, wlink)) {
case PHANTOM_SYMLINK_RETRY: {
/* if target doesn't exist, add to phantom symlinks list */
wchar_t wfullpath[MAX_LONG_PATH];
struct phantom_symlink_info *psi;

/* convert to absolute path to be independent of cwd */
len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
if (!len || len >= MAX_LONG_PATH) {
errno = err_win_to_posix(GetLastError());
return -1;
}

/* over-allocate and fill phantom_symlink_info structure */
psi = xmalloc(sizeof(struct phantom_symlink_info)
+ sizeof(wchar_t) * (len + wcslen(wtarget) + 2));
psi->wlink = (wchar_t *)(psi + 1);
wcscpy(psi->wlink, wfullpath);
psi->wtarget = psi->wlink + len + 1;
wcscpy(psi->wtarget, wtarget);

EnterCriticalSection(&phantom_symlinks_cs);
psi->next = phantom_symlinks;
phantom_symlinks = psi;
LeaveCriticalSection(&phantom_symlinks_cs);
break;
}
case PHANTOM_SYMLINK_DIRECTORY:
/* if we created a dir symlink, process other phantom symlinks */
process_phantom_symlinks();
break;
default:
break;
}
return 0;
return create_phantom_symlink(wtarget, wlink);
}

#ifndef _WINNT_H
Expand Down
51 changes: 51 additions & 0 deletions t/t2040-checkout-symlink-attr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

test_description='checkout symlinks with `symlink` attribute on Windows
Ensures that Git for Windows creates symlinks of the right type,
as specified by the `symlink` attribute in `.gitattributes`.'

# Tell MSYS to create native symlinks. Without this flag test-lib's
# prerequisite detection for SYMLINKS doesn't detect the right thing.
MSYS=winsymlinks:nativestrict && export MSYS

. ./test-lib.sh

if ! test_have_prereq MINGW,SYMLINKS
then
skip_all='skipping $0: MinGW-only test, which requires symlink support.'
test_done
fi

# Adds a symlink to the index without clobbering the work tree.
cache_symlink () {
sha=$(printf '%s' "$1" | git hash-object --stdin -w) &&
git update-index --add --cacheinfo 120000,$sha,"$2"
}

# MSYS2 is very forgiving, it will resolve symlinks even if the
# symlink type isn't correct. To make this test meaningful, try
# them with a native, non-MSYS executable.
cat_native () {
filename=$(cygpath -w "$1") &&
cmd.exe /c "type \"$filename\""
}

test_expect_success 'checkout symlinks with attr' '
cache_symlink file1 file-link &&
cache_symlink dir dir-link &&
printf "file-link symlink=file\ndir-link symlink=dir\n" >.gitattributes &&
git add .gitattributes &&
git checkout . &&
mkdir dir &&
echo "contents1" >file1 &&
echo "contents2" >dir/file2 &&
test "$(cat_native file-link)" = "contents1" &&
test "$(cat_native dir-link/file2)" = "contents2"
'

test_done

0 comments on commit 6126a54

Please sign in to comment.