Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
7321cff0
Unverified
Commit
7321cff0
authored
Nov 15, 2018
by
Edward Thomson
Committed by
GitHub
Nov 15, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4713 from libgit2/ethomson/win_symlinks
Support symlinks on Windows when core.symlinks=true
parents
9189a66a
da500cc6
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
331 additions
and
198 deletions
+331
-198
src/CMakeLists.txt
+1
-1
src/repository.c
+50
-16
src/win32/posix_w32.c
+18
-36
tests/checkout/conflict.c
+56
-48
tests/checkout/icase.c
+17
-28
tests/checkout/index.c
+92
-45
tests/repo/init.c
+65
-24
tests/repo/repo_helpers.c
+30
-0
tests/repo/repo_helpers.h
+2
-0
No files found.
src/CMakeLists.txt
View file @
7321cff0
...
...
@@ -423,7 +423,7 @@ FILE(GLOB SRC_H
# On Windows use specific platform sources
IF (WIN32 AND NOT CYGWIN)
ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0
501
)
ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0
600
)
IF(MSVC)
SET(WIN_RC "
win32/git2.rc
")
...
...
src/repository.c
View file @
7321cff0
...
...
@@ -944,18 +944,20 @@ static int load_config(
git_buf
config_path
=
GIT_BUF_INIT
;
git_config
*
cfg
=
NULL
;
assert
(
repo
&&
out
);
assert
(
out
);
if
((
error
=
git_config_new
(
&
cfg
))
<
0
)
return
error
;
if
((
error
=
git_repository_item_path
(
&
config_path
,
repo
,
GIT_REPOSITORY_ITEM_CONFIG
))
==
0
)
error
=
git_config_add_file_ondisk
(
cfg
,
config_path
.
ptr
,
GIT_CONFIG_LEVEL_LOCAL
,
repo
,
0
);
if
(
repo
)
{
if
((
error
=
git_repository_item_path
(
&
config_path
,
repo
,
GIT_REPOSITORY_ITEM_CONFIG
))
==
0
)
error
=
git_config_add_file_ondisk
(
cfg
,
config_path
.
ptr
,
GIT_CONFIG_LEVEL_LOCAL
,
repo
,
0
);
if
(
error
&&
error
!=
GIT_ENOTFOUND
)
goto
on_error
;
if
(
error
&&
error
!=
GIT_ENOTFOUND
)
goto
on_error
;
git_buf_dispose
(
&
config_path
);
git_buf_dispose
(
&
config_path
);
}
if
(
global_config_path
!=
NULL
&&
(
error
=
git_config_add_file_ondisk
(
...
...
@@ -1411,24 +1413,56 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
static
bool
are_symlinks_supported
(
const
char
*
wd_path
)
{
git_config
*
config
=
NULL
;
git_buf
path
=
GIT_BUF_INIT
;
int
fd
;
struct
stat
st
;
int
symlinks_supported
=
-
1
;
bool
symlinks
=
false
;
/*
* To emulate Git for Windows, symlinks on Windows must be explicitly
* opted-in. We examine the system configuration for a core.symlinks
* set to true. If found, we then examine the filesystem to see if
* symlinks are _actually_ supported by the current user. If that is
* _not_ set, then we do not test or enable symlink support.
*/
#ifdef GIT_WIN32
git_buf
global_buf
=
GIT_BUF_INIT
;
git_buf
xdg_buf
=
GIT_BUF_INIT
;
git_buf
system_buf
=
GIT_BUF_INIT
;
git_buf
programdata_buf
=
GIT_BUF_INIT
;
git_config_find_global
(
&
global_buf
);
git_config_find_xdg
(
&
xdg_buf
);
git_config_find_system
(
&
system_buf
);
git_config_find_programdata
(
&
programdata_buf
);
if
(
load_config
(
&
config
,
NULL
,
path_unless_empty
(
&
global_buf
),
path_unless_empty
(
&
xdg_buf
),
path_unless_empty
(
&
system_buf
),
path_unless_empty
(
&
programdata_buf
))
<
0
)
goto
done
;
if
(
git_config_get_bool
(
&
symlinks
,
config
,
"core.symlinks"
)
<
0
||
!
symlinks
)
goto
done
;
#endif
if
((
fd
=
git_futils_mktmp
(
&
path
,
wd_path
,
0666
))
<
0
||
p_close
(
fd
)
<
0
||
p_unlink
(
path
.
ptr
)
<
0
||
p_symlink
(
"testing"
,
path
.
ptr
)
<
0
||
p_lstat
(
path
.
ptr
,
&
st
)
<
0
)
symlinks_supported
=
fals
e
;
else
symlinks_supported
=
(
S_ISLNK
(
st
.
st_mode
)
!=
0
);
p_close
(
fd
)
<
0
||
p_unlink
(
path
.
ptr
)
<
0
||
p_symlink
(
"testing"
,
path
.
ptr
)
<
0
||
p_lstat
(
path
.
ptr
,
&
st
)
<
0
)
goto
don
e
;
symlinks
=
(
S_ISLNK
(
st
.
st_mode
)
!=
0
);
(
void
)
p_unlink
(
path
.
ptr
);
git_buf_dispose
(
&
path
);
return
symlinks_supported
;
done:
git_buf_dispose
(
&
path
);
git_config_free
(
config
);
return
symlinks
;
}
static
int
create_empty_file
(
const
char
*
path
,
mode_t
mode
)
...
...
src/win32/posix_w32.c
View file @
7321cff0
...
...
@@ -29,15 +29,16 @@
#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
#endif
#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x02
#endif
/* Allowable mode bits on Win32. Using mode bits that are not supported on
* Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
* so we simply remove them.
*/
#define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
/* GetFinalPathNameByHandleW signature */
typedef
DWORD
(
WINAPI
*
PFGetFinalPathNameByHandleW
)(
HANDLE
,
LPWSTR
,
DWORD
,
DWORD
);
unsigned
long
git_win32__createfile_sharemode
=
FILE_SHARE_READ
|
FILE_SHARE_WRITE
;
int
git_win32__retries
=
10
;
...
...
@@ -393,12 +394,20 @@ int p_readlink(const char *path, char *buf, size_t bufsiz)
return
(
int
)
bufsiz
;
}
int
p_symlink
(
const
char
*
old
,
const
char
*
new
)
int
p_symlink
(
const
char
*
target
,
const
char
*
path
)
{
/* Real symlinks on NTFS require admin privileges. Until this changes,
* libgit2 just creates a text file with the link target in the contents.
*/
return
git_futils_fake_symlink
(
old
,
new
);
git_win32_path
target_w
,
path_w
;
wchar_t
*
target_p
;
if
(
git_win32_path_from_utf8
(
path_w
,
path
)
<
0
||
git__utf8_to_16
(
target_w
,
MAX_PATH
,
target
)
<
0
)
return
-
1
;
if
(
!
CreateSymbolicLinkW
(
path_w
,
target_w
,
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
))
return
-
1
;
return
0
;
}
struct
open_opts
{
...
...
@@ -598,40 +607,13 @@ int p_getcwd(char *buffer_out, size_t size)
return
0
;
}
/*
* Returns the address of the GetFinalPathNameByHandleW function.
* This function is available on Windows Vista and higher.
*/
static
PFGetFinalPathNameByHandleW
get_fpnbyhandle
(
void
)
{
static
PFGetFinalPathNameByHandleW
pFunc
=
NULL
;
PFGetFinalPathNameByHandleW
toReturn
=
pFunc
;
if
(
!
toReturn
)
{
HMODULE
hModule
=
GetModuleHandleW
(
L"kernel32"
);
if
(
hModule
)
toReturn
=
(
PFGetFinalPathNameByHandleW
)
GetProcAddress
(
hModule
,
"GetFinalPathNameByHandleW"
);
pFunc
=
toReturn
;
}
assert
(
toReturn
);
return
toReturn
;
}
static
int
getfinalpath_w
(
git_win32_path
dest
,
const
wchar_t
*
path
)
{
PFGetFinalPathNameByHandleW
pgfp
=
get_fpnbyhandle
();
HANDLE
hFile
;
DWORD
dwChars
;
if
(
!
pgfp
)
return
-
1
;
/* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
* specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
* target of the link. */
...
...
@@ -642,7 +624,7 @@ static int getfinalpath_w(
return
-
1
;
/* Call GetFinalPathNameByHandle */
dwChars
=
pgfp
(
hFile
,
dest
,
GIT_WIN_PATH_UTF16
,
FILE_NAME_NORMALIZED
);
dwChars
=
GetFinalPathNameByHandleW
(
hFile
,
dest
,
GIT_WIN_PATH_UTF16
,
FILE_NAME_NORMALIZED
);
CloseHandle
(
hFile
);
if
(
!
dwChars
||
dwChars
>=
GIT_WIN_PATH_UTF16
)
...
...
tests/checkout/conflict.c
View file @
7321cff0
...
...
@@ -2,6 +2,7 @@
#include "git2/repository.h"
#include "git2/sys/index.h"
#include "fileops.h"
#include "repository.h"
static
git_repository
*
g_repo
;
static
git_index
*
g_index
;
...
...
@@ -184,28 +185,35 @@ static void ensure_workdir(const char *path, int mode, const char *oid_str)
ensure_workdir_oid
(
path
,
oid_str
);
}
static
void
ensure_workdir_link
(
const
char
*
path
,
const
char
*
target
)
static
void
ensure_workdir_link
(
git_repository
*
repo
,
const
char
*
path
,
const
char
*
target
)
{
#ifdef GIT_WIN32
ensure_workdir_contents
(
path
,
target
);
#else
git_buf
fullpath
=
GIT_BUF_INIT
;
char
actual
[
1024
];
struct
stat
st
;
int
len
;
int
symlinks
;
cl_git_pass
(
git_buf_joinpath
(
&
fullpath
,
git_repository_workdir
(
g_repo
),
path
));
cl_git_pass
(
git_repository__cvar
(
&
symlinks
,
repo
,
GIT_CVAR_SYMLINKS
));
cl_git_pass
(
p_lstat
(
git_buf_cstr
(
&
fullpath
),
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
if
(
!
symlinks
)
{
ensure_workdir_contents
(
path
,
target
);
}
else
{
git_buf
fullpath
=
GIT_BUF_INIT
;
char
actual
[
1024
];
struct
stat
st
;
int
len
;
cl_assert
((
len
=
p_readlink
(
git_buf_cstr
(
&
fullpath
),
actual
,
1024
))
>
0
);
actual
[
len
]
=
'\0'
;
cl_assert
(
strcmp
(
actual
,
target
)
==
0
);
cl_git_pass
(
git_buf_joinpath
(
&
fullpath
,
git_repository_workdir
(
g_repo
),
path
));
git_buf_dispose
(
&
fullpath
);
#endif
cl_git_pass
(
p_lstat
(
git_buf_cstr
(
&
fullpath
),
&
st
));
cl_assert
(
S_ISLNK
(
st
.
st_mode
));
cl_assert
((
len
=
p_readlink
(
git_buf_cstr
(
&
fullpath
),
actual
,
1024
))
>
0
);
actual
[
len
]
=
'\0'
;
cl_assert
(
strcmp
(
actual
,
target
)
==
0
);
git_buf_dispose
(
&
fullpath
);
}
}
void
test_checkout_conflict__ignored
(
void
)
...
...
@@ -415,8 +423,8 @@ void test_checkout_conflict__links(void)
cl_git_pass
(
git_checkout_index
(
g_repo
,
g_index
,
&
opts
));
/* Conflicts with links always keep the ours side (even with -Xtheirs) */
ensure_workdir_link
(
"link-1"
,
LINK_OURS_TARGET
);
ensure_workdir_link
(
"link-2"
,
LINK_OURS_TARGET
);
ensure_workdir_link
(
g_repo
,
"link-1"
,
LINK_OURS_TARGET
);
ensure_workdir_link
(
g_repo
,
"link-2"
,
LINK_OURS_TARGET
);
}
void
test_checkout_conflict__add_add
(
void
)
...
...
@@ -684,7 +692,7 @@ void test_checkout_conflict__renames(void)
void
test_checkout_conflict__rename_keep_ours
(
void
)
{
git_checkout_options
opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
struct
checkout_index_entry
checkout_index_entries
[]
=
{
{
0100644
,
"68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e"
,
0
,
"0a-no-change.txt"
},
{
0100644
,
"f0ce2b8e4986084d9b308fb72709e414c23eb5e6"
,
0
,
"0b-duplicated-in-ours.txt"
},
...
...
@@ -728,122 +736,122 @@ void test_checkout_conflict__rename_keep_ours(void)
{
0100644
,
"b42712cfe99a1a500b2a51fe984e0b8a7702ba11"
,
2
,
"7-both-renamed.txt"
},
{
0100644
,
"b69fe837e4cecfd4c9a40cdca7c138468687df07"
,
3
,
"7-both-renamed.txt"
}
};
struct
checkout_name_entry
checkout_name_entries
[]
=
{
{
"3a-renamed-in-ours-deleted-in-theirs.txt"
,
"3a-newname-in-ours-deleted-in-theirs.txt"
,
""
},
{
"3b-renamed-in-theirs-deleted-in-ours.txt"
,
""
,
"3b-newname-in-theirs-deleted-in-ours.txt"
},
{
"4a-renamed-in-ours-added-in-theirs.txt"
,
"4a-newname-in-ours-added-in-theirs.txt"
,
""
},
{
"4b-renamed-in-theirs-added-in-ours.txt"
,
""
,
"4b-newname-in-theirs-added-in-ours.txt"
},
{
"5a-renamed-in-ours-added-in-theirs.txt"
,
"5a-newname-in-ours-added-in-theirs.txt"
,
"5a-renamed-in-ours-added-in-theirs.txt"
},
{
"5b-renamed-in-theirs-added-in-ours.txt"
,
"5b-renamed-in-theirs-added-in-ours.txt"
,
"5b-newname-in-theirs-added-in-ours.txt"
},
{
"6-both-renamed-1-to-2.txt"
,
"6-both-renamed-1-to-2-ours.txt"
,
"6-both-renamed-1-to-2-theirs.txt"
},
{
"7-both-renamed-side-1.txt"
,
"7-both-renamed.txt"
,
"7-both-renamed-side-1.txt"
},
{
"7-both-renamed-side-2.txt"
,
"7-both-renamed-side-2.txt"
,
"7-both-renamed.txt"
}
};
opts
.
checkout_strategy
|=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_USE_OURS
;
create_index
(
checkout_index_entries
,
41
);
create_index_names
(
checkout_name_entries
,
9
);
cl_git_pass
(
git_index_write
(
g_index
));
cl_git_pass
(
git_checkout_index
(
g_repo
,
g_index
,
&
opts
));
ensure_workdir
(
"0a-no-change.txt"
,
0100644
,
"68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e"
);
ensure_workdir
(
"0b-duplicated-in-ours.txt"
,
0100644
,
"f0ce2b8e4986084d9b308fb72709e414c23eb5e6"
);
ensure_workdir
(
"0b-rewritten-in-ours.txt"
,
0100644
,
"e376fbdd06ebf021c92724da9f26f44212734e3e"
);
ensure_workdir
(
"0c-duplicated-in-theirs.txt"
,
0100644
,
"2f56120107d680129a5d9791b521cb1e73a2ed31"
);
ensure_workdir
(
"0c-rewritten-in-theirs.txt"
,
0100644
,
"efc9121fdedaf08ba180b53ebfbcf71bd488ed09"
);
ensure_workdir
(
"1a-newname-in-ours-edited-in-theirs.txt"
,
0100644
,
"0d872f8e871a30208305978ecbf9e66d864f1638"
);
ensure_workdir
(
"1a-newname-in-ours.txt"
,
0100644
,
"d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb"
);
ensure_workdir
(
"1b-newname-in-theirs-edited-in-ours.txt"
,
0100644
,
"ed9523e62e453e50dd9be1606af19399b96e397a"
);
ensure_workdir
(
"1b-newname-in-theirs.txt"
,
0100644
,
"2b5f1f181ee3b58ea751f5dd5d8f9b445520a136"
);
ensure_workdir
(
"2-newname-in-both.txt"
,
0100644
,
"178940b450f238a56c0d75b7955cb57b38191982"
);
ensure_workdir
(
"3a-newname-in-ours-deleted-in-theirs.txt"
,
0100644
,
"18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9"
);
ensure_workdir
(
"3b-newname-in-theirs-deleted-in-ours.txt"
,
0100644
,
"36219b49367146cb2e6a1555b5a9ebd4d0328495"
);
ensure_workdir
(
"4a-newname-in-ours-added-in-theirs.txt"
,
0100644
,
"227792b52aaa0b238bea00ec7e509b02623f168c"
);
ensure_workdir
(
"4b-newname-in-theirs-added-in-ours.txt"
,
0100644
,
"de872ee3618b894992e9d1e18ba2ebe256a112f9"
);
ensure_workdir
(
"5a-newname-in-ours-added-in-theirs.txt"
,
0100644
,
"d3719a5ae8e4d92276b5313ce976f6ee5af2b436"
);
ensure_workdir
(
"5b-newname-in-theirs-added-in-ours.txt"
,
0100644
,
"385c8a0f26ddf79e9041e15e17dc352ed2c4cced"
);
ensure_workdir
(
"6-both-renamed-1-to-2-ours.txt"
,
0100644
,
"d8fa77b6833082c1ea36b7828a582d4c43882450"
);
ensure_workdir
(
"7-both-renamed.txt"
,
0100644
,
"b42712cfe99a1a500b2a51fe984e0b8a7702ba11"
);
}
...
...
tests/checkout/icase.c
View file @
7321cff0
...
...
@@ -3,6 +3,7 @@
#include "git2/checkout.h"
#include "refs.h"
#include "path.h"
#include "repository.h"
#ifdef GIT_WIN32
# include <windows.h>
...
...
@@ -44,29 +45,6 @@ void test_checkout_icase__cleanup(void)
static
char
*
get_filename
(
const
char
*
in
)
{
#ifdef GIT_WIN32
HANDLE
fh
;
HMODULE
kerneldll
;
char
*
filename
;
typedef
DWORD
(
__stdcall
*
getfinalpathname
)(
HANDLE
,
LPSTR
,
DWORD
,
DWORD
);
getfinalpathname
getfinalpathfn
;
cl_assert
(
filename
=
malloc
(
MAX_PATH
));
cl_assert
(
kerneldll
=
LoadLibrary
(
"kernel32.dll"
));
cl_assert
(
getfinalpathfn
=
(
getfinalpathname
)
GetProcAddress
(
kerneldll
,
"GetFinalPathNameByHandleA"
));
cl_assert
(
fh
=
CreateFileA
(
in
,
FILE_READ_ATTRIBUTES
|
STANDARD_RIGHTS_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
FILE_FLAG_BACKUP_SEMANTICS
,
NULL
));
cl_win32_pass
(
getfinalpathfn
(
fh
,
filename
,
MAX_PATH
,
VOLUME_NAME_DOS
));
CloseHandle
(
fh
);
git_path_mkposix
(
filename
);
return
filename
;
#else
char
*
search_dirname
,
*
search_filename
,
*
filename
=
NULL
;
git_buf
out
=
GIT_BUF_INIT
;
DIR
*
dir
;
...
...
@@ -92,7 +70,6 @@ static char *get_filename(const char *in)
git_buf_dispose
(
&
out
);
return
filename
;
#endif
}
static
void
assert_name_is
(
const
char
*
expected
)
...
...
@@ -115,6 +92,18 @@ static void assert_name_is(const char *expected)
free
(
actual
);
}
static
int
symlink_or_fake
(
git_repository
*
repo
,
const
char
*
a
,
const
char
*
b
)
{
int
symlinks
;
cl_git_pass
(
git_repository__cvar
(
&
symlinks
,
repo
,
GIT_CVAR_SYMLINKS
));
if
(
symlinks
)
return
p_symlink
(
a
,
b
);
else
return
git_futils_fake_symlink
(
a
,
b
);
}
void
test_checkout_icase__refuses_to_overwrite_files_for_files
(
void
)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
...
...
@@ -141,7 +130,7 @@ void test_checkout_icase__refuses_to_overwrite_links_for_files(void)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_symlink
(
"../tmp"
,
"testrepo/BRANCH_FILE.txt"
));
cl_must_pass
(
symlink_or_fake
(
repo
,
"../tmp"
,
"testrepo/BRANCH_FILE.txt"
));
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -153,7 +142,7 @@ void test_checkout_icase__overwrites_links_for_files_when_forced(void)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_must_pass
(
p_symlink
(
"../tmp"
,
"testrepo/NEW.txt"
));
cl_must_pass
(
symlink_or_fake
(
repo
,
"../tmp"
,
"testrepo/NEW.txt"
));
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -229,7 +218,7 @@ void test_checkout_icase__refuses_to_overwrite_links_for_folders(void)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_must_pass
(
p_symlink
(
".."
,
"testrepo/A"
));
cl_must_pass
(
symlink_or_fake
(
repo
,
".."
,
"testrepo/A"
));
cl_git_fail
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
@@ -241,7 +230,7 @@ void test_checkout_icase__overwrites_links_for_folders_when_forced(void)
{
checkout_opts
.
checkout_strategy
=
GIT_CHECKOUT_FORCE
;
cl_must_pass
(
p_symlink
(
".."
,
"testrepo/A"
));
cl_must_pass
(
symlink_or_fake
(
repo
,
".."
,
"testrepo/A"
));
cl_git_pass
(
git_checkout_tree
(
repo
,
obj
,
&
checkout_opts
));
...
...
tests/checkout/index.c
View file @
7321cff0
...
...
@@ -5,8 +5,10 @@
#include "fileops.h"
#include "repository.h"
#include "remote.h"
#include "repo/repo_helpers.h"
static
git_repository
*
g_repo
;
static
git_buf
g_global_path
=
GIT_BUF_INIT
;
void
test_checkout_index__initialize
(
void
)
{
...
...
@@ -22,21 +24,29 @@ void test_checkout_index__initialize(void)
cl_git_rewritefile
(
"./testrepo/.gitattributes"
,
"* text eol=lf
\n
"
);
git_libgit2_opts
(
GIT_OPT_GET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
&
g_global_path
);
}
void
test_checkout_index__cleanup
(
void
)
{
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
g_global_path
.
ptr
);
git_buf_dispose
(
&
g_global_path
);
cl_git_sandbox_cleanup
();
/* try to remove alternative dir */
if
(
git_path_isdir
(
"alternative"
))
git_futils_rmdir_r
(
"alternative"
,
NULL
,
GIT_RMDIR_REMOVE_FILES
);
/* try to remove directories created by tests */
cl_fixture_cleanup
(
"alternative"
);
cl_fixture_cleanup
(
"symlink"
);
cl_fixture_cleanup
(
"symlink.git"
);
cl_fixture_cleanup
(
"tmp_global_path"
);
}
void
test_checkout_index__cannot_checkout_a_bare_repository
(
void
)
{
test_checkout_index__cleanup
();
cl_git_sandbox_cleanup
();
g_repo
=
cl_git_sandbox_init
(
"testrepo.git"
);
cl_git_fail
(
git_checkout_index
(
g_repo
,
NULL
,
NULL
));
...
...
@@ -136,23 +146,20 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
#endif
}
void
test_checkout_index__honor_coresymlinks_default
(
void
)
static
void
populate_symlink_workdir
(
void
)
{
git_repository
*
repo
;
git_remote
*
origin
;
git_object
*
target
;
char
cwd
[
GIT_PATH_MAX
];
const
char
*
url
=
git_repository_path
(
g_repo
);
cl_assert
(
getcwd
(
cwd
,
sizeof
(
cwd
))
!=
NULL
);
cl_assert_equal_i
(
0
,
p_mkdir
(
"readonly"
,
0555
));
/* Read-only directory */
cl_assert_equal_i
(
0
,
chdir
(
"readonly"
));
cl_git_pass
(
git_repository_init
(
&
repo
,
"../symlink.git"
,
true
));
cl_assert_equal_i
(
0
,
chdir
(
cwd
));
cl_assert_equal_i
(
0
,
p_mkdir
(
"symlink"
,
0777
));
cl_git_pass
(
git_repository_set_workdir
(
repo
,
"symlink"
,
1
));
/* Delete the `origin` repo (if it exists) so we can recreate it. */
git_remote_delete
(
repo
,
GIT_REMOTE_ORIGIN
);
cl_git_pass
(
git_remote_create
(
&
origin
,
repo
,
GIT_REMOTE_ORIGIN
,
url
));
cl_git_pass
(
git_remote_fetch
(
origin
,
NULL
,
NULL
,
NULL
));
git_remote_free
(
origin
);
...
...
@@ -161,28 +168,79 @@ void test_checkout_index__honor_coresymlinks_default(void)
cl_git_pass
(
git_reset
(
repo
,
target
,
GIT_RESET_HARD
,
NULL
));
git_object_free
(
target
);
git_repository_free
(
repo
);
}
void
test_checkout_index__honor_coresymlinks_default_true
(
void
)
{
char
link_data
[
GIT_PATH_MAX
];
int
link_size
=
GIT_PATH_MAX
;
cl_must_pass
(
p_mkdir
(
"symlink"
,
0777
));
if
(
!
filesystem_supports_symlinks
(
"symlink/test"
))
cl_skip
();
#ifdef GIT_WIN32
/*
* Windows explicitly requires the global configuration to have
* core.symlinks=true in addition to actual filesystem support.
*/
create_tmp_global_config
(
"tmp_global_path"
,
"core.symlinks"
,
"true"
);
#endif
populate_symlink_workdir
();
link_size
=
p_readlink
(
"./symlink/link_to_new.txt"
,
link_data
,
link_size
);
cl_assert
(
link_size
>=
0
);
link_data
[
link_size
]
=
'\0'
;
cl_assert_equal_i
(
link_size
,
strlen
(
"new.txt"
));
cl_assert_equal_s
(
link_data
,
"new.txt"
);
check_file_contents
(
"./symlink/link_to_new.txt"
,
"my new file
\n
"
);
}
void
test_checkout_index__honor_coresymlinks_default_false
(
void
)
{
cl_must_pass
(
p_mkdir
(
"symlink"
,
0777
));
#ifndef GIT_WIN32
/*
* This test is largely for Windows platforms to ensure that
* we respect an unset core.symlinks even when the platform
* supports symlinks. Bail entirely on POSIX platforms that
* do support symlinks.
*/
if
(
filesystem_supports_symlinks
(
"symlink/test"
))
cl_skip
();
#endif
populate_symlink_workdir
();
check_file_contents
(
"./symlink/link_to_new.txt"
,
"new.txt"
);
#else
{
char
link_data
[
1024
];
size_t
link_size
=
1024
;
link_size
=
p_readlink
(
"./symlink/link_to_new.txt"
,
link_data
,
link_size
);
link_data
[
link_size
]
=
'\0'
;
cl_assert_equal_i
(
link_size
,
strlen
(
"new.txt"
));
cl_assert_equal_s
(
link_data
,
"new.txt"
);
check_file_contents
(
"./symlink/link_to_new.txt"
,
"my new file
\n
"
);
}
void
test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported
(
void
)
{
git_checkout_options
opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
if
(
filesystem_supports_symlinks
(
"testrepo/test"
))
{
cl_skip
();
}
#endif
cl_fixture_cleanup
(
"symlink"
);
cl_repo_set_bool
(
g_repo
,
"core.symlinks"
,
true
);
opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
;
cl_git_fail
(
git_checkout_index
(
g_repo
,
NULL
,
&
opts
));
}
void
test_checkout_index__honor_coresymlinks_setting_set_to_true
(
void
)
{
git_checkout_options
opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
char
link_data
[
GIT_PATH_MAX
];
size_t
link_size
=
GIT_PATH_MAX
;
if
(
!
filesystem_supports_symlinks
(
"testrepo/test"
))
{
cl_skip
();
}
cl_repo_set_bool
(
g_repo
,
"core.symlinks"
,
true
);
...
...
@@ -190,20 +248,11 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
cl_git_pass
(
git_checkout_index
(
g_repo
,
NULL
,
&
opts
));
#ifdef GIT_WIN32
check_file_contents
(
"./testrepo/link_to_new.txt"
,
"new.txt"
);
#else
{
char
link_data
[
1024
];
size_t
link_size
=
1024
;
link_size
=
p_readlink
(
"./testrepo/link_to_new.txt"
,
link_data
,
link_size
);
link_data
[
link_size
]
=
'\0'
;
cl_assert_equal_i
(
link_size
,
strlen
(
"new.txt"
));
cl_assert_equal_s
(
link_data
,
"new.txt"
);
check_file_contents
(
"./testrepo/link_to_new.txt"
,
"my new file
\n
"
);
}
#endif
link_size
=
p_readlink
(
"./testrepo/link_to_new.txt"
,
link_data
,
link_size
);
link_data
[
link_size
]
=
'\0'
;
cl_assert_equal_i
(
link_size
,
strlen
(
"new.txt"
));
cl_assert_equal_s
(
link_data
,
"new.txt"
);
check_file_contents
(
"./testrepo/link_to_new.txt"
,
"my new file
\n
"
);
}
void
test_checkout_index__honor_coresymlinks_setting_set_to_false
(
void
)
...
...
@@ -474,7 +523,7 @@ void test_checkout_index__can_overcome_name_clashes(void)
cl_assert
(
git_path_isfile
(
"./testrepo/path0/file0"
));
opts
.
checkout_strategy
=
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_SAFE
|
GIT_CHECKOUT_RECREATE_MISSING
|
GIT_CHECKOUT_ALLOW_CONFLICTS
;
cl_git_pass
(
git_checkout_index
(
g_repo
,
index
,
&
opts
));
...
...
@@ -547,9 +596,9 @@ void test_checkout_index__can_update_prefixed_files(void)
void
test_checkout_index__can_checkout_a_newly_initialized_repository
(
void
)
{
test_checkout_index__cleanup
();
cl_git_sandbox_cleanup
();
g_repo
=
cl_git_sandbox_init
(
"empty_standard_repo"
);
cl_git_remove_placeholders
(
git_repository_path
(
g_repo
),
"dummy-marker.txt"
);
cl_git_pass
(
git_checkout_index
(
g_repo
,
NULL
,
NULL
));
...
...
@@ -559,8 +608,7 @@ void test_checkout_index__issue_1397(void)
{
git_checkout_options
opts
=
GIT_CHECKOUT_OPTIONS_INIT
;
test_checkout_index__cleanup
();
cl_git_sandbox_cleanup
();
g_repo
=
cl_git_sandbox_init
(
"issue_1397"
);
cl_repo_set_bool
(
g_repo
,
"core.autocrlf"
,
true
);
...
...
@@ -613,8 +661,7 @@ void test_checkout_index__target_directory_from_bare(void)
checkout_counts
cts
;
memset
(
&
cts
,
0
,
sizeof
(
cts
));
test_checkout_index__cleanup
();
cl_git_sandbox_cleanup
();
g_repo
=
cl_git_sandbox_init
(
"testrepo.git"
);
cl_assert
(
git_repository_is_bare
(
g_repo
));
...
...
tests/repo/init.c
View file @
7321cff0
...
...
@@ -4,6 +4,7 @@
#include "config.h"
#include "path.h"
#include "config/config_helpers.h"
#include "repo/repo_helpers.h"
enum
repo_mode
{
STANDARD_REPOSITORY
=
0
,
...
...
@@ -12,7 +13,6 @@ enum repo_mode {
static
git_repository
*
_repo
=
NULL
;
static
git_buf
_global_path
=
GIT_BUF_INIT
;
static
git_buf
_tmp_path
=
GIT_BUF_INIT
;
static
mode_t
g_umask
=
0
;
void
test_repo_init__initialize
(
void
)
...
...
@@ -35,9 +35,7 @@ void test_repo_init__cleanup(void)
_global_path
.
ptr
);
git_buf_dispose
(
&
_global_path
);
if
(
_tmp_path
.
size
>
0
&&
git_path_isdir
(
_tmp_path
.
ptr
))
git_futils_rmdir_r
(
_tmp_path
.
ptr
,
NULL
,
GIT_RMDIR_REMOVE_FILES
);
git_buf_dispose
(
&
_tmp_path
);
cl_fixture_cleanup
(
"tmp_global_path"
);
}
static
void
cleanup_repository
(
void
*
path
)
...
...
@@ -247,6 +245,68 @@ void test_repo_init__detect_ignorecase(void)
"core.ignorecase"
,
found_without_match
?
true
:
GIT_ENOTFOUND
);
}
/*
* Windows: if the filesystem supports symlinks (because we're running
* as administrator, or because the user has opted into it for normal
* users) then we can also opt-in explicitly by settings `core.symlinks`
* in the global config. Symlinks remain off by default.
*/
void
test_repo_init__symlinks_win32_enabled_by_global_config
(
void
)
{
#ifndef GIT_WIN32
cl_skip
();
#else
git_config
*
config
,
*
repo_config
;
int
val
;
if
(
!
filesystem_supports_symlinks
(
"link"
))
cl_skip
();
create_tmp_global_config
(
"tmp_global_config"
,
"core.symlinks"
,
"true"
);
/*
* Create a new repository (can't use `assert_config_on_init` since we
* want to examine configuration levels with more granularity.)
*/
cl_git_pass
(
git_repository_init
(
&
_repo
,
"config_entry/test.non.bare.git"
,
false
));
/* Ensure that core.symlinks remains set (via the global config). */
cl_git_pass
(
git_repository_config
(
&
config
,
_repo
));
cl_git_pass
(
git_config_get_bool
(
&
val
,
config
,
"core.symlinks"
));
cl_assert_equal_i
(
1
,
val
);
/*
* Ensure that the repository config does not set core.symlinks.
* It should remain inherited.
*/
cl_git_pass
(
git_config_open_level
(
&
repo_config
,
config
,
GIT_CONFIG_LEVEL_LOCAL
));
cl_git_fail_with
(
GIT_ENOTFOUND
,
git_config_get_bool
(
&
val
,
repo_config
,
"core.symlinks"
));
git_config_free
(
repo_config
);
git_config_free
(
config
);
#endif
}
void
test_repo_init__symlinks_win32_off_by_default
(
void
)
{
#ifndef GIT_WIN32
cl_skip
();
#else
assert_config_entry_on_init
(
"core.symlinks"
,
false
);
#endif
}
void
test_repo_init__symlinks_posix_detected
(
void
)
{
#ifdef GIT_WIN32
cl_skip
();
#else
assert_config_entry_on_init
(
"core.symlinks"
,
filesystem_supports_symlinks
(
"link"
)
?
GIT_ENOTFOUND
:
false
);
#endif
}
void
test_repo_init__detect_precompose_unicode_required
(
void
)
{
#ifdef GIT_USE_ICONV
...
...
@@ -563,26 +623,7 @@ static const char *template_sandbox(const char *name)
static
void
configure_templatedir
(
const
char
*
template_path
)
{
git_buf
config_path
=
GIT_BUF_INIT
;
git_buf
config_data
=
GIT_BUF_INIT
;
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_GET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
&
_tmp_path
));
cl_git_pass
(
git_buf_puts
(
&
_tmp_path
,
".tmp"
));
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
_tmp_path
.
ptr
));
cl_must_pass
(
p_mkdir
(
_tmp_path
.
ptr
,
0777
));
cl_git_pass
(
git_buf_joinpath
(
&
config_path
,
_tmp_path
.
ptr
,
".gitconfig"
));
cl_git_pass
(
git_buf_printf
(
&
config_data
,
"[init]
\n\t
templatedir =
\"
%s
\"\n
"
,
template_path
));
cl_git_mkfile
(
config_path
.
ptr
,
config_data
.
ptr
);
git_buf_dispose
(
&
config_path
);
git_buf_dispose
(
&
config_data
);
create_tmp_global_config
(
"tmp_global_path"
,
"init.templatedir"
,
template_path
);
}
static
void
validate_templates
(
git_repository
*
repo
,
const
char
*
template_path
)
...
...
tests/repo/repo_helpers.c
View file @
7321cff0
...
...
@@ -20,3 +20,33 @@ void delete_head(git_repository* repo)
git_buf_dispose
(
&
head_path
);
}
int
filesystem_supports_symlinks
(
const
char
*
path
)
{
struct
stat
st
;
bool
support
=
0
;
if
(
p_symlink
(
"target"
,
path
)
==
0
)
{
if
(
p_lstat
(
path
,
&
st
)
==
0
&&
S_ISLNK
(
st
.
st_mode
))
support
=
1
;
p_unlink
(
path
);
}
return
support
;
}
void
create_tmp_global_config
(
const
char
*
dirname
,
const
char
*
key
,
const
char
*
val
)
{
git_buf
path
=
GIT_BUF_INIT
;
git_config
*
config
;
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
dirname
));
cl_must_pass
(
p_mkdir
(
dirname
,
0777
));
cl_git_pass
(
git_buf_joinpath
(
&
path
,
dirname
,
".gitconfig"
));
cl_git_pass
(
git_config_open_ondisk
(
&
config
,
path
.
ptr
));
cl_git_pass
(
git_config_set_string
(
config
,
key
,
val
));
git_config_free
(
config
);
git_buf_dispose
(
&
path
);
}
tests/repo/repo_helpers.h
View file @
7321cff0
...
...
@@ -4,3 +4,5 @@
extern
void
make_head_unborn
(
git_repository
*
repo
,
const
char
*
target
);
extern
void
delete_head
(
git_repository
*
repo
);
extern
int
filesystem_supports_symlinks
(
const
char
*
path
);
extern
void
create_tmp_global_config
(
const
char
*
path
,
const
char
*
key
,
const
char
*
val
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment