mirror of
https://github.com/git/git.git
synced 2026-02-04 23:16:38 +00:00
Merge 'msys2' into HEAD
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
@@ -265,9 +265,9 @@ docdep_prereqs = \
|
||||
cmd-list.made $(cmds_txt)
|
||||
|
||||
doc.dep : $(docdep_prereqs) $(wildcard *.txt) build-docdep.perl
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
$(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
|
||||
mv $@+ $@
|
||||
$(QUIET_GEN)$(RM) $@.new $@ && \
|
||||
$(PERL_PATH) ./build-docdep.perl >$@.new $(QUIET_STDERR) && \
|
||||
mv $@.new $@
|
||||
|
||||
-include doc.dep
|
||||
|
||||
@@ -303,8 +303,8 @@ mergetools-list.made: ../git-mergetool--lib.sh $(wildcard ../mergetools/*)
|
||||
date >$@
|
||||
|
||||
clean:
|
||||
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
|
||||
$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
|
||||
$(RM) *.xml *.xml.new *.html *.html.new *.1 *.5 *.7
|
||||
$(RM) *.texi *.texi.new *.texi.new.new git.info gitman.info
|
||||
$(RM) *.pdf
|
||||
$(RM) howto-index.txt howto/*.html doc.dep
|
||||
$(RM) technical/*.html technical/api-index.txt
|
||||
@@ -312,14 +312,14 @@ clean:
|
||||
$(RM) manpage-base-url.xsl
|
||||
|
||||
$(MAN_HTML): %.html : %.txt asciidoc.conf
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(TXT_TO_HTML) -d manpage -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_ASCIIDOC)$(RM) $@.new $@ && \
|
||||
$(TXT_TO_HTML) -d manpage -o $@.new $< && \
|
||||
mv $@.new $@
|
||||
|
||||
$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(TXT_TO_HTML) -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_ASCIIDOC)$(RM) $@.new $@ && \
|
||||
$(TXT_TO_HTML) -o $@.new $< && \
|
||||
mv $@.new $@
|
||||
|
||||
manpage-base-url.xsl: manpage-base-url.xsl.in
|
||||
sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
|
||||
@@ -329,14 +329,14 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
|
||||
$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
|
||||
|
||||
%.xml : %.txt asciidoc.conf
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(TXT_TO_XML) -d manpage -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_ASCIIDOC)$(RM) $@.new $@ && \
|
||||
$(TXT_TO_XML) -d manpage -o $@.new $< && \
|
||||
mv $@.new $@
|
||||
|
||||
user-manual.xml: user-manual.txt user-manual.conf
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(TXT_TO_XML) -d article -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_ASCIIDOC)$(RM) $@.new $@ && \
|
||||
$(TXT_TO_XML) -d book -o $@.new $< && \
|
||||
mv $@.new $@
|
||||
|
||||
technical/api-index.txt: technical/api-index-skel.txt \
|
||||
technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
|
||||
@@ -350,45 +350,45 @@ XSLT = docbook.xsl
|
||||
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
|
||||
|
||||
user-manual.html: user-manual.xml $(XSLT)
|
||||
$(QUIET_XSLTPROC)$(RM) $@+ $@ && \
|
||||
xsltproc $(XSLTOPTS) -o $@+ $(XSLT) $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_XSLTPROC)$(RM) $@.new $@ && \
|
||||
xsltproc $(XSLTOPTS) -o $@.new $(XSLT) $< && \
|
||||
mv $@.new $@
|
||||
|
||||
git.info: user-manual.texi
|
||||
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
|
||||
|
||||
user-manual.texi: user-manual.xml
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
$(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
|
||||
$(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
|
||||
rm $@++ && \
|
||||
mv $@+ $@
|
||||
$(QUIET_DB2TEXI)$(RM) $@.new $@ && \
|
||||
$(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@.new.new && \
|
||||
$(PERL_PATH) fix-texi.perl <$@.new.new >$@.new && \
|
||||
rm $@.new.new && \
|
||||
mv $@.new $@
|
||||
|
||||
user-manual.pdf: user-manual.xml
|
||||
$(QUIET_DBLATEX)$(RM) $@+ $@ && \
|
||||
$(DBLATEX) -o $@+ -p $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.xsl -s $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.sty $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_DBLATEX)$(RM) $@.new $@ && \
|
||||
$(DBLATEX) -o $@.new -p $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.xsl -s $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.sty $< && \
|
||||
mv $@.new $@
|
||||
|
||||
gitman.texi: $(MAN_XML) cat-texi.perl
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
$(QUIET_DB2TEXI)$(RM) $@.new $@ && \
|
||||
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
|
||||
--to-stdout $(xml) &&) true) > $@++ && \
|
||||
$(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
|
||||
rm $@++ && \
|
||||
mv $@+ $@
|
||||
--to-stdout $(xml) &&) true) > $@.new.new && \
|
||||
$(PERL_PATH) cat-texi.perl $@ <$@.new.new >$@.new && \
|
||||
rm $@.new.new && \
|
||||
mv $@.new $@
|
||||
|
||||
gitman.info: gitman.texi
|
||||
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
|
||||
|
||||
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \
|
||||
mv $@+ $@
|
||||
$(QUIET_DB2TEXI)$(RM) $@.new $@ && \
|
||||
$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@.new && \
|
||||
mv $@.new $@
|
||||
|
||||
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
|
||||
$(QUIET_GEN)$(RM) $@+ $@ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(sort $(wildcard howto/*.txt)) >$@+ && \
|
||||
mv $@+ $@
|
||||
$(QUIET_GEN)$(RM) $@.new $@ && \
|
||||
'$(SHELL_PATH_SQ)' ./howto-index.sh $(sort $(wildcard howto/*.txt)) >$@.new && \
|
||||
mv $@.new $@
|
||||
|
||||
$(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
|
||||
@@ -397,10 +397,10 @@ WEBDOC_DEST = /pub/software/scm/git/docs
|
||||
|
||||
howto/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
|
||||
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
|
||||
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
|
||||
$(QUIET_ASCIIDOC)$(RM) $@.new $@ && \
|
||||
sed -e '1,/^$$/d' $< | \
|
||||
$(TXT_TO_HTML) - >$@+ && \
|
||||
mv $@+ $@
|
||||
$(TXT_TO_HTML) - >$@.new && \
|
||||
mv $@.new $@
|
||||
|
||||
install-webdoc : html
|
||||
'$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST)
|
||||
|
||||
@@ -713,6 +713,19 @@ relatively high IO latencies. When enabled, Git will do the
|
||||
index comparison to the filesystem data in parallel, allowing
|
||||
overlapping IO's. Defaults to true.
|
||||
|
||||
core.fscache::
|
||||
Enable additional caching of file system data for some operations.
|
||||
+
|
||||
Git for Windows uses this to bulk-read and cache lstat data of entire
|
||||
directories (instead of doing lstat file by file).
|
||||
|
||||
core.longpaths::
|
||||
Enable long path (> 260) support for builtin commands in Git for
|
||||
Windows. This is disabled by default, as long paths are not supported
|
||||
by Windows Explorer, cmd.exe and the Git for Windows tool chain
|
||||
(msys, bash, tcl, perl...). Only enable this if you know what you're
|
||||
doing and are prepared to live with a few quirks.
|
||||
|
||||
core.createObject::
|
||||
You can set this to 'link', in which case a hardlink followed by
|
||||
a delete of the source are used to make sure that object creation
|
||||
|
||||
@@ -308,18 +308,15 @@ master or exposed as a part of a stable branch.
|
||||
<10> create a signed tag.
|
||||
<11> make sure master was not accidentally rewound beyond that
|
||||
already pushed out. `ko` shorthand points at the Git maintainer's
|
||||
repository at kernel.org, and looks like this:
|
||||
+
|
||||
------------
|
||||
(in .git/config)
|
||||
[remote "ko"]
|
||||
url = kernel.org:/pub/scm/git/git.git
|
||||
fetch = refs/heads/*:refs/remotes/ko/*
|
||||
push = refs/heads/master
|
||||
push = refs/heads/next
|
||||
push = +refs/heads/pu
|
||||
push = refs/heads/maint
|
||||
------------
|
||||
repository at kernel.org, and looks like this: +
|
||||
++(in .git/config) +
|
||||
{startsb}remote "ko"{endsb} +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}url = kernel.org:/pub/scm/git/git.git +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}fetch = refs/heads/\*:refs/remotes/ko/* +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}push = refs/heads/master +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}push = refs/heads/next +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}push = +refs/heads/pu +
|
||||
{nbsp}{nbsp}{nbsp}{nbsp}push = refs/heads/maint++
|
||||
+
|
||||
<12> In the output from `git show-branch`, `master` should have
|
||||
everything `ko/master` has, and `next` should have
|
||||
|
||||
@@ -3424,7 +3424,7 @@ just missing one particular blob version.
|
||||
|
||||
[[the-index]]
|
||||
The index
|
||||
-----------
|
||||
---------
|
||||
|
||||
The index is a binary file (generally kept in `.git/index`) containing a
|
||||
sorted list of path names, each with permissions and the SHA-1 of a blob
|
||||
@@ -4366,6 +4366,10 @@ itself!
|
||||
Git Glossary
|
||||
============
|
||||
|
||||
[[git-explained]]
|
||||
Git explained
|
||||
-------------
|
||||
|
||||
include::glossary-content.txt[]
|
||||
|
||||
[[git-quick-start]]
|
||||
@@ -4607,6 +4611,10 @@ $ git gc
|
||||
Appendix B: Notes and todo list for this manual
|
||||
===============================================
|
||||
|
||||
[[todo-list]]
|
||||
Todo list
|
||||
---------
|
||||
|
||||
This is a work in progress.
|
||||
|
||||
The basic requirements:
|
||||
|
||||
@@ -574,6 +574,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
|
||||
clean_get_color(CLEAN_COLOR_RESET));
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
if (strbuf_getline(&choice, stdin, '\n') != EOF) {
|
||||
strbuf_trim(&choice);
|
||||
} else {
|
||||
@@ -656,6 +657,7 @@ static int filter_by_patterns_cmd(void)
|
||||
clean_print_color(CLEAN_COLOR_PROMPT);
|
||||
printf(_("Input ignore patterns>> "));
|
||||
clean_print_color(CLEAN_COLOR_RESET);
|
||||
fflush(stdout);
|
||||
if (strbuf_getline(&confirm, stdin, '\n') != EOF)
|
||||
strbuf_trim(&confirm);
|
||||
else
|
||||
@@ -754,6 +756,7 @@ static int ask_each_cmd(void)
|
||||
qname = quote_path_relative(item->string, NULL, &buf);
|
||||
/* TRANSLATORS: Make sure to keep [y/N] as is */
|
||||
printf(_("Remove %s [y/N]? "), qname);
|
||||
fflush(stdout);
|
||||
if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
|
||||
strbuf_trim(&confirm);
|
||||
} else {
|
||||
|
||||
@@ -1360,6 +1360,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
PATHSPEC_PREFER_FULL,
|
||||
prefix, argv);
|
||||
|
||||
enable_fscache(1);
|
||||
read_cache_preload(&s.pathspec);
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
|
||||
|
||||
|
||||
4
cache.h
4
cache.h
@@ -653,6 +653,10 @@ enum hide_dotfiles_type {
|
||||
};
|
||||
extern enum hide_dotfiles_type hide_dotfiles;
|
||||
|
||||
extern int core_fscache;
|
||||
|
||||
extern int core_long_paths;
|
||||
|
||||
enum branch_track {
|
||||
BRANCH_TRACK_UNSPECIFIED = -1,
|
||||
BRANCH_TRACK_NEVER = 0,
|
||||
|
||||
331
compat/mingw.c
331
compat/mingw.c
@@ -6,6 +6,8 @@
|
||||
#include "../run-command.h"
|
||||
#include "../cache.h"
|
||||
|
||||
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
||||
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
unsigned int _CRT_fmode = _O_BINARY;
|
||||
|
||||
@@ -204,8 +206,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
|
||||
int mingw_unlink(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
wchar_t wpathname[MAX_PATH];
|
||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||
wchar_t wpathname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
/* read-only files cannot be removed */
|
||||
@@ -234,7 +236,7 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||
{
|
||||
WIN32_FIND_DATAW findbuf;
|
||||
HANDLE handle;
|
||||
wchar_t wbuf[MAX_PATH + 2];
|
||||
wchar_t wbuf[MAX_LONG_PATH + 2];
|
||||
wcscpy(wbuf, wpath);
|
||||
wcscat(wbuf, L"\\*");
|
||||
handle = FindFirstFileW(wbuf, &findbuf);
|
||||
@@ -255,8 +257,8 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||
int mingw_rmdir(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
wchar_t wpathname[MAX_PATH];
|
||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||
wchar_t wpathname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||
@@ -296,9 +298,9 @@ static int make_hidden(const wchar_t *path)
|
||||
|
||||
void mingw_mark_as_git_dir(const char *dir)
|
||||
{
|
||||
wchar_t wdir[MAX_PATH];
|
||||
wchar_t wdir[MAX_LONG_PATH];
|
||||
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())
|
||||
if (xutftowcs_path(wdir, dir) < 0 || make_hidden(wdir))
|
||||
if (xutftowcs_long_path(wdir, dir) < 0 || make_hidden(wdir))
|
||||
warning("Failed to make '%s' hidden", dir);
|
||||
git_config_set("core.hideDotFiles",
|
||||
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
|
||||
@@ -309,9 +311,12 @@ void mingw_mark_as_git_dir(const char *dir)
|
||||
int mingw_mkdir(const char *path, int mode)
|
||||
{
|
||||
int ret;
|
||||
wchar_t wpath[MAX_PATH];
|
||||
if (xutftowcs_path(wpath, path) < 0)
|
||||
wchar_t wpath[MAX_LONG_PATH];
|
||||
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
|
||||
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
|
||||
core_long_paths) < 0)
|
||||
return -1;
|
||||
|
||||
ret = _wmkdir(wpath);
|
||||
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE) {
|
||||
/*
|
||||
@@ -331,7 +336,7 @@ int mingw_open (const char *filename, int oflags, ...)
|
||||
va_list args;
|
||||
unsigned mode;
|
||||
int fd;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
|
||||
va_start(args, oflags);
|
||||
mode = va_arg(args, int);
|
||||
@@ -340,7 +345,7 @@ int mingw_open (const char *filename, int oflags, ...)
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
fd = _wopen(wfilename, oflags, mode);
|
||||
|
||||
@@ -393,13 +398,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
|
||||
{
|
||||
int hide = 0;
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
|
||||
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
|
||||
basename((char*)filename)[0] == '.')
|
||||
hide = access(filename, F_OK);
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
return NULL;
|
||||
file = _wfopen(wfilename, wotype);
|
||||
@@ -412,13 +417,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
||||
{
|
||||
int hide = 0;
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
|
||||
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
|
||||
basename((char*)filename)[0] == '.')
|
||||
hide = access(filename, F_OK);
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
return NULL;
|
||||
file = _wfreopen(wfilename, wotype, stream);
|
||||
@@ -451,45 +456,36 @@ int mingw_fflush(FILE *stream)
|
||||
|
||||
int mingw_access(const char *filename, int mode)
|
||||
{
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
/* X_OK is not supported by the MSVCRT version */
|
||||
return _waccess(wfilename, mode & ~X_OK);
|
||||
}
|
||||
|
||||
/* cached length of current directory for handle_long_path */
|
||||
static int current_directory_len = 0;
|
||||
|
||||
int mingw_chdir(const char *dirname)
|
||||
{
|
||||
int result;
|
||||
wchar_t wdirname[MAX_PATH];
|
||||
/* SetCurrentDirectoryW doesn't support long paths */
|
||||
if (xutftowcs_path(wdirname, dirname) < 0)
|
||||
return -1;
|
||||
return _wchdir(wdirname);
|
||||
result = _wchdir(wdirname);
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
int mingw_chmod(const char *filename, int mode)
|
||||
{
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
return _wchmod(wfilename, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
||||
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
||||
*/
|
||||
static inline long long filetime_to_hnsec(const FILETIME *ft)
|
||||
{
|
||||
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
|
||||
/* Windows to Unix Epoch conversion */
|
||||
return winTime - 116444736000000000LL;
|
||||
}
|
||||
|
||||
static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
{
|
||||
return (time_t)(filetime_to_hnsec(ft) / 10000000);
|
||||
}
|
||||
|
||||
/* We keep the do_lstat code in a separate function to avoid recursion.
|
||||
* When a path ends with a slash, the stat will fail with ENOENT. In
|
||||
* this case, we strip the trailing slashes and stat again.
|
||||
@@ -500,8 +496,8 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, file_name) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, file_name) < 0)
|
||||
return -1;
|
||||
|
||||
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
||||
@@ -566,7 +562,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
{
|
||||
int namelen;
|
||||
char alt_name[PATH_MAX];
|
||||
char alt_name[MAX_LONG_PATH];
|
||||
|
||||
if (!do_lstat(follow, file_name, buf))
|
||||
return 0;
|
||||
@@ -582,7 +578,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
return -1;
|
||||
while (namelen && file_name[namelen-1] == '/')
|
||||
--namelen;
|
||||
if (!namelen || namelen >= PATH_MAX)
|
||||
if (!namelen || namelen >= MAX_LONG_PATH)
|
||||
return -1;
|
||||
|
||||
memcpy(alt_name, file_name, namelen);
|
||||
@@ -590,6 +586,8 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
return do_lstat(follow, alt_name, buf);
|
||||
}
|
||||
|
||||
int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
|
||||
|
||||
int mingw_lstat(const char *file_name, struct stat *buf)
|
||||
{
|
||||
return do_stat_internal(0, file_name, buf);
|
||||
@@ -642,8 +640,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
|
||||
FILETIME mft, aft;
|
||||
int fh, rc;
|
||||
DWORD attrs;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, file_name) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, file_name) < 0)
|
||||
return -1;
|
||||
|
||||
/* must have write permission */
|
||||
@@ -691,6 +689,7 @@ unsigned int sleep (unsigned int seconds)
|
||||
char *mingw_mktemp(char *template)
|
||||
{
|
||||
wchar_t wtemplate[MAX_PATH];
|
||||
/* we need to return the path, thus no long paths here! */
|
||||
if (xutftowcs_path(wtemplate, template) < 0)
|
||||
return NULL;
|
||||
if (!_wmktemp(wtemplate))
|
||||
@@ -729,13 +728,13 @@ int pipe(int filedes[2])
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
|
||||
filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
|
||||
if (filedes[0] < 0) {
|
||||
CloseHandle(h[0]);
|
||||
CloseHandle(h[1]);
|
||||
return -1;
|
||||
}
|
||||
filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
|
||||
filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
|
||||
if (filedes[1] < 0) {
|
||||
close(filedes[0]);
|
||||
CloseHandle(h[1]);
|
||||
@@ -837,11 +836,6 @@ static const char *parse_interpreter(const char *cmd)
|
||||
char *p, *opt;
|
||||
int n, fd;
|
||||
|
||||
/* don't even try a .exe */
|
||||
n = strlen(cmd);
|
||||
if (n >= 4 && !strcasecmp(cmd+n-4, ".exe"))
|
||||
return NULL;
|
||||
|
||||
fd = open(cmd, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
@@ -960,6 +954,10 @@ static int do_putenv(char **env, const char *name, int size, int free_old);
|
||||
static int environ_size = 0;
|
||||
/* allocated size of environ array, in bytes */
|
||||
static int environ_alloc = 0;
|
||||
/* used as a indicator when the environment has been changed outside mingw.c */
|
||||
static char **saved_environ;
|
||||
|
||||
static void maybe_reinitialize_environ(void);
|
||||
|
||||
/*
|
||||
* Create environment block suitable for CreateProcess. Merges current
|
||||
@@ -969,9 +967,12 @@ static wchar_t *make_environment_block(char **deltaenv)
|
||||
{
|
||||
wchar_t *wenvblk = NULL;
|
||||
char **tmpenv;
|
||||
int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0;
|
||||
int i = 0, size, wenvsz = 0, wenvpos = 0;
|
||||
|
||||
while (deltaenv && deltaenv[i])
|
||||
maybe_reinitialize_environ();
|
||||
size = environ_size;
|
||||
|
||||
while (deltaenv && deltaenv[i] && *deltaenv[i])
|
||||
i++;
|
||||
|
||||
/* copy the environment, leaving space for changes */
|
||||
@@ -979,11 +980,11 @@ static wchar_t *make_environment_block(char **deltaenv)
|
||||
memcpy(tmpenv, environ, size * sizeof(char*));
|
||||
|
||||
/* merge supplied environment changes into the temporary environment */
|
||||
for (i = 0; deltaenv && deltaenv[i]; i++)
|
||||
for (i = 0; deltaenv && deltaenv[i] && *deltaenv[i]; i++)
|
||||
size = do_putenv(tmpenv, deltaenv[i], size, 0);
|
||||
|
||||
/* create environment block from temporary environment */
|
||||
for (i = 0; tmpenv[i]; i++) {
|
||||
for (i = 0; tmpenv[i] && *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;
|
||||
@@ -1044,6 +1045,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
si.hStdOutput = winansi_get_osfhandle(fhout);
|
||||
si.hStdError = winansi_get_osfhandle(fherr);
|
||||
|
||||
/* executables and the current directory don't support long paths */
|
||||
if (xutftowcs_path(wcmd, cmd) < 0)
|
||||
return -1;
|
||||
if (dir && xutftowcs_path(wdir, dir) < 0)
|
||||
@@ -1066,6 +1068,16 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
free(quoted);
|
||||
}
|
||||
|
||||
if (getenv("GIT_STRACE_COMMANDS")) {
|
||||
char **path = get_path_split();
|
||||
cmd = path_lookup("strace.exe", path, 1);
|
||||
if (!cmd)
|
||||
return error("strace not found!");
|
||||
if (xutftowcs_path(wcmd, cmd) < 0)
|
||||
return -1;
|
||||
strbuf_insert(&args, 0, "strace ", 7);
|
||||
}
|
||||
|
||||
wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
|
||||
xutftowcs(wargs, args.buf, 2 * args.len + 1);
|
||||
strbuf_release(&args);
|
||||
@@ -1261,6 +1273,41 @@ static int compareenv(const void *v1, const void *v2)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions implemented outside Git are able to modify the environment,
|
||||
* too. For example, cURL's curl_global_init() function sets the CHARSET
|
||||
* environment variable (at least in certain circumstances).
|
||||
*
|
||||
* Therefore we need to be *really* careful *not* to assume that we have
|
||||
* sole control over the environment and reinitalize it when necessary.
|
||||
*/
|
||||
static void maybe_reinitialize_environ(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!saved_environ) {
|
||||
warning("MinGW environment not initialized yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (environ_size <= 0)
|
||||
return;
|
||||
|
||||
if (saved_environ != environ)
|
||||
/* We have *no* idea how much space was allocated outside */
|
||||
environ_alloc = 0;
|
||||
else if (!environ[environ_size - 1])
|
||||
return; /* still consistent */
|
||||
|
||||
for (i = 0; environ[i] && *environ[i]; i++)
|
||||
; /* continue counting */
|
||||
environ[i] = NULL;
|
||||
environ_size = i + 1;
|
||||
|
||||
/* sort environment for O(log n) getenv / putenv */
|
||||
qsort(environ, i, sizeof(char*), compareenv);
|
||||
}
|
||||
|
||||
static int bsearchenv(char **env, const char *name, size_t size)
|
||||
{
|
||||
unsigned low = 0, high = size;
|
||||
@@ -1284,7 +1331,7 @@ static int bsearchenv(char **env, const char *name, size_t size)
|
||||
*/
|
||||
static int do_putenv(char **env, const char *name, int size, int free_old)
|
||||
{
|
||||
int i = bsearchenv(env, name, size - 1);
|
||||
int i = size <= 0 ? -1 : bsearchenv(env, name, size - 1);
|
||||
|
||||
/* optionally free removed / replaced entry */
|
||||
if (i >= 0 && free_old)
|
||||
@@ -1309,7 +1356,14 @@ static int do_putenv(char **env, const char *name, int size, int free_old)
|
||||
char *mingw_getenv(const char *name)
|
||||
{
|
||||
char *value;
|
||||
int pos = bsearchenv(environ, name, environ_size - 1);
|
||||
int pos;
|
||||
|
||||
if (environ_size <= 0)
|
||||
return NULL;
|
||||
|
||||
maybe_reinitialize_environ();
|
||||
pos = bsearchenv(environ, name, environ_size - 1);
|
||||
|
||||
if (pos < 0)
|
||||
return NULL;
|
||||
value = strchr(environ[pos], '=');
|
||||
@@ -1318,7 +1372,9 @@ char *mingw_getenv(const char *name)
|
||||
|
||||
int mingw_putenv(const char *namevalue)
|
||||
{
|
||||
maybe_reinitialize_environ();
|
||||
ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
|
||||
saved_environ = environ;
|
||||
environ_size = do_putenv(environ, namevalue, environ_size, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -1619,8 +1675,9 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||
{
|
||||
DWORD attrs, gle;
|
||||
int tries = 0;
|
||||
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
|
||||
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
|
||||
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpold, pold) < 0 ||
|
||||
xutftowcs_long_path(wpnew, pnew) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -1639,7 +1696,12 @@ repeat:
|
||||
if (gle == ERROR_ACCESS_DENIED &&
|
||||
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
|
||||
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
errno = EISDIR;
|
||||
DWORD attrsold = GetFileAttributesW(wpold);
|
||||
if (attrsold == INVALID_FILE_ATTRIBUTES ||
|
||||
!(attrsold & FILE_ATTRIBUTE_DIRECTORY))
|
||||
errno = EISDIR;
|
||||
else if (!_wrmdir(wpnew))
|
||||
goto repeat;
|
||||
return -1;
|
||||
}
|
||||
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
|
||||
@@ -1884,7 +1946,8 @@ void mingw_open_html(const char *unixpath)
|
||||
die("cannot run browser");
|
||||
|
||||
printf("Launching default browser to display HTML ...\n");
|
||||
r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
|
||||
r = HCAST(int, ShellExecute(NULL, "open", htmlpath,
|
||||
NULL, "\\", SW_SHOWNORMAL));
|
||||
FreeLibrary(shell32);
|
||||
/* see the MSDN documentation referring to the result codes here */
|
||||
if (r <= 32) {
|
||||
@@ -1896,9 +1959,9 @@ 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)
|
||||
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
|
||||
xutftowcs_long_path(wnewpath, newpath) < 0)
|
||||
return -1;
|
||||
|
||||
if (!create_hard_link) {
|
||||
@@ -2079,6 +2142,124 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
|
||||
{
|
||||
int result;
|
||||
wchar_t buf[MAX_LONG_PATH];
|
||||
|
||||
/*
|
||||
* we don't need special handling if path is relative to the current
|
||||
* directory, and current directory + path don't exceed the desired
|
||||
* max_path limit. This should cover > 99 % of cases with minimal
|
||||
* performance impact (git almost always uses relative paths).
|
||||
*/
|
||||
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
|
||||
(current_directory_len + len < max_path))
|
||||
return len;
|
||||
|
||||
/*
|
||||
* handle everything else:
|
||||
* - absolute paths: "C:\dir\file"
|
||||
* - absolute UNC paths: "\\server\share\dir\file"
|
||||
* - absolute paths on current drive: "\dir\file"
|
||||
* - relative paths on other drive: "X:file"
|
||||
* - prefixed paths: "\\?\...", "\\.\..."
|
||||
*/
|
||||
|
||||
/* convert to absolute path using GetFullPathNameW */
|
||||
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
|
||||
if (!result) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* return absolute path if it fits within max_path (even if
|
||||
* "cwd + path" doesn't due to '..' components)
|
||||
*/
|
||||
if (result < max_path) {
|
||||
wcscpy(path, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* error out if we shouldn't expand the path or buf is too small */
|
||||
if (!expand || result >= MAX_LONG_PATH - 6) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* prefix full path with "\\?\" or "\\?\UNC\" */
|
||||
if (buf[0] == '\\') {
|
||||
/* ...unless already prefixed */
|
||||
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
|
||||
return len;
|
||||
|
||||
wcscpy(path, L"\\\\?\\UNC\\");
|
||||
wcscpy(path + 8, buf + 2);
|
||||
return result + 6;
|
||||
} else {
|
||||
wcscpy(path, L"\\\\?\\");
|
||||
wcscpy(path + 4, buf);
|
||||
return result + 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_windows_environment()
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
/* on Windows it is TMP and TEMP */
|
||||
if (!getenv("TMPDIR")) {
|
||||
if (!(tmp = getenv("TMP")))
|
||||
tmp = getenv("TEMP");
|
||||
if (tmp)
|
||||
setenv("TMPDIR", tmp, 1);
|
||||
}
|
||||
|
||||
if ((tmp = getenv("TMPDIR"))) {
|
||||
/*
|
||||
* Convert all dir separators to forward slashes,
|
||||
* to help shell commands called from the Git
|
||||
* executable (by not mistaking the dir separators
|
||||
* for escape characters).
|
||||
*/
|
||||
for (; *tmp; tmp++)
|
||||
if (*tmp == '\\')
|
||||
*tmp = '/';
|
||||
}
|
||||
|
||||
if (!getenv("TZ") && (tmp = getenv("MSYS2_TZ")))
|
||||
setenv("TZ", tmp, 1);
|
||||
|
||||
/* simulate TERM to enable auto-color (see color.c) */
|
||||
if (!getenv("TERM"))
|
||||
setenv("TERM", "cygwin", 1);
|
||||
|
||||
/* calculate HOME if not set */
|
||||
if (!getenv("HOME")) {
|
||||
/*
|
||||
* try $HOMEDRIVE$HOMEPATH - the home share may be a network
|
||||
* location, thus also check if the path exists (i.e. is not
|
||||
* disconnected)
|
||||
*/
|
||||
if ((tmp = getenv("HOMEDRIVE"))) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addstr(&buf, tmp);
|
||||
if ((tmp = getenv("HOMEPATH"))) {
|
||||
strbuf_addstr(&buf, tmp);
|
||||
if (is_directory(buf.buf))
|
||||
setenv("HOME", buf.buf, 1);
|
||||
else
|
||||
tmp = NULL; /* use $USERPROFILE */
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
/* use $USERPROFILE if the home share is not available */
|
||||
if (!tmp && (tmp = getenv("USERPROFILE")))
|
||||
setenv("HOME", tmp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
|
||||
* mingw startup code, see init.c in mingw runtime).
|
||||
@@ -2138,7 +2319,7 @@ void mingw_startup()
|
||||
*/
|
||||
environ_size = i + 1;
|
||||
environ_alloc = alloc_nr(environ_size * sizeof(char*));
|
||||
environ = malloc_startup(environ_alloc);
|
||||
saved_environ = environ = malloc_startup(environ_alloc);
|
||||
|
||||
/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
|
||||
maxlen = 3 * maxlen + 1;
|
||||
@@ -2157,20 +2338,19 @@ void mingw_startup()
|
||||
qsort(environ, i, sizeof(char*), compareenv);
|
||||
|
||||
/* fix Windows specific environment settings */
|
||||
setup_windows_environment();
|
||||
|
||||
/* on Windows it is TMP and TEMP */
|
||||
if (!mingw_getenv("TMPDIR")) {
|
||||
const char *tmp = mingw_getenv("TMP");
|
||||
if (!tmp)
|
||||
tmp = mingw_getenv("TEMP");
|
||||
if (tmp)
|
||||
setenv("TMPDIR", tmp, 1);
|
||||
/*
|
||||
* Avoid a segmentation fault when cURL tries to set the CHARSET
|
||||
* variable and putenv() barfs at our nedmalloc'ed environment.
|
||||
*/
|
||||
if (!getenv("CHARSET")) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addf(&buf, "cp%u", GetACP());
|
||||
setenv("CHARSET", buf.buf, 1);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
/* simulate TERM to enable auto-color (see color.c) */
|
||||
if (!getenv("TERM"))
|
||||
setenv("TERM", "cygwin", 1);
|
||||
|
||||
/* initialize critical section for waitpid pinfo_t list */
|
||||
InitializeCriticalSection(&pinfo_cs);
|
||||
|
||||
@@ -2182,6 +2362,9 @@ void mingw_startup()
|
||||
|
||||
/* initialize Unicode console */
|
||||
winansi_init();
|
||||
|
||||
/* init length of current directory for handle_long_path */
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
}
|
||||
|
||||
int uname(struct utsname *buf)
|
||||
|
||||
122
compat/mingw.h
122
compat/mingw.h
@@ -1,27 +1,34 @@
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef _POSIX
|
||||
typedef _sigset_t sigset_t;
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
/*
|
||||
* things that are not available in header files
|
||||
*/
|
||||
|
||||
typedef int pid_t;
|
||||
typedef int uid_t;
|
||||
typedef int socklen_t;
|
||||
#define hstrerror strerror
|
||||
|
||||
#define S_IFLNK 0120000 /* Symbolic link */
|
||||
#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISSOCK(x) 0
|
||||
|
||||
#ifndef S_IRWXG
|
||||
#define S_IRGRP 0
|
||||
#define S_IWGRP 0
|
||||
#define S_IXGRP 0
|
||||
#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
|
||||
#endif
|
||||
#ifndef S_IRWXO
|
||||
#define S_IROTH 0
|
||||
#define S_IWOTH 0
|
||||
#define S_IXOTH 0
|
||||
#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
|
||||
#endif
|
||||
|
||||
#define S_ISUID 0004000
|
||||
#define S_ISGID 0002000
|
||||
@@ -100,8 +107,10 @@ static inline int symlink(const char *oldpath, const char *newpath)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
static inline int fchmod(int fildes, mode_t mode)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
#ifndef __MINGW64_VERSION_MAJOR
|
||||
static inline pid_t fork(void)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
#endif
|
||||
static inline unsigned int alarm(unsigned int seconds)
|
||||
{ return 0; }
|
||||
static inline int fsync(int fd)
|
||||
@@ -176,8 +185,10 @@ int pipe(int filedes[2]);
|
||||
unsigned int sleep (unsigned int seconds);
|
||||
int mkstemp(char *template);
|
||||
int gettimeofday(struct timeval *tv, void *tz);
|
||||
#ifndef __MINGW64_VERSION_MAJOR
|
||||
struct tm *gmtime_r(const time_t *timep, struct tm *result);
|
||||
struct tm *localtime_r(const time_t *timep, struct tm *result);
|
||||
#endif
|
||||
int getpagesize(void); /* defined in MinGW's libgcc.a */
|
||||
struct passwd *getpwuid(uid_t uid);
|
||||
int setitimer(int type, struct itimerval *in, struct itimerval *out);
|
||||
@@ -226,6 +237,10 @@ 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 MinGW startup code!"
|
||||
#endif
|
||||
|
||||
char *mingw_getenv(const char *name);
|
||||
#define getenv mingw_getenv
|
||||
int mingw_putenv(const char *namevalue);
|
||||
@@ -295,11 +310,29 @@ static inline int getrlimit(int resource, struct rlimit *rlp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
||||
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
||||
*/
|
||||
static inline long long filetime_to_hnsec(const FILETIME *ft)
|
||||
{
|
||||
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
|
||||
/* Windows to Unix Epoch conversion */
|
||||
return winTime - 116444736000000000LL;
|
||||
}
|
||||
|
||||
static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
{
|
||||
return (time_t)(filetime_to_hnsec(ft) / 10000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use mingw specific stat()/lstat()/fstat() implementations on Windows.
|
||||
*/
|
||||
#ifndef __MINGW64_VERSION_MAJOR
|
||||
#define off_t off64_t
|
||||
#define lseek _lseeki64
|
||||
#endif
|
||||
|
||||
/* use struct stat with 64 bit st_size */
|
||||
#ifdef stat
|
||||
@@ -316,7 +349,7 @@ int mingw_fstat(int fd, struct stat *buf);
|
||||
#ifdef lstat
|
||||
#undef lstat
|
||||
#endif
|
||||
#define lstat mingw_lstat
|
||||
extern int (*lstat)(const char *file_name, struct stat *buf);
|
||||
|
||||
#ifndef _stati64
|
||||
# define _stati64(x,y) mingw_stat(x,y)
|
||||
@@ -372,8 +405,12 @@ static inline char *mingw_find_last_dir_sep(const char *path)
|
||||
int mingw_offset_1st_component(const char *path);
|
||||
#define offset_1st_component mingw_offset_1st_component
|
||||
#define PATH_SEP ';'
|
||||
#ifndef __MINGW64_VERSION_MAJOR
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRId64 "I64d"
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
void mingw_open_html(const char *path);
|
||||
#define open_html mingw_open_html
|
||||
@@ -381,6 +418,42 @@ void mingw_open_html(const char *path);
|
||||
void mingw_mark_as_git_dir(const char *dir);
|
||||
#define mark_as_git_dir mingw_mark_as_git_dir
|
||||
|
||||
/**
|
||||
* Max length of long paths (exceeding MAX_PATH). The actual maximum supported
|
||||
* by NTFS is 32,767 (* sizeof(wchar_t)), but we choose an arbitrary smaller
|
||||
* value to limit required stack memory.
|
||||
*/
|
||||
#define MAX_LONG_PATH 4096
|
||||
|
||||
/**
|
||||
* Handles paths that would exceed the MAX_PATH limit of Windows Unicode APIs.
|
||||
*
|
||||
* With expand == false, the function checks for over-long paths and fails
|
||||
* with ENAMETOOLONG. The path parameter is not modified, except if cwd + path
|
||||
* exceeds max_path, but the resulting absolute path doesn't (e.g. due to
|
||||
* eliminating '..' components). The path parameter must point to a buffer
|
||||
* of max_path wide characters.
|
||||
*
|
||||
* With expand == true, an over-long path is automatically converted in place
|
||||
* to an absolute path prefixed with '\\?\', and the new length is returned.
|
||||
* The path parameter must point to a buffer of MAX_LONG_PATH wide characters.
|
||||
*
|
||||
* Parameters:
|
||||
* path: path to check and / or convert
|
||||
* len: size of path on input (number of wide chars without \0)
|
||||
* max_path: max short path length to check (usually MAX_PATH = 260, but just
|
||||
* 248 for CreateDirectoryW)
|
||||
* expand: false to only check the length, true to expand the path to a
|
||||
* '\\?\'-prefixed absolute path
|
||||
*
|
||||
* Return:
|
||||
* length of the resulting path, or -1 on failure
|
||||
*
|
||||
* Errors:
|
||||
* ENAMETOOLONG if path is too long
|
||||
*/
|
||||
int handle_long_path(wchar_t *path, int len, int max_path, int expand);
|
||||
|
||||
/**
|
||||
* Converts UTF-8 encoded string to UTF-16LE.
|
||||
*
|
||||
@@ -438,17 +511,48 @@ static inline int xutftowcs(wchar_t *wcs, const char *utf, size_t wcslen)
|
||||
return xutftowcsn(wcs, utf, wcslen, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified file system specific wrapper of xutftowcsn and handle_long_path.
|
||||
* Converts ERANGE to ENAMETOOLONG. If expand is true, wcs must be at least
|
||||
* MAX_LONG_PATH wide chars (see handle_long_path).
|
||||
*/
|
||||
static inline int xutftowcs_path_ex(wchar_t *wcs, const char *utf,
|
||||
size_t wcslen, int utflen, int max_path, int expand)
|
||||
{
|
||||
int result = xutftowcsn(wcs, utf, wcslen, utflen);
|
||||
if (result < 0 && errno == ERANGE)
|
||||
errno = ENAMETOOLONG;
|
||||
if (result >= 0)
|
||||
result = handle_long_path(wcs, result, max_path, expand);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified file system specific variant of xutftowcsn, assumes output
|
||||
* buffer size is MAX_PATH wide chars and input string is \0-terminated,
|
||||
* fails with ENAMETOOLONG if input string is too long.
|
||||
* fails with ENAMETOOLONG if input string is too long. Typically used for
|
||||
* Windows APIs that don't support long paths, e.g. SetCurrentDirectory,
|
||||
* LoadLibrary, CreateProcess...
|
||||
*/
|
||||
static inline int xutftowcs_path(wchar_t *wcs, const char *utf)
|
||||
{
|
||||
int result = xutftowcsn(wcs, utf, MAX_PATH, -1);
|
||||
if (result < 0 && errno == ERANGE)
|
||||
errno = ENAMETOOLONG;
|
||||
return result;
|
||||
return xutftowcs_path_ex(wcs, utf, MAX_PATH, -1, MAX_PATH, 0);
|
||||
}
|
||||
|
||||
/* need to re-declare that here as mingw.h is included before cache.h */
|
||||
extern int core_long_paths;
|
||||
|
||||
/**
|
||||
* Simplified file system specific variant of xutftowcsn for Windows APIs
|
||||
* that support long paths via '\\?\'-prefix, assumes output buffer size is
|
||||
* MAX_LONG_PATH wide chars, fails with ENAMETOOLONG if input string is too
|
||||
* long. The 'core.longpaths' git-config option controls whether the path
|
||||
* is only checked or expanded to a long path.
|
||||
*/
|
||||
static inline int xutftowcs_long_path(wchar_t *wcs, const char *utf)
|
||||
{
|
||||
return xutftowcs_path_ex(wcs, utf, MAX_LONG_PATH, -1, MAX_PATH,
|
||||
core_long_paths);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -720,6 +720,9 @@ struct mallinfo {
|
||||
inlining are defined as macros, so these aren't used for them.
|
||||
*/
|
||||
|
||||
#ifdef __MINGW64_VERSION_MAJOR
|
||||
#undef FORCEINLINE
|
||||
#endif
|
||||
#ifndef FORCEINLINE
|
||||
#if defined(__GNUC__)
|
||||
#define FORCEINLINE __inline __attribute__ ((always_inline))
|
||||
@@ -1382,6 +1385,9 @@ LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value);
|
||||
|
||||
/*** Atomic operations ***/
|
||||
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
|
||||
#ifdef __MINGW64_VERSION_MAJOR
|
||||
#undef _ReadWriteBarrier
|
||||
#endif
|
||||
#define _ReadWriteBarrier() __sync_synchronize()
|
||||
#else
|
||||
static __inline__ __attribute__((always_inline)) long __sync_lock_test_and_set(volatile long * const Target, const long Value)
|
||||
@@ -1798,9 +1804,10 @@ struct win32_mlock_t
|
||||
volatile long threadid;
|
||||
};
|
||||
|
||||
static inline int return_0(int i) { return 0; }
|
||||
#define MLOCK_T struct win32_mlock_t
|
||||
#define CURRENT_THREAD win32_getcurrentthreadid()
|
||||
#define INITIAL_LOCK(sl) (memset(sl, 0, sizeof(MLOCK_T)), 0)
|
||||
#define INITIAL_LOCK(sl) (memset(sl, 0, sizeof(MLOCK_T)), return_0(0))
|
||||
#define ACQUIRE_LOCK(sl) win32_acquire_lock(sl)
|
||||
#define RELEASE_LOCK(sl) win32_release_lock(sl)
|
||||
#define TRY_LOCK(sl) win32_try_lock(sl)
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
#ifdef WIN32_NATIVE
|
||||
|
||||
#define IsConsoleHandle(h) (((long) (h) & 3) == 3)
|
||||
#define IsConsoleHandle(h) (((long) (intptr_t) (h) & 3) == 3)
|
||||
|
||||
static BOOL
|
||||
IsSocketHandle (HANDLE h)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <inttypes.h>
|
||||
#include "git-compat-util.h"
|
||||
#include "run-command.h"
|
||||
#include "compat/terminal.h"
|
||||
#include "sigchain.h"
|
||||
#include "strbuf.h"
|
||||
#include "cache.h"
|
||||
|
||||
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
||||
|
||||
@@ -91,6 +94,55 @@ static int disable_echo(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *shell_prompt(const char *prompt, int echo)
|
||||
{
|
||||
const char *read_input[] = {
|
||||
/* Note: call 'bash' explicitly, as 'read -s' is bash-specific */
|
||||
"bash", "-c", echo ?
|
||||
"cat >/dev/tty && read -r line </dev/tty && echo \"$line\"" :
|
||||
"cat >/dev/tty && read -r -s line </dev/tty && echo \"$line\" && echo >/dev/tty",
|
||||
NULL
|
||||
};
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
static struct strbuf buffer = STRBUF_INIT;
|
||||
int prompt_len = strlen(prompt), len = -1, code;
|
||||
|
||||
child.argv = read_input;
|
||||
child.in = -1;
|
||||
child.out = -1;
|
||||
child.silent_exec_failure = 1;
|
||||
|
||||
if (start_command(&child))
|
||||
return NULL;
|
||||
|
||||
if (write_in_full(child.in, prompt, prompt_len) != prompt_len) {
|
||||
error("could not write to prompt script");
|
||||
close(child.in);
|
||||
goto ret;
|
||||
}
|
||||
close(child.in);
|
||||
|
||||
strbuf_reset(&buffer);
|
||||
len = strbuf_read(&buffer, child.out, 1024);
|
||||
if (len < 0) {
|
||||
error("could not read from prompt script");
|
||||
goto ret;
|
||||
}
|
||||
|
||||
strbuf_strip_suffix(&buffer, "\n");
|
||||
strbuf_strip_suffix(&buffer, "\r");
|
||||
|
||||
ret:
|
||||
close(child.out);
|
||||
code = finish_command(&child);
|
||||
if (code) {
|
||||
error("failed to execute prompt script (exit code %d)", code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return len < 0 ? NULL : buffer.buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef FORCE_TEXT
|
||||
@@ -103,6 +155,15 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
||||
int r;
|
||||
FILE *input_fh, *output_fh;
|
||||
|
||||
#ifdef GIT_WINDOWS_NATIVE
|
||||
|
||||
/* try shell_prompt first, fall back to CONIN/OUT if bash is missing */
|
||||
char *result = shell_prompt(prompt, echo);
|
||||
if (result || errno != ENOENT)
|
||||
return result;
|
||||
|
||||
#endif
|
||||
|
||||
input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
|
||||
if (!input_fh)
|
||||
return NULL;
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#include "../../git-compat-util.h"
|
||||
|
||||
struct DIR {
|
||||
typedef struct dirent_DIR {
|
||||
struct DIR base_dir; /* extend base struct DIR */
|
||||
struct dirent dd_dir; /* includes d_type */
|
||||
HANDLE dd_handle; /* FindFirstFile handle */
|
||||
int dd_stat; /* 0-based index */
|
||||
};
|
||||
char dd_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
} dirent_DIR;
|
||||
|
||||
DIR *(*opendir)(const char *dirname) = dirent_opendir;
|
||||
|
||||
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
/* convert UTF-16 name to UTF-8 */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
|
||||
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
|
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */
|
||||
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
@@ -18,41 +22,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
ent->d_type = DT_REG;
|
||||
}
|
||||
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length < MAX_PATH */
|
||||
if ((len = xutftowcs_path(pattern, name)) < 0)
|
||||
return NULL;
|
||||
|
||||
/* append optional '/' and wildcard '*' */
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '/';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(DIR));
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return dir;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dir)
|
||||
static struct dirent *dirent_readdir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF; /* No set_errno for mingw */
|
||||
@@ -79,7 +49,7 @@ struct dirent *readdir(DIR *dir)
|
||||
return &dir->dd_dir;
|
||||
}
|
||||
|
||||
int closedir(DIR *dir)
|
||||
static int dirent_closedir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF;
|
||||
@@ -90,3 +60,44 @@ int closedir(DIR *dir)
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR *dirent_opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
dirent_DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length */
|
||||
if ((len = xutftowcs_path_ex(pattern, name, MAX_LONG_PATH, -1,
|
||||
MAX_PATH - 2, core_long_paths)) < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* append optional '\' and wildcard '*'. Note: we need to use '\' as
|
||||
* Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
|
||||
*/
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '\\';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(dirent_DIR));
|
||||
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
|
||||
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
|
||||
dir->dd_dir.d_name = dir->dd_name;
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return (DIR*) dir;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
#ifndef DIRENT_H
|
||||
#define DIRENT_H
|
||||
|
||||
typedef struct DIR DIR;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_DIR 1
|
||||
#define DT_REG 2
|
||||
#define DT_LNK 3
|
||||
|
||||
struct dirent {
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char *d_name; /* file name */
|
||||
};
|
||||
|
||||
DIR *opendir(const char *dirname);
|
||||
struct dirent *readdir(DIR *dir);
|
||||
int closedir(DIR *dir);
|
||||
/*
|
||||
* Base DIR structure, contains pointers to readdir/closedir implementations so
|
||||
* that opendir may choose a concrete implementation on a call-by-call basis.
|
||||
*/
|
||||
typedef struct DIR {
|
||||
struct dirent *(*preaddir)(struct DIR *dir);
|
||||
int (*pclosedir)(struct DIR *dir);
|
||||
} DIR;
|
||||
|
||||
/* default dirent implementation */
|
||||
extern DIR *dirent_opendir(const char *dirname);
|
||||
|
||||
/* current dirent implementation */
|
||||
extern DIR *(*opendir)(const char *dirname);
|
||||
|
||||
#define readdir(dir) (dir->preaddir(dir))
|
||||
#define closedir(dir) (dir->pclosedir(dir))
|
||||
|
||||
#endif /* DIRENT_H */
|
||||
|
||||
506
compat/win32/fscache.c
Normal file
506
compat/win32/fscache.c
Normal file
@@ -0,0 +1,506 @@
|
||||
#include "../../cache.h"
|
||||
#include "../../hashmap.h"
|
||||
#include "../win32.h"
|
||||
#include "fscache.h"
|
||||
|
||||
static int initialized;
|
||||
static volatile long enabled;
|
||||
static struct hashmap map;
|
||||
static CRITICAL_SECTION mutex;
|
||||
|
||||
/*
|
||||
* An entry in the file system cache. Used for both entire directory listings
|
||||
* and file entries.
|
||||
*/
|
||||
struct fsentry {
|
||||
struct hashmap_entry ent;
|
||||
mode_t st_mode;
|
||||
/* Length of name. */
|
||||
unsigned short len;
|
||||
/*
|
||||
* Name of the entry. For directory listings: relative path of the
|
||||
* directory, without trailing '/' (empty for cwd()). For file entries:
|
||||
* name of the file. Typically points to the end of the structure if
|
||||
* the fsentry is allocated on the heap (see fsentry_alloc), or to a
|
||||
* local variable if on the stack (see fsentry_init).
|
||||
*/
|
||||
const char *name;
|
||||
/* Pointer to the directory listing, or NULL for the listing itself. */
|
||||
struct fsentry *list;
|
||||
/* Pointer to the next file entry of the list. */
|
||||
struct fsentry *next;
|
||||
|
||||
union {
|
||||
/* Reference count of the directory listing. */
|
||||
volatile long refcnt;
|
||||
/* Handle to wait on the loading thread. */
|
||||
HANDLE hwait;
|
||||
struct {
|
||||
/* More stat members (only used for file entries). */
|
||||
off64_t st_size;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Compares the paths of two fsentry structures for equality.
|
||||
*/
|
||||
static int fsentry_cmp(const struct fsentry *fse1, const struct fsentry *fse2)
|
||||
{
|
||||
int res;
|
||||
if (fse1 == fse2)
|
||||
return 0;
|
||||
|
||||
/* compare the list parts first */
|
||||
if (fse1->list != fse2->list && (res = fsentry_cmp(
|
||||
fse1->list ? fse1->list : fse1,
|
||||
fse2->list ? fse2->list : fse2)))
|
||||
return res;
|
||||
|
||||
/* if list parts are equal, compare len and name */
|
||||
if (fse1->len != fse2->len)
|
||||
return fse1->len - fse2->len;
|
||||
return strnicmp(fse1->name, fse2->name, fse1->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the hash code of an fsentry structure's path.
|
||||
*/
|
||||
static unsigned int fsentry_hash(const struct fsentry *fse)
|
||||
{
|
||||
unsigned int hash = fse->list ? fse->list->ent.hash : 0;
|
||||
return hash ^ memihash(fse->name, fse->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an fsentry structure for use by fsentry_hash and fsentry_cmp.
|
||||
*/
|
||||
static void fsentry_init(struct fsentry *fse, struct fsentry *list,
|
||||
const char *name, size_t len)
|
||||
{
|
||||
fse->list = list;
|
||||
fse->name = name;
|
||||
fse->len = len;
|
||||
hashmap_entry_init(fse, fsentry_hash(fse));
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an fsentry structure on the heap.
|
||||
*/
|
||||
static struct fsentry *fsentry_alloc(struct fsentry *list, const char *name,
|
||||
size_t len)
|
||||
{
|
||||
/* overallocate fsentry and copy the name to the end */
|
||||
struct fsentry *fse = xmalloc(sizeof(struct fsentry) + len + 1);
|
||||
char *nm = ((char*) fse) + sizeof(struct fsentry);
|
||||
memcpy(nm, name, len);
|
||||
nm[len] = 0;
|
||||
/* init the rest of the structure */
|
||||
fsentry_init(fse, list, nm, len);
|
||||
fse->next = NULL;
|
||||
fse->refcnt = 1;
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a reference to an fsentry.
|
||||
*/
|
||||
inline static void fsentry_addref(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
InterlockedIncrement(&(fse->refcnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the reference to an fsentry, frees the memory if its the last ref.
|
||||
*/
|
||||
static void fsentry_release(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
if (InterlockedDecrement(&(fse->refcnt)))
|
||||
return;
|
||||
|
||||
while (fse) {
|
||||
struct fsentry *next = fse->next;
|
||||
free(fse);
|
||||
fse = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
|
||||
*/
|
||||
static struct fsentry *fseentry_create_entry(struct fsentry *list,
|
||||
const WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
char buf[MAX_PATH * 3];
|
||||
int len;
|
||||
struct fsentry *fse;
|
||||
len = xwcstoutf(buf, fdata->cFileName, ARRAY_SIZE(buf));
|
||||
|
||||
fse = fsentry_alloc(list, buf, len);
|
||||
|
||||
fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
|
||||
fse->st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
|
||||
| fdata->nFileSizeLow;
|
||||
fse->st_atime = filetime_to_time_t(&(fdata->ftLastAccessTime));
|
||||
fse->st_mtime = filetime_to_time_t(&(fdata->ftLastWriteTime));
|
||||
fse->st_ctime = filetime_to_time_t(&(fdata->ftCreationTime));
|
||||
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an fsentry-based directory listing (similar to opendir / readdir).
|
||||
* Dir should not contain trailing '/'. Use an empty string for the current
|
||||
* directory (not "."!).
|
||||
*/
|
||||
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
|
||||
{
|
||||
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int wlen;
|
||||
struct fsentry *list, **phead;
|
||||
DWORD err;
|
||||
|
||||
/* convert name to UTF-16 and check length */
|
||||
if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH,
|
||||
dir->len, MAX_PATH - 2, core_long_paths)) < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* append optional '\' and wildcard '*'. Note: we need to use '\' as
|
||||
* Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
|
||||
*/
|
||||
if (wlen)
|
||||
pattern[wlen++] = '\\';
|
||||
pattern[wlen++] = '*';
|
||||
pattern[wlen] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate object to hold directory listing */
|
||||
list = fsentry_alloc(NULL, dir->name, dir->len);
|
||||
|
||||
/* walk directory and build linked list of fsentry structures */
|
||||
phead = &list->next;
|
||||
do {
|
||||
*phead = fseentry_create_entry(list, &fdata);
|
||||
phead = &(*phead)->next;
|
||||
} while (FindNextFileW(h, &fdata));
|
||||
|
||||
/* remember result of last FindNextFile, then close find handle */
|
||||
err = GetLastError();
|
||||
FindClose(h);
|
||||
|
||||
/* return the list if we've got all the files */
|
||||
if (err == ERROR_NO_MORE_FILES)
|
||||
return list;
|
||||
|
||||
/* otherwise free the list and return error */
|
||||
fsentry_release(list);
|
||||
errno = err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a directory listing to the cache.
|
||||
*/
|
||||
static void fscache_add(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
for (; fse; fse = fse->next)
|
||||
hashmap_add(&map, fse);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a directory listing from the cache.
|
||||
*/
|
||||
static void fscache_remove(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
for (; fse; fse = fse->next)
|
||||
hashmap_remove(&map, fse, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the cache.
|
||||
*/
|
||||
static void fscache_clear()
|
||||
{
|
||||
struct hashmap_iter iter;
|
||||
struct fsentry *fse;
|
||||
while ((fse = hashmap_iter_first(&map, &iter))) {
|
||||
fscache_remove(fse);
|
||||
fsentry_release(fse);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the cache is enabled for the given path.
|
||||
*/
|
||||
static inline int fscache_enabled(const char *path)
|
||||
{
|
||||
return enabled > 0 && !is_absolute_path(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up a cache entry, waits if its being loaded by another thread.
|
||||
* The mutex must be owned by the calling thread.
|
||||
*/
|
||||
static struct fsentry *fscache_get_wait(struct fsentry *key)
|
||||
{
|
||||
struct fsentry *fse = hashmap_get(&map, key, NULL);
|
||||
|
||||
/* return if its a 'real' entry (future entries have refcnt == 0) */
|
||||
if (!fse || fse->list || fse->refcnt)
|
||||
return fse;
|
||||
|
||||
/* create an event and link our key to the future entry */
|
||||
key->hwait = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
key->next = fse->next;
|
||||
fse->next = key;
|
||||
|
||||
/* wait for the loading thread to signal us */
|
||||
LeaveCriticalSection(&mutex);
|
||||
WaitForSingleObject(key->hwait, INFINITE);
|
||||
CloseHandle(key->hwait);
|
||||
EnterCriticalSection(&mutex);
|
||||
|
||||
/* repeat cache lookup */
|
||||
return hashmap_get(&map, key, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up or creates a cache entry for the specified key.
|
||||
*/
|
||||
static struct fsentry *fscache_get(struct fsentry *key)
|
||||
{
|
||||
struct fsentry *fse, *future, *waiter;
|
||||
|
||||
EnterCriticalSection(&mutex);
|
||||
/* check if entry is in cache */
|
||||
fse = fscache_get_wait(key);
|
||||
if (fse) {
|
||||
fsentry_addref(fse);
|
||||
LeaveCriticalSection(&mutex);
|
||||
return fse;
|
||||
}
|
||||
/* if looking for a file, check if directory listing is in cache */
|
||||
if (!fse && key->list) {
|
||||
fse = fscache_get_wait(key->list);
|
||||
if (fse) {
|
||||
LeaveCriticalSection(&mutex);
|
||||
/* dir entry without file entry -> file doesn't exist */
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* add future entry to indicate that we're loading it */
|
||||
future = key->list ? key->list : key;
|
||||
future->next = NULL;
|
||||
future->refcnt = 0;
|
||||
hashmap_add(&map, future);
|
||||
|
||||
/* create the directory listing (outside mutex!) */
|
||||
LeaveCriticalSection(&mutex);
|
||||
fse = fsentry_create_list(future);
|
||||
EnterCriticalSection(&mutex);
|
||||
|
||||
/* remove future entry and signal waiting threads */
|
||||
hashmap_remove(&map, future, NULL);
|
||||
waiter = future->next;
|
||||
while (waiter) {
|
||||
HANDLE h = waiter->hwait;
|
||||
waiter = waiter->next;
|
||||
SetEvent(h);
|
||||
}
|
||||
|
||||
/* leave on error (errno set by fsentry_create_list) */
|
||||
if (!fse) {
|
||||
LeaveCriticalSection(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* add directory listing to the cache */
|
||||
fscache_add(fse);
|
||||
|
||||
/* lookup file entry if requested (fse already points to directory) */
|
||||
if (key->list)
|
||||
fse = hashmap_get(&map, key, NULL);
|
||||
|
||||
/* return entry or ENOENT */
|
||||
if (fse)
|
||||
fsentry_addref(fse);
|
||||
else
|
||||
errno = ENOENT;
|
||||
|
||||
LeaveCriticalSection(&mutex);
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables or disables the cache. Note that the cache is read-only, changes to
|
||||
* the working directory are NOT reflected in the cache while enabled.
|
||||
*/
|
||||
int fscache_enable(int enable)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!initialized) {
|
||||
/* allow the cache to be disabled entirely */
|
||||
if (!core_fscache)
|
||||
return 0;
|
||||
|
||||
InitializeCriticalSection(&mutex);
|
||||
hashmap_init(&map, (hashmap_cmp_fn) fsentry_cmp, 0);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
result = enable ? InterlockedIncrement(&enabled)
|
||||
: InterlockedDecrement(&enabled);
|
||||
|
||||
if (enable && result == 1) {
|
||||
/* redirect opendir and lstat to the fscache implementations */
|
||||
opendir = fscache_opendir;
|
||||
lstat = fscache_lstat;
|
||||
} else if (!enable && !result) {
|
||||
/* reset opendir and lstat to the original implementations */
|
||||
opendir = dirent_opendir;
|
||||
lstat = mingw_lstat;
|
||||
EnterCriticalSection(&mutex);
|
||||
fscache_clear();
|
||||
LeaveCriticalSection(&mutex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lstat replacement, uses the cache if enabled, otherwise redirects to
|
||||
* mingw_lstat.
|
||||
*/
|
||||
int fscache_lstat(const char *filename, struct stat *st)
|
||||
{
|
||||
int dirlen, base, len;
|
||||
struct fsentry key[2], *fse;
|
||||
|
||||
if (!fscache_enabled(filename))
|
||||
return mingw_lstat(filename, st);
|
||||
|
||||
/* split filename into path + name */
|
||||
len = strlen(filename);
|
||||
if (len && is_dir_sep(filename[len - 1]))
|
||||
len--;
|
||||
base = len;
|
||||
while (base && !is_dir_sep(filename[base - 1]))
|
||||
base--;
|
||||
dirlen = base ? base - 1 : 0;
|
||||
|
||||
/* lookup entry for path + name in cache */
|
||||
fsentry_init(key, NULL, filename, dirlen);
|
||||
fsentry_init(key + 1, key, filename + base, len - base);
|
||||
fse = fscache_get(key + 1);
|
||||
if (!fse)
|
||||
return -1;
|
||||
|
||||
/* copy stat data */
|
||||
st->st_ino = 0;
|
||||
st->st_gid = 0;
|
||||
st->st_uid = 0;
|
||||
st->st_dev = 0;
|
||||
st->st_rdev = 0;
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = fse->st_mode;
|
||||
st->st_size = fse->st_size;
|
||||
st->st_atime = fse->st_atime;
|
||||
st->st_mtime = fse->st_mtime;
|
||||
st->st_ctime = fse->st_ctime;
|
||||
|
||||
/* don't forget to release fsentry */
|
||||
fsentry_release(fse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct fscache_DIR {
|
||||
struct DIR base_dir; /* extend base struct DIR */
|
||||
struct fsentry *pfsentry;
|
||||
struct dirent dirent;
|
||||
} fscache_DIR;
|
||||
|
||||
/*
|
||||
* Readdir replacement.
|
||||
*/
|
||||
static struct dirent *fscache_readdir(DIR *base_dir)
|
||||
{
|
||||
fscache_DIR *dir = (fscache_DIR*) base_dir;
|
||||
struct fsentry *next = dir->pfsentry->next;
|
||||
if (!next)
|
||||
return NULL;
|
||||
dir->pfsentry = next;
|
||||
dir->dirent.d_type = S_ISDIR(next->st_mode) ? DT_DIR : DT_REG;
|
||||
dir->dirent.d_name = (char*) next->name;
|
||||
return &(dir->dirent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closedir replacement.
|
||||
*/
|
||||
static int fscache_closedir(DIR *base_dir)
|
||||
{
|
||||
fscache_DIR *dir = (fscache_DIR*) base_dir;
|
||||
fsentry_release(dir->pfsentry);
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Opendir replacement, uses a directory listing from the cache if enabled,
|
||||
* otherwise calls original dirent implementation.
|
||||
*/
|
||||
DIR *fscache_opendir(const char *dirname)
|
||||
{
|
||||
struct fsentry key, *list;
|
||||
fscache_DIR *dir;
|
||||
int len;
|
||||
|
||||
if (!fscache_enabled(dirname))
|
||||
return dirent_opendir(dirname);
|
||||
|
||||
/* prepare name (strip trailing '/', replace '.') */
|
||||
len = strlen(dirname);
|
||||
if ((len == 1 && dirname[0] == '.') ||
|
||||
(len && is_dir_sep(dirname[len - 1])))
|
||||
len--;
|
||||
|
||||
/* get directory listing from cache */
|
||||
fsentry_init(&key, NULL, dirname, len);
|
||||
list = fscache_get(&key);
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
/* alloc and return DIR structure */
|
||||
dir = (fscache_DIR*) xmalloc(sizeof(fscache_DIR));
|
||||
dir->base_dir.preaddir = fscache_readdir;
|
||||
dir->base_dir.pclosedir = fscache_closedir;
|
||||
dir->pfsentry = list;
|
||||
return (DIR*) dir;
|
||||
}
|
||||
10
compat/win32/fscache.h
Normal file
10
compat/win32/fscache.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef FSCACHE_H
|
||||
#define FSCACHE_H
|
||||
|
||||
int fscache_enable(int enable);
|
||||
#define enable_fscache(x) fscache_enable(x)
|
||||
|
||||
DIR *fscache_opendir(const char *dir);
|
||||
int fscache_lstat(const char *file_name, struct stat *buf);
|
||||
|
||||
#endif
|
||||
25
compat/win32/git.manifest
Normal file
25
compat/win32/git.manifest
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity type="win32" name="Git" version="0.0.0.1" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -18,7 +18,10 @@
|
||||
*/
|
||||
#define pthread_mutex_t CRITICAL_SECTION
|
||||
|
||||
#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
|
||||
static inline int return_0(int i) {
|
||||
return 0;
|
||||
}
|
||||
#define pthread_mutex_init(a,b) return_0((InitializeCriticalSection((a)), 0))
|
||||
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
|
||||
#define pthread_mutex_lock EnterCriticalSection
|
||||
#define pthread_mutex_unlock LeaveCriticalSection
|
||||
@@ -77,7 +80,7 @@ extern pthread_t pthread_self(void);
|
||||
|
||||
static inline int pthread_exit(void *ret)
|
||||
{
|
||||
ExitThread((DWORD)ret);
|
||||
ExitThread((DWORD)(size_t)ret);
|
||||
}
|
||||
|
||||
typedef DWORD pthread_key_t;
|
||||
|
||||
@@ -23,6 +23,7 @@ static HANDLE hthread, hread, hwrite;
|
||||
static HANDLE hconsole1, hconsole2;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5
|
||||
typedef struct _CONSOLE_FONT_INFOEX {
|
||||
ULONG cbSize;
|
||||
DWORD nFont;
|
||||
@@ -32,6 +33,7 @@ typedef struct _CONSOLE_FONT_INFOEX {
|
||||
WCHAR FaceName[LF_FACESIZE];
|
||||
} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
|
||||
PCONSOLE_FONT_INFOEX);
|
||||
@@ -452,7 +454,8 @@ static HANDLE duplicate_handle(HANDLE hnd)
|
||||
HANDLE hresult, hproc = GetCurrentProcess();
|
||||
if (!DuplicateHandle(hproc, hnd, hproc, &hresult, 0, TRUE,
|
||||
DUPLICATE_SAME_ACCESS))
|
||||
die_lasterr("DuplicateHandle(%li) failed", (long) hnd);
|
||||
die_lasterr("DuplicateHandle(%li) failed",
|
||||
(long) (intptr_t) hnd);
|
||||
return hresult;
|
||||
}
|
||||
|
||||
@@ -480,6 +483,7 @@ static size_t sizeof_ioinfo = 0;
|
||||
#define IOINFO_L2E 5
|
||||
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
|
||||
|
||||
#define FPIPE 0x08
|
||||
#define FDEV 0x40
|
||||
|
||||
static inline ioinfo* _pioinfo(int fd)
|
||||
@@ -527,6 +531,45 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
|
||||
return old_handle;
|
||||
}
|
||||
|
||||
#ifdef DETECT_MSYS_TTY
|
||||
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
|
||||
static void detect_msys_tty(int fd)
|
||||
{
|
||||
ULONG result;
|
||||
BYTE buffer[1024];
|
||||
POBJECT_NAME_INFORMATION nameinfo = (POBJECT_NAME_INFORMATION) buffer;
|
||||
PWSTR name;
|
||||
|
||||
/* check if fd is a pipe */
|
||||
HANDLE h = (HANDLE) _get_osfhandle(fd);
|
||||
if (GetFileType(h) != FILE_TYPE_PIPE)
|
||||
return;
|
||||
|
||||
/* get pipe name */
|
||||
if (!NT_SUCCESS(NtQueryObject(h, ObjectNameInformation,
|
||||
buffer, sizeof(buffer) - 2, &result)))
|
||||
return;
|
||||
name = nameinfo->Name.Buffer;
|
||||
name[nameinfo->Name.Length] = 0;
|
||||
|
||||
/* check if this could be a msys pty pipe ('msys-XXXX-ptyN-XX') */
|
||||
if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty"))
|
||||
return;
|
||||
|
||||
/* init ioinfo size if we haven't done so */
|
||||
if (init_sizeof_ioinfo())
|
||||
return;
|
||||
|
||||
/* set FDEV flag, reset FPIPE flag */
|
||||
_pioinfo(fd)->osflags &= ~FPIPE;
|
||||
_pioinfo(fd)->osflags |= FDEV;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void winansi_init(void)
|
||||
{
|
||||
int con1, con2;
|
||||
@@ -535,8 +578,15 @@ void winansi_init(void)
|
||||
/* check if either stdout or stderr is a console output screen buffer */
|
||||
con1 = is_console(1);
|
||||
con2 = is_console(2);
|
||||
if (!con1 && !con2)
|
||||
if (!con1 && !con2) {
|
||||
#ifdef DETECT_MSYS_TTY
|
||||
/* check if stdin / stdout / stderr are msys pty pipes */
|
||||
detect_msys_tty(0);
|
||||
detect_msys_tty(1);
|
||||
detect_msys_tty(2);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* create a named pipe to communicate with the console thread */
|
||||
sprintf(name, "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
|
||||
@@ -572,8 +622,11 @@ void winansi_init(void)
|
||||
HANDLE winansi_get_osfhandle(int fd)
|
||||
{
|
||||
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
|
||||
if ((fd == 1 || fd == 2) && isatty(fd)
|
||||
&& GetFileType(hnd) == FILE_TYPE_PIPE)
|
||||
return (fd == 1) ? hconsole1 : hconsole2;
|
||||
if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
|
||||
if (fd == 1 && hconsole1)
|
||||
return hconsole1;
|
||||
else if (fd == 2 && hconsole2)
|
||||
return hconsole2;
|
||||
}
|
||||
return hnd;
|
||||
}
|
||||
|
||||
10
config.c
10
config.c
@@ -920,6 +920,16 @@ static int git_default_core_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.fscache")) {
|
||||
core_fscache = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.longpaths")) {
|
||||
core_long_paths = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ ifeq ($(uname_S),Windows)
|
||||
BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
|
||||
COMPAT_OBJS = compat/msvc.o compat/winansi.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/dirent.o
|
||||
compat/win32/dirent.o compat/win32/fscache.o
|
||||
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
|
||||
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
|
||||
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
|
||||
@@ -504,7 +504,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_MKSTEMPS = YesPlease
|
||||
NO_SVN_TESTS = YesPlease
|
||||
NO_PERL_MAKEMAKER = YesPlease
|
||||
RUNTIME_PREFIX = YesPlease
|
||||
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
||||
NO_NSEC = YesPlease
|
||||
@@ -521,13 +520,12 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
NO_POSIX_GOODIES = UnfortunatelyYes
|
||||
DEFAULT_HELP_FORMAT = html
|
||||
NO_D_INO_IN_DIRENT = YesPlease
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32
|
||||
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
|
||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/dirent.o
|
||||
compat/win32/dirent.o compat/win32/fscache.o
|
||||
BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
|
||||
BASIC_LDFLAGS += -Wl,--large-address-aware
|
||||
EXTLIBS += -lws2_32
|
||||
GITLIBS += git.res
|
||||
PTHREAD_LIBS =
|
||||
@@ -536,7 +534,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
X = .exe
|
||||
SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
|
||||
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
|
||||
htmldir = doc/git/html/
|
||||
htmldir = share/doc/git/$(firstword $(subst -, ,$(GIT_VERSION)))/html
|
||||
prefix =
|
||||
INSTALL = /bin/install
|
||||
EXTLIBS += /mingw/lib/libz.a
|
||||
@@ -544,8 +542,37 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
|
||||
INTERNAL_QSORT = YesPlease
|
||||
HAVE_LIBCHARSET_H = YesPlease
|
||||
NO_GETTEXT = YesPlease
|
||||
COMPAT_CLFAGS += -D__USE_MINGW_ACCESS
|
||||
else
|
||||
NO_CURL = YesPlease
|
||||
ifeq ($(shell expr "$(uname_R)" : '2\.'),2)
|
||||
# MSys2
|
||||
prefix = /usr/
|
||||
ifeq (MINGW32,$(MSYSTEM))
|
||||
prefix = /mingw32
|
||||
endif
|
||||
ifeq (MINGW64,$(MSYSTEM))
|
||||
prefix = /mingw64
|
||||
else
|
||||
COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
|
||||
BASIC_LDFLAGS += -Wl,--large-address-aware
|
||||
endif
|
||||
CC = gcc
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY
|
||||
EXTLIBS += -lntdll
|
||||
INSTALL = /bin/install
|
||||
NO_R_TO_GCC_LINKER = YesPlease
|
||||
INTERNAL_QSORT = YesPlease
|
||||
HAVE_LIBCHARSET_H = YesPlease
|
||||
NO_GETTEXT =
|
||||
USE_GETTEXT_SCHEME = fallthrough
|
||||
USE_LIBPCRE= YesPlease
|
||||
NO_CURL =
|
||||
USE_NED_ALLOCATOR = YesPlease
|
||||
NO_PYTHON =
|
||||
else
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
|
||||
NO_CURL = YesPlease
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifeq ($(uname_S),QNX)
|
||||
|
||||
@@ -65,6 +65,8 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
||||
struct startup_info *startup_info;
|
||||
unsigned long pack_size_limit_cfg;
|
||||
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||
int core_fscache;
|
||||
int core_long_paths;
|
||||
|
||||
#ifndef PROTECT_HFS_DEFAULT
|
||||
#define PROTECT_HFS_DEFAULT 0
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#ifndef NO_GETTEXT
|
||||
# include <locale.h>
|
||||
# include <libintl.h>
|
||||
# ifdef HAVE_LIBCHARSET_H
|
||||
# ifdef GIT_WINDOWS_NATIVE
|
||||
# define locale_charset() "UTF-8"
|
||||
# elif defined HAVE_LIBCHARSET_H
|
||||
# include <libcharset.h>
|
||||
# else
|
||||
# include <langinfo.h>
|
||||
|
||||
@@ -188,7 +188,9 @@ It does not apply to blobs recorded in its index.")"
|
||||
}
|
||||
|
||||
clean_abort () {
|
||||
test $# = 0 || echo >&2 "$@"
|
||||
test $# = 0 || cat >&2 <<EOF
|
||||
$@
|
||||
EOF
|
||||
rm -fr "$dotest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -184,8 +184,10 @@
|
||||
#if defined(__MINGW32__)
|
||||
/* pull in Windows compatibility stuff */
|
||||
#include "compat/mingw.h"
|
||||
#include "compat/win32/fscache.h"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "compat/msvc.h"
|
||||
#include "compat/win32/fscache.h"
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -945,4 +947,19 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
|
||||
#define mark_as_git_dir(x) /* noop */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable/disable a read-only cache for file system data on platforms that
|
||||
* support it.
|
||||
*
|
||||
* Implementing a live-cache is complicated and requires special platform
|
||||
* support (inotify, ReadDirectoryChangesW...). enable_fscache shall be used
|
||||
* to mark sections of git code that extensively read from the file system
|
||||
* without modifying anything. Implementations can use this to cache e.g. stat
|
||||
* data or even file content without the need to synchronize with the file
|
||||
* system.
|
||||
*/
|
||||
#ifndef enable_fscache
|
||||
#define enable_fscache(x) /* noop */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -161,7 +161,9 @@ ifeq ($(uname_S),Darwin)
|
||||
endif
|
||||
endif
|
||||
ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
|
||||
NO_MSGFMT=1
|
||||
endif
|
||||
GITGUI_WINDOWS_WRAPPER := YesPlease
|
||||
GITGUI_RELATIVE := 1
|
||||
endif
|
||||
|
||||
@@ -71,7 +71,9 @@ do
|
||||
|
||||
case "$LF$common$LF" in
|
||||
*"$LF$SHA1$LF"*)
|
||||
echo "Already up-to-date with $pretty_name"
|
||||
cat << EOF
|
||||
Already up-to-date with $pretty_name
|
||||
EOF
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
@@ -83,7 +85,9 @@ do
|
||||
# tree as the intermediate result of the merge.
|
||||
# We still need to count this as part of the parent set.
|
||||
|
||||
echo "Fast-forwarding to: $pretty_name"
|
||||
cat << EOF
|
||||
Fast-forwarding to: $pretty_name
|
||||
EOF
|
||||
git read-tree -u -m $head $SHA1 || exit
|
||||
MRC=$SHA1 MRT=$(git write-tree)
|
||||
continue
|
||||
@@ -91,7 +95,9 @@ do
|
||||
|
||||
NON_FF_MERGE=1
|
||||
|
||||
echo "Trying simple merge with $pretty_name"
|
||||
cat << EOF
|
||||
Trying simple merge with $pretty_name
|
||||
EOF
|
||||
git read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
|
||||
next=$(git write-tree 2>/dev/null)
|
||||
if test $? -ne 0
|
||||
|
||||
2
git.rc
2
git.rc
@@ -20,3 +20,5 @@ BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
1 RT_MANIFEST "compat/win32/git.manifest"
|
||||
|
||||
21
http.c
21
http.c
@@ -9,6 +9,7 @@
|
||||
#include "version.h"
|
||||
#include "pkt-line.h"
|
||||
#include "gettext.h"
|
||||
#include "exec_cmd.h"
|
||||
|
||||
int active_requests;
|
||||
int http_is_verbose;
|
||||
@@ -182,6 +183,18 @@ static void process_curl_messages(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int git_config_path(const char **result,
|
||||
const char *var, const char *value)
|
||||
{
|
||||
if (git_config_string(result, var, value))
|
||||
return 1;
|
||||
#ifdef __MINGW32__
|
||||
if (**result == '/')
|
||||
*result = system_path((*result) + 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_options(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp("http.sslverify", var)) {
|
||||
@@ -191,17 +204,17 @@ static int http_options(const char *var, const char *value, void *cb)
|
||||
if (!strcmp("http.sslcipherlist", var))
|
||||
return git_config_string(&ssl_cipherlist, var, value);
|
||||
if (!strcmp("http.sslcert", var))
|
||||
return git_config_string(&ssl_cert, var, value);
|
||||
return git_config_path(&ssl_cert, var, value);
|
||||
#if LIBCURL_VERSION_NUM >= 0x070903
|
||||
if (!strcmp("http.sslkey", var))
|
||||
return git_config_string(&ssl_key, var, value);
|
||||
return git_config_path(&ssl_key, var, value);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x070908
|
||||
if (!strcmp("http.sslcapath", var))
|
||||
return git_config_string(&ssl_capath, var, value);
|
||||
return git_config_path(&ssl_capath, var, value);
|
||||
#endif
|
||||
if (!strcmp("http.sslcainfo", var))
|
||||
return git_config_string(&ssl_cainfo, var, value);
|
||||
return git_config_path(&ssl_cainfo, var, value);
|
||||
if (!strcmp("http.sslcertpasswordprotected", var)) {
|
||||
ssl_cert_password_required = git_config_bool(var, value);
|
||||
return 0;
|
||||
|
||||
@@ -188,7 +188,8 @@ sub repository {
|
||||
};
|
||||
|
||||
if ($dir) {
|
||||
$dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;
|
||||
_verify_require();
|
||||
File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
|
||||
$opts{Repository} = abs_path($dir);
|
||||
|
||||
# If --git-dir went ok, this shouldn't die either.
|
||||
|
||||
@@ -84,6 +84,7 @@ static void preload_index(struct index_state *index,
|
||||
offset = 0;
|
||||
work = DIV_ROUND_UP(index->cache_nr, threads);
|
||||
memset(&data, 0, sizeof(data));
|
||||
enable_fscache(1);
|
||||
for (i = 0; i < threads; i++) {
|
||||
struct thread_data *p = data+i;
|
||||
p->index = index;
|
||||
@@ -100,6 +101,7 @@ static void preload_index(struct index_state *index,
|
||||
if (pthread_join(p->pthread, NULL))
|
||||
die("unable to join threaded lstat");
|
||||
}
|
||||
enable_fscache(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
102
t/t2027-checkout-long-paths.sh
Executable file
102
t/t2027-checkout-long-paths.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout long paths on Windows
|
||||
|
||||
Ensures that Git for Windows can deal with long paths (>260) enabled via core.longpaths'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if test_have_prereq !MINGW
|
||||
then
|
||||
skip_all='skipping MINGW specific long paths test'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success setup '
|
||||
p=longpathxx && # -> 10
|
||||
p=$p$p$p$p$p && # -> 50
|
||||
p=$p$p$p$p$p && # -> 250
|
||||
|
||||
path=${p}/longtestfile && # -> 263 (MAX_PATH = 260)
|
||||
|
||||
blob=$(echo foobar | git hash-object -w --stdin) &&
|
||||
|
||||
printf "100644 %s 0\t%s\n" "$blob" "$path" |
|
||||
git update-index --add --index-info &&
|
||||
git commit -m initial -q
|
||||
'
|
||||
|
||||
test_expect_success 'checkout of long paths without core.longpaths fails' '
|
||||
git config core.longpaths false &&
|
||||
test_must_fail git checkout -f 2>error &&
|
||||
grep -q "Filename too long" error &&
|
||||
test_path_is_missing longpa~1/longtestfile
|
||||
'
|
||||
|
||||
test_expect_success 'checkout of long paths with core.longpaths works' '
|
||||
git config core.longpaths true &&
|
||||
git checkout -f &&
|
||||
test_path_is_file longpa~1/longtestfile
|
||||
'
|
||||
|
||||
test_expect_success 'update of long paths' '
|
||||
echo frotz >> longpa~1/longtestfile &&
|
||||
echo $path > expect &&
|
||||
git ls-files -m > actual &&
|
||||
test_cmp expect actual &&
|
||||
git add $path &&
|
||||
git commit -m second &&
|
||||
git grep "frotz" HEAD -- $path
|
||||
'
|
||||
|
||||
test_expect_success cleanup '
|
||||
# bash cannot delete the trash dir if it contains a long path
|
||||
# lets help cleaning up (unless in debug mode)
|
||||
if test -z "$debug"
|
||||
then
|
||||
rm -rf longpa~1
|
||||
fi
|
||||
'
|
||||
|
||||
# check that the template used in the test won't be too long:
|
||||
abspath="$(pwd -W)"/testdir
|
||||
test ${#abspath} -gt 230 ||
|
||||
test_set_prereq SHORTABSPATH
|
||||
|
||||
test_expect_success SHORTABSPATH 'clean up path close to MAX_PATH' '
|
||||
p=/123456789abcdef/123456789abcdef/123456789abcdef/123456789abc/ef &&
|
||||
p=y$p$p$p$p &&
|
||||
subdir="x$(echo "$p" | tail -c $((253 - ${#abspath})) - )" &&
|
||||
# Now, $abspath/$subdir has exactly 254 characters, and is inside CWD
|
||||
p2="$abspath/$subdir" &&
|
||||
test 254 = ${#p2} &&
|
||||
|
||||
# Be careful to overcome path limitations of the MSys tools and split
|
||||
# the $subdir into two parts. ($subdir2 has to contain 16 chars and a
|
||||
# slash somewhere following; that is why we asked for abspath <= 230 and
|
||||
# why we placed a slash near the end of the $subdir template.)
|
||||
subdir2=${subdir#????????????????*/} &&
|
||||
subdir1=testdir/${subdir%/$subdir2} &&
|
||||
mkdir -p "$subdir1" &&
|
||||
i=0 &&
|
||||
# The most important case is when absolute path is 258 characters long,
|
||||
# and that will be when i == 4.
|
||||
while test $i -le 7
|
||||
do
|
||||
mkdir -p $subdir2 &&
|
||||
touch $subdir2/one-file &&
|
||||
mv ${subdir2%%/*} "$subdir1/" &&
|
||||
subdir2=z${subdir2} &&
|
||||
i=$(($i+1)) ||
|
||||
exit 1
|
||||
done &&
|
||||
|
||||
# now check that git is able to clear the tree:
|
||||
(cd testdir &&
|
||||
git init &&
|
||||
git config core.longpaths yes &&
|
||||
git clean -fdx) &&
|
||||
test ! -d "$subdir1"
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -13,6 +13,7 @@ tree, index, and tree objects.
|
||||
|
||||
HT=' '
|
||||
|
||||
test_have_prereq MINGW ||
|
||||
echo 2>/dev/null > "Name with an${HT}HT"
|
||||
if ! test -f "Name with an${HT}HT"
|
||||
then
|
||||
|
||||
@@ -14,7 +14,8 @@ test_expect_success \
|
||||
git add -- foo bar baz 'space embedded' -q &&
|
||||
git commit -m 'add normal files'"
|
||||
|
||||
if touch -- 'tab embedded' 'newline
|
||||
|
||||
if ! test_have_prereq MINGW && touch -- 'tab embedded' 'newline
|
||||
embedded' 2>/dev/null
|
||||
then
|
||||
test_set_prereq FUNNYNAMES
|
||||
|
||||
@@ -38,7 +38,7 @@ cat >expected <<EOF
|
||||
add 'sub/foo'
|
||||
EOF
|
||||
|
||||
if mkdir ":" 2>/dev/null
|
||||
if ! test_have_prereq MINGW && mkdir ":" 2>/dev/null
|
||||
then
|
||||
test_set_prereq COLON_DIR
|
||||
fi
|
||||
|
||||
@@ -12,6 +12,7 @@ GN='純'
|
||||
HT=' '
|
||||
DQ='"'
|
||||
|
||||
test_have_prereq MINGW ||
|
||||
echo foo 2>/dev/null > "Name and an${HT}HT"
|
||||
if ! test -f "Name and an${HT}HT"
|
||||
then
|
||||
|
||||
@@ -13,6 +13,7 @@ P1='pathname with HT'
|
||||
P2='pathname with SP'
|
||||
P3='pathname
|
||||
with LF'
|
||||
test_have_prereq !MINGW &&
|
||||
echo 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
|
||||
skip_all='Your filesystem does not allow tabs in filenames'
|
||||
test_done
|
||||
|
||||
@@ -19,7 +19,8 @@ test_expect_success 'setup' '
|
||||
|
||||
test_when_finished "rm -f \"tab embedded.txt\"" &&
|
||||
test_when_finished "rm -f '\''\"quoteembedded\".txt'\''" &&
|
||||
if touch -- "tab embedded.txt" '\''"quoteembedded".txt'\''
|
||||
if ! test_have_prereq MINGW &&
|
||||
touch -- "tab embedded.txt" '\''"quoteembedded".txt'\''
|
||||
then
|
||||
test_set_prereq FUNNYNAMES
|
||||
fi
|
||||
|
||||
@@ -16,6 +16,14 @@ This test checks the following functionality:
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if test_have_prereq MINGW
|
||||
then
|
||||
# Avoid posix-to-windows path mangling
|
||||
pwd () {
|
||||
builtin pwd
|
||||
}
|
||||
fi
|
||||
|
||||
D=`pwd`
|
||||
|
||||
mk_empty () {
|
||||
|
||||
105
t/t7411-submodule-long-path.sh
Executable file
105
t/t7411-submodule-long-path.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2013 Doug Kelly
|
||||
#
|
||||
|
||||
test_description='Test submodules with a path near PATH_MAX
|
||||
|
||||
This test verifies that "git submodule" initialization, update and clones work, including with recursive submodules and paths approaching PATH_MAX (260 characters on Windows)
|
||||
'
|
||||
|
||||
TEST_NO_CREATE_REPO=1
|
||||
. ./test-lib.sh
|
||||
|
||||
# cloning a submodule calls is_git_directory("$path/../.git/modules/$path"),
|
||||
# which effectively limits the maximum length to PATH_MAX / 2 minus some
|
||||
# overhead; start with 3 * 36 = 108 chars (test 2 fails if >= 110)
|
||||
longpath36=0123456789abcdefghijklmnopqrstuvwxyz
|
||||
longpath180=$longpath36$longpath36$longpath36$longpath36$longpath36
|
||||
|
||||
# the git database must fit within PATH_MAX, which limits the submodule name
|
||||
# to PATH_MAX - len(pwd) - ~90 (= len("/objects//") + 40-byte sha1 + some
|
||||
# overhead from the test case)
|
||||
pwd=$(pwd)
|
||||
pwdlen=$(echo "$pwd" | wc -c)
|
||||
longpath=$(echo $longpath180 | cut -c 1-$((170-$pwdlen)))
|
||||
|
||||
test_expect_success 'submodule with a long path' '
|
||||
git init --bare remote &&
|
||||
test_create_repo bundle1 &&
|
||||
(
|
||||
cd bundle1 &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
mkdir home &&
|
||||
(
|
||||
cd home &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git submodule add ../bundle1 $longpath &&
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home2 &&
|
||||
(
|
||||
cd home2 &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git checkout master &&
|
||||
git submodule update --init &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'recursive submodule with a long path' '
|
||||
git init --bare super &&
|
||||
test_create_repo child &&
|
||||
(
|
||||
cd child &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
test_create_repo parent &&
|
||||
(
|
||||
cd parent &&
|
||||
git submodule add ../child $longpath &&
|
||||
test_commit "aim"
|
||||
) &&
|
||||
mkdir home3 &&
|
||||
(
|
||||
cd home3 &&
|
||||
git clone ../super test &&
|
||||
cd test &&
|
||||
git submodule add ../parent foo &&
|
||||
git submodule update --init --recursive
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home4 &&
|
||||
(
|
||||
cd home4 &&
|
||||
git clone ../super test --recursive &&
|
||||
(
|
||||
cd test/foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -33,7 +33,9 @@ is($r->config_int("test.int"), 2048, "config_int: integer");
|
||||
is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent");
|
||||
ok($r->config_bool("test.booltrue"), "config_bool: true");
|
||||
ok(!$r->config_bool("test.boolfalse"), "config_bool: false");
|
||||
is($r->config_path("test.path"), $r->config("test.pathexpanded"),
|
||||
our $test_path = $r->config_path("test.path");
|
||||
$test_path =~ s/\\/\//g if ($^O eq 'msys');
|
||||
is($test_path, $r->config("test.pathexpanded"),
|
||||
"config_path: ~/foo expansion");
|
||||
is_deeply([$r->config_path("test.pathmulti")], ["foo", "bar"],
|
||||
"config_path: multiple values");
|
||||
|
||||
@@ -67,7 +67,7 @@ repo_with_newline='repo
|
||||
with
|
||||
newline'
|
||||
|
||||
if mkdir "$repo_with_newline" 2>/dev/null
|
||||
if ! test_have_prereq MINGW && mkdir "$repo_with_newline" 2>/dev/null
|
||||
then
|
||||
test_set_prereq FUNNYNAMES
|
||||
else
|
||||
|
||||
@@ -555,6 +555,20 @@ test_eval_ () {
|
||||
#
|
||||
# The test itself is run with stderr put back to &4 (so either to
|
||||
# /dev/null, or to the original stderr if --verbose was used).
|
||||
if test -n "$TEST_NO_REDIRECT"
|
||||
then
|
||||
test_eval_inner_ "$@"
|
||||
test_eval_ret_=$?
|
||||
if test "$trace" = t
|
||||
then
|
||||
set +x
|
||||
if test "$test_eval_ret_" != 0
|
||||
then
|
||||
say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
|
||||
fi
|
||||
fi
|
||||
return $test_eval_ret_
|
||||
fi
|
||||
{
|
||||
test_eval_inner_ "$@" </dev/null >&3 2>&4
|
||||
test_eval_ret_=$?
|
||||
@@ -998,7 +1012,7 @@ test_i18ngrep () {
|
||||
test_lazy_prereq PIPE '
|
||||
# test whether the filesystem supports FIFOs
|
||||
case $(uname -s) in
|
||||
CYGWIN*)
|
||||
CYGWIN*|MINGW*)
|
||||
false
|
||||
;;
|
||||
*)
|
||||
|
||||
Reference in New Issue
Block a user