mirror of
https://github.com/git/git.git
synced 2026-01-17 14:21:57 +00:00
Merge pull request #1897 from piscisaureus/symlink-attr
Specify symlink type in .gitattributes
This commit is contained in:
@@ -286,9 +286,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
|
||||
|
||||
@@ -324,8 +324,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
|
||||
@@ -334,14 +334,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)|" $< > $@
|
||||
@@ -351,14 +351,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 book -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))
|
||||
@@ -375,46 +375,46 @@ 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 $@+ $(DBLATEX_COMMON) $< && \
|
||||
mv $@+ $@
|
||||
$(QUIET_DBLATEX)$(RM) $@.new $@ && \
|
||||
$(DBLATEX) -o $@.new $(DBLATEX_COMMON) $< && \
|
||||
mv $@.new $@
|
||||
|
||||
gitman.texi: $(MAN_XML) cat-texi.perl texi.xsl
|
||||
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
|
||||
($(foreach xml,$(sort $(MAN_XML)),xsltproc -o $(xml)+ texi.xsl $(xml) && \
|
||||
$(DOCBOOK2X_TEXI) --encoding=UTF-8 --to-stdout $(xml)+ && \
|
||||
rm $(xml)+ &&) true) > $@++ && \
|
||||
$(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \
|
||||
rm $@++ && \
|
||||
mv $@+ $@
|
||||
$(QUIET_DB2TEXI)$(RM) $@.new $@ && \
|
||||
($(foreach xml,$(sort $(MAN_XML)),xsltproc -o $(xml).new texi.xsl $(xml) && \
|
||||
$(DOCBOOK2X_TEXI) --encoding=UTF-8 --to-stdout $(xml).new && \
|
||||
rm $(xml).new &&) 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
|
||||
@@ -423,10 +423,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)
|
||||
|
||||
@@ -378,6 +378,36 @@ sign `$` upon checkout. Any byte sequence that begins with
|
||||
with `$Id$` upon check-in.
|
||||
|
||||
|
||||
`symlink`
|
||||
^^^^^^^^^
|
||||
|
||||
On Windows, symbolic links have a type: a "file symlink" must point at
|
||||
a file, and a "directory symlink" must point at a directory. If the
|
||||
type of symlink does not match its target, it doesn't work.
|
||||
|
||||
Git does not record the type of symlink in the index or in a tree. On
|
||||
checkout it'll guess the type, which only works if the target exists
|
||||
at the time the symlink is created. This may often not be the case,
|
||||
for example when the link points at a directory inside a submodule.
|
||||
|
||||
The `symlink` attribute allows you to explicitly set the type of symlink
|
||||
to `file` or `dir`, so Git doesn't have to guess. If you have a set of
|
||||
symlinks that point at other files, you can do:
|
||||
|
||||
------------------------
|
||||
*.gif symlink=file
|
||||
------------------------
|
||||
|
||||
To tell Git that a symlink points at a directory, use:
|
||||
|
||||
------------------------
|
||||
tools_folder symlink=dir
|
||||
------------------------
|
||||
|
||||
The `symlink` attribute is ignored on platforms other than Windows,
|
||||
since they don't distinguish between different types of symlinks.
|
||||
|
||||
|
||||
`filter`
|
||||
^^^^^^^^
|
||||
|
||||
|
||||
@@ -576,6 +576,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_lf(&choice, stdin) != EOF) {
|
||||
strbuf_trim(&choice);
|
||||
} else {
|
||||
@@ -658,6 +659,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_lf(&confirm, stdin) != EOF)
|
||||
strbuf_trim(&confirm);
|
||||
else
|
||||
@@ -756,6 +758,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_lf(&confirm, stdin) != EOF) {
|
||||
strbuf_trim(&confirm);
|
||||
} else {
|
||||
|
||||
708
compat/mingw.c
708
compat/mingw.c
@@ -2,16 +2,16 @@
|
||||
#include "win32.h"
|
||||
#include <conio.h>
|
||||
#include <wchar.h>
|
||||
#include <winioctl.h>
|
||||
#include "../strbuf.h"
|
||||
#include "../run-command.h"
|
||||
#include "../cache.h"
|
||||
#include "win32/lazyload.h"
|
||||
#include "../config.h"
|
||||
#include "../attr.h"
|
||||
|
||||
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
||||
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
|
||||
int err_win_to_posix(DWORD winerr)
|
||||
{
|
||||
int error = ENOSYS;
|
||||
@@ -74,6 +74,7 @@ int err_win_to_posix(DWORD winerr)
|
||||
case ERROR_INVALID_PARAMETER: error = EINVAL; break;
|
||||
case ERROR_INVALID_PASSWORD: error = EPERM; break;
|
||||
case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
|
||||
case ERROR_INVALID_REPARSE_DATA: error = EINVAL; break;
|
||||
case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
|
||||
case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
|
||||
case ERROR_INVALID_WORKSTATION: error = EACCES; break;
|
||||
@@ -88,6 +89,7 @@ int err_win_to_posix(DWORD winerr)
|
||||
case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
|
||||
case ERROR_NOACCESS: error = EFAULT; break;
|
||||
case ERROR_NONE_MAPPED: error = EINVAL; break;
|
||||
case ERROR_NOT_A_REPARSE_POINT: error = EINVAL; break;
|
||||
case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
|
||||
case ERROR_NOT_READY: error = EAGAIN; break;
|
||||
case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
|
||||
@@ -108,6 +110,9 @@ int err_win_to_posix(DWORD winerr)
|
||||
case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
|
||||
case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
|
||||
case ERROR_READ_FAULT: error = EIO; break;
|
||||
case ERROR_REPARSE_ATTRIBUTE_CONFLICT: error = EINVAL; break;
|
||||
case ERROR_REPARSE_TAG_INVALID: error = EINVAL; break;
|
||||
case ERROR_REPARSE_TAG_MISMATCH: error = EINVAL; break;
|
||||
case ERROR_SEEK: error = EIO; break;
|
||||
case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
|
||||
case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
|
||||
@@ -174,15 +179,12 @@ static int read_yes_no_answer(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ask_yes_no_if_possible(const char *format, ...)
|
||||
static int ask_yes_no_if_possible(const char *format, va_list args)
|
||||
{
|
||||
char question[4096];
|
||||
const char *retry_hook[] = { NULL, NULL, NULL };
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(question, sizeof(question), format, args);
|
||||
va_end(args);
|
||||
|
||||
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
|
||||
retry_hook[1] = question;
|
||||
@@ -204,6 +206,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
|
||||
}
|
||||
}
|
||||
|
||||
static int retry_ask_yes_no(int *tries, const char *format, ...)
|
||||
{
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
va_list args;
|
||||
int result, saved_errno = errno;
|
||||
|
||||
if ((*tries) < ARRAY_SIZE(delay)) {
|
||||
/*
|
||||
* We assume that some other process had the file open at the wrong
|
||||
* moment and retry. In order to give the other process a higher
|
||||
* chance to complete its operation, we give up our time slice now.
|
||||
* If we have to retry again, we do sleep a bit.
|
||||
*/
|
||||
Sleep(delay[*tries]);
|
||||
(*tries)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
va_start(args, format);
|
||||
result = ask_yes_no_if_possible(format, args);
|
||||
va_end(args);
|
||||
errno = saved_errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Windows only */
|
||||
enum hide_dotfiles_type {
|
||||
HIDE_DOTFILES_FALSE = 0,
|
||||
@@ -245,6 +272,182 @@ int mingw_core_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD symlink_file_flags = 0, symlink_directory_flags = 1;
|
||||
|
||||
enum phantom_symlink_result {
|
||||
PHANTOM_SYMLINK_RETRY,
|
||||
PHANTOM_SYMLINK_DONE,
|
||||
PHANTOM_SYMLINK_DIRECTORY
|
||||
};
|
||||
|
||||
static inline int is_wdir_sep(wchar_t wchar)
|
||||
{
|
||||
return wchar == L'/' || wchar == L'\\';
|
||||
}
|
||||
|
||||
static const wchar_t *make_relative_to(const wchar_t *path,
|
||||
const wchar_t *relative_to, wchar_t *out,
|
||||
size_t size)
|
||||
{
|
||||
size_t i = wcslen(relative_to), len;
|
||||
|
||||
/* Is `path` already absolute? */
|
||||
if (is_wdir_sep(path[0]) ||
|
||||
(iswalpha(path[0]) && path[1] == L':' && is_wdir_sep(path[2])))
|
||||
return path;
|
||||
|
||||
while (i > 0 && !is_wdir_sep(relative_to[i - 1]))
|
||||
i--;
|
||||
|
||||
/* Is `relative_to` in the current directory? */
|
||||
if (!i)
|
||||
return path;
|
||||
|
||||
len = wcslen(path);
|
||||
if (i + len + 1 > size) {
|
||||
error("Could not make '%S' relative to '%S' (too large)",
|
||||
path, relative_to);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(out, relative_to, i * sizeof(wchar_t));
|
||||
wcscpy(out + i, path);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Changes a file symlink to a directory symlink if the target exists and is a
|
||||
* directory.
|
||||
*/
|
||||
static enum phantom_symlink_result
|
||||
process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
|
||||
{
|
||||
HANDLE hnd;
|
||||
BY_HANDLE_FILE_INFORMATION fdata;
|
||||
wchar_t relative[MAX_LONG_PATH];
|
||||
const wchar_t *rel;
|
||||
|
||||
/* check that wlink is still a file symlink */
|
||||
if ((GetFileAttributesW(wlink)
|
||||
& (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
|
||||
!= FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
return PHANTOM_SYMLINK_DONE;
|
||||
|
||||
/* make it relative, if necessary */
|
||||
rel = make_relative_to(wtarget, wlink, relative, ARRAY_SIZE(relative));
|
||||
if (!rel)
|
||||
return PHANTOM_SYMLINK_DONE;
|
||||
|
||||
/* let Windows resolve the link by opening it */
|
||||
hnd = CreateFileW(rel, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hnd == INVALID_HANDLE_VALUE) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return PHANTOM_SYMLINK_RETRY;
|
||||
}
|
||||
|
||||
if (!GetFileInformationByHandle(hnd, &fdata)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(hnd);
|
||||
return PHANTOM_SYMLINK_RETRY;
|
||||
}
|
||||
CloseHandle(hnd);
|
||||
|
||||
/* if target exists and is a file, we're done */
|
||||
if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return PHANTOM_SYMLINK_DONE;
|
||||
|
||||
/* otherwise recreate the symlink with directory flag */
|
||||
if (DeleteFileW(wlink) &&
|
||||
CreateSymbolicLinkW(wlink, wtarget, symlink_directory_flags))
|
||||
return PHANTOM_SYMLINK_DIRECTORY;
|
||||
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return PHANTOM_SYMLINK_RETRY;
|
||||
}
|
||||
|
||||
/* keep track of newly created symlinks to non-existing targets */
|
||||
struct phantom_symlink_info {
|
||||
struct phantom_symlink_info *next;
|
||||
wchar_t *wlink;
|
||||
wchar_t *wtarget;
|
||||
};
|
||||
|
||||
static struct phantom_symlink_info *phantom_symlinks = NULL;
|
||||
static CRITICAL_SECTION phantom_symlinks_cs;
|
||||
|
||||
static void process_phantom_symlinks(void)
|
||||
{
|
||||
struct phantom_symlink_info *current, **psi;
|
||||
EnterCriticalSection(&phantom_symlinks_cs);
|
||||
/* process phantom symlinks list */
|
||||
psi = &phantom_symlinks;
|
||||
while ((current = *psi)) {
|
||||
enum phantom_symlink_result result = process_phantom_symlink(
|
||||
current->wtarget, current->wlink);
|
||||
if (result == PHANTOM_SYMLINK_RETRY) {
|
||||
psi = ¤t->next;
|
||||
} else {
|
||||
/* symlink was processed, remove from list */
|
||||
*psi = current->next;
|
||||
free(current);
|
||||
/* if symlink was a directory, start over */
|
||||
if (result == PHANTOM_SYMLINK_DIRECTORY)
|
||||
psi = &phantom_symlinks;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&phantom_symlinks_cs);
|
||||
}
|
||||
|
||||
static int create_phantom_symlink(wchar_t *wtarget, wchar_t *wlink)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* create file symlink */
|
||||
if (!CreateSymbolicLinkW(wlink, wtarget, symlink_file_flags)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* convert to directory symlink if target exists */
|
||||
switch (process_phantom_symlink(wtarget, wlink)) {
|
||||
case PHANTOM_SYMLINK_RETRY: {
|
||||
/* if target doesn't exist, add to phantom symlinks list */
|
||||
wchar_t wfullpath[MAX_LONG_PATH];
|
||||
struct phantom_symlink_info *psi;
|
||||
|
||||
/* convert to absolute path to be independent of cwd */
|
||||
len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
|
||||
if (!len || len >= MAX_LONG_PATH) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* over-allocate and fill phantom_symlink_info structure */
|
||||
psi = xmalloc(sizeof(struct phantom_symlink_info) +
|
||||
sizeof(wchar_t) * (len + wcslen(wtarget) + 2));
|
||||
psi->wlink = (wchar_t *)(psi + 1);
|
||||
wcscpy(psi->wlink, wfullpath);
|
||||
psi->wtarget = psi->wlink + len + 1;
|
||||
wcscpy(psi->wtarget, wtarget);
|
||||
|
||||
EnterCriticalSection(&phantom_symlinks_cs);
|
||||
psi->next = phantom_symlinks;
|
||||
phantom_symlinks = psi;
|
||||
LeaveCriticalSection(&phantom_symlinks_cs);
|
||||
break;
|
||||
}
|
||||
case PHANTOM_SYMLINK_DIRECTORY:
|
||||
/* if we created a dir symlink, process other phantom symlinks */
|
||||
process_phantom_symlinks();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Normalizes NT paths as returned by some low-level APIs. */
|
||||
static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
||||
{
|
||||
@@ -272,31 +475,28 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
||||
|
||||
int mingw_unlink(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
int tries = 0;
|
||||
wchar_t wpathname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
/* read-only files cannot be removed */
|
||||
_wchmod(wpathname, 0666);
|
||||
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||
do {
|
||||
/* read-only files cannot be removed */
|
||||
_wchmod(wpathname, 0666);
|
||||
if (!_wunlink(wpathname))
|
||||
return 0;
|
||||
if (!is_file_in_use_error(GetLastError()))
|
||||
break;
|
||||
/*
|
||||
* We assume that some other process had the source or
|
||||
* destination file open at the wrong moment and retry.
|
||||
* In order to give the other process a higher chance to
|
||||
* complete its operation, we give up our time slice now.
|
||||
* If we have to retry again, we do sleep a bit.
|
||||
* _wunlink() / DeleteFileW() for directory symlinks fails with
|
||||
* ERROR_ACCESS_DENIED (EACCES), so try _wrmdir() as well. This is the
|
||||
* same error we get if a file is in use (already checked above).
|
||||
*/
|
||||
Sleep(delay[tries]);
|
||||
tries++;
|
||||
}
|
||||
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
||||
ask_yes_no_if_possible("Unlink of file '%s' failed. "
|
||||
"Should I try again?", pathname))
|
||||
ret = _wunlink(wpathname);
|
||||
return ret;
|
||||
if (!_wrmdir(wpathname))
|
||||
return 0;
|
||||
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
|
||||
"Should I try again?", pathname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int is_dir_empty(const wchar_t *wpath)
|
||||
@@ -323,12 +523,14 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||
|
||||
int mingw_rmdir(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
int tries = 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)) {
|
||||
do {
|
||||
if (!_wrmdir(wpathname))
|
||||
return 0;
|
||||
if (!is_file_in_use_error(GetLastError()))
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
if (errno != EACCES)
|
||||
@@ -337,21 +539,9 @@ int mingw_rmdir(const char *pathname)
|
||||
errno = ENOTEMPTY;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* We assume that some other process had the source or
|
||||
* destination file open at the wrong moment and retry.
|
||||
* In order to give the other process a higher chance to
|
||||
* complete its operation, we give up our time slice now.
|
||||
* If we have to retry again, we do sleep a bit.
|
||||
*/
|
||||
Sleep(delay[tries]);
|
||||
tries++;
|
||||
}
|
||||
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
|
||||
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
|
||||
"Should I try again?", pathname))
|
||||
ret = _wrmdir(wpathname);
|
||||
return ret;
|
||||
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
|
||||
"Should I try again?", pathname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int needs_hiding(const char *path)
|
||||
@@ -407,6 +597,8 @@ int mingw_mkdir(const char *path, int mode)
|
||||
return -1;
|
||||
|
||||
ret = _wmkdir(wpath);
|
||||
if (!ret)
|
||||
process_phantom_symlinks();
|
||||
if (!ret && needs_hiding(path))
|
||||
return set_hidden_flag(wpath, 1);
|
||||
return ret;
|
||||
@@ -615,7 +807,24 @@ int mingw_chdir(const char *dirname)
|
||||
wchar_t wdirname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wdirname, dirname) < 0)
|
||||
return -1;
|
||||
result = _wchdir(wdirname);
|
||||
|
||||
if (has_symlinks) {
|
||||
HANDLE hnd = CreateFileW(wdirname, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hnd == INVALID_HANDLE_VALUE) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!GetFinalPathNameByHandleW(hnd, wdirname, ARRAY_SIZE(wdirname), 0)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(hnd);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(hnd);
|
||||
}
|
||||
|
||||
result = _wchdir(normalize_ntpath(wdirname));
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
return result;
|
||||
}
|
||||
@@ -661,53 +870,46 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* If follow is true then act like stat() and report on the link
|
||||
* target. Otherwise report on the link itself.
|
||||
*/
|
||||
static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
int mingw_lstat(const char *file_name, struct stat *buf)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
||||
WIN32_FIND_DATAW findbuf = { 0 };
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, file_name) < 0)
|
||||
int wlen = xutftowcs_long_path(wfilename, file_name);
|
||||
if (wlen < 0)
|
||||
return -1;
|
||||
|
||||
/* strip trailing '/', or GetFileAttributes will fail */
|
||||
while (wlen && is_dir_sep(wfilename[wlen - 1]))
|
||||
wfilename[--wlen] = 0;
|
||||
if (!wlen) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
||||
/* for reparse points, use FindFirstFile to get the reparse tag */
|
||||
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
FindClose(handle);
|
||||
}
|
||||
buf->st_ino = 0;
|
||||
buf->st_gid = 0;
|
||||
buf->st_uid = 0;
|
||||
buf->st_nlink = 1;
|
||||
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
|
||||
buf->st_size = fdata.nFileSizeLow |
|
||||
(((off_t)fdata.nFileSizeHigh)<<32);
|
||||
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
|
||||
findbuf.dwReserved0);
|
||||
buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH :
|
||||
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
|
||||
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
||||
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
||||
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
|
||||
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
||||
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
WIN32_FIND_DATAW findbuf;
|
||||
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
||||
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
|
||||
if (follow) {
|
||||
char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||||
} else {
|
||||
buf->st_mode = S_IFLNK;
|
||||
}
|
||||
buf->st_mode |= S_IREAD;
|
||||
if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
|
||||
buf->st_mode |= S_IWRITE;
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
error:
|
||||
switch (GetLastError()) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
@@ -734,39 +936,6 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We provide our own lstat/fstat functions, since the provided
|
||||
* lstat/fstat functions are so slow. These stat functions are
|
||||
* tailored for Git's usage (read: fast), and are not meant to be
|
||||
* complete. Note that Git stat()s are redirected to mingw_lstat()
|
||||
* too, since Windows doesn't really handle symlinks that well.
|
||||
*/
|
||||
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
{
|
||||
int namelen;
|
||||
char alt_name[MAX_LONG_PATH];
|
||||
|
||||
if (!do_lstat(follow, file_name, buf))
|
||||
return 0;
|
||||
|
||||
/* if file_name ended in a '/', Windows returned ENOENT;
|
||||
* try again without trailing slashes
|
||||
*/
|
||||
if (errno != ENOENT)
|
||||
return -1;
|
||||
|
||||
namelen = strlen(file_name);
|
||||
if (namelen && file_name[namelen-1] != '/')
|
||||
return -1;
|
||||
while (namelen && file_name[namelen-1] == '/')
|
||||
--namelen;
|
||||
if (!namelen || namelen >= MAX_LONG_PATH)
|
||||
return -1;
|
||||
|
||||
memcpy(alt_name, file_name, namelen);
|
||||
alt_name[namelen] = 0;
|
||||
return do_lstat(follow, alt_name, buf);
|
||||
}
|
||||
|
||||
int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
|
||||
|
||||
static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
|
||||
@@ -782,7 +951,7 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
|
||||
buf->st_gid = 0;
|
||||
buf->st_uid = 0;
|
||||
buf->st_nlink = 1;
|
||||
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
|
||||
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes, 0);
|
||||
buf->st_size = fdata.nFileSizeLow |
|
||||
(((off_t)fdata.nFileSizeHigh)<<32);
|
||||
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
||||
@@ -792,13 +961,25 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mingw_lstat(const char *file_name, struct stat *buf)
|
||||
{
|
||||
return do_stat_internal(0, file_name, buf);
|
||||
}
|
||||
int mingw_stat(const char *file_name, struct stat *buf)
|
||||
{
|
||||
return do_stat_internal(1, file_name, buf);
|
||||
wchar_t wfile_name[MAX_LONG_PATH];
|
||||
HANDLE hnd;
|
||||
int result;
|
||||
|
||||
/* open the file and let Windows resolve the links */
|
||||
if (xutftowcs_long_path(wfile_name, file_name) < 0)
|
||||
return -1;
|
||||
hnd = CreateFileW(wfile_name, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hnd == INVALID_HANDLE_VALUE) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
result = get_file_info_by_handle(hnd, buf);
|
||||
CloseHandle(hnd);
|
||||
return result;
|
||||
}
|
||||
|
||||
int mingw_fstat(int fd, struct stat *buf)
|
||||
@@ -1385,6 +1566,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
HANDLE cons;
|
||||
const char *(*quote_arg)(const char *arg) =
|
||||
is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
|
||||
const char *strace_env;
|
||||
|
||||
do_unset_environment_variables();
|
||||
|
||||
@@ -1456,6 +1638,31 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
free(quoted);
|
||||
}
|
||||
|
||||
strace_env = getenv("GIT_STRACE_COMMANDS");
|
||||
if (strace_env) {
|
||||
char *p = path_lookup("strace.exe", 1);
|
||||
if (!p)
|
||||
return error("strace not found!");
|
||||
if (xutftowcs_path(wcmd, p) < 0) {
|
||||
free(p);
|
||||
return -1;
|
||||
}
|
||||
free(p);
|
||||
if (!strcmp("1", strace_env) ||
|
||||
!strcasecmp("yes", strace_env) ||
|
||||
!strcasecmp("true", strace_env))
|
||||
strbuf_insert(&args, 0, "strace ", 7);
|
||||
else {
|
||||
const char *quoted = quote_arg(strace_env);
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addf(&buf, "strace -o %s ", quoted);
|
||||
if (quoted != strace_env)
|
||||
free((char *)quoted);
|
||||
strbuf_insert(&args, 0, buf.buf, buf.len);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
|
||||
xutftowcs(wargs, args.buf, 2 * args.len + 1);
|
||||
strbuf_release(&args);
|
||||
@@ -2081,27 +2288,29 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
|
||||
#undef rename
|
||||
int mingw_rename(const char *pold, const char *pnew)
|
||||
{
|
||||
DWORD attrs, gle;
|
||||
DWORD attrs = INVALID_FILE_ATTRIBUTES, gle;
|
||||
int tries = 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;
|
||||
|
||||
/*
|
||||
* Try native rename() first to get errno right.
|
||||
* It is based on MoveFile(), which cannot overwrite existing files.
|
||||
*/
|
||||
if (!_wrename(wpold, wpnew))
|
||||
return 0;
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
repeat:
|
||||
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
|
||||
if (MoveFileExW(wpold, wpnew,
|
||||
MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
|
||||
return 0;
|
||||
/* TODO: translate more errors */
|
||||
gle = GetLastError();
|
||||
if (gle == ERROR_ACCESS_DENIED &&
|
||||
|
||||
/* revert file attributes on failure */
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES)
|
||||
SetFileAttributesW(wpnew, attrs);
|
||||
|
||||
if (!is_file_in_use_error(gle)) {
|
||||
errno = err_win_to_posix(gle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES &&
|
||||
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
|
||||
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
DWORD attrsold = GetFileAttributesW(wpold);
|
||||
@@ -2113,28 +2322,10 @@ repeat:
|
||||
return -1;
|
||||
}
|
||||
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
|
||||
SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
|
||||
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
|
||||
return 0;
|
||||
gle = GetLastError();
|
||||
/* revert file attributes on failure */
|
||||
SetFileAttributesW(wpnew, attrs);
|
||||
}
|
||||
SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY))
|
||||
goto repeat;
|
||||
}
|
||||
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
|
||||
/*
|
||||
* We assume that some other process had the source or
|
||||
* destination file open at the wrong moment and retry.
|
||||
* In order to give the other process a higher chance to
|
||||
* complete its operation, we give up our time slice now.
|
||||
* If we have to retry again, we do sleep a bit.
|
||||
*/
|
||||
Sleep(delay[tries]);
|
||||
tries++;
|
||||
goto repeat;
|
||||
}
|
||||
if (gle == ERROR_ACCESS_DENIED &&
|
||||
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
|
||||
if (retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
|
||||
"Should I try again?", pold, pnew))
|
||||
goto repeat;
|
||||
|
||||
@@ -2406,6 +2597,179 @@ int link(const char *oldpath, const char *newpath)
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum symlink_type {
|
||||
SYMLINK_TYPE_UNSPECIFIED = 0,
|
||||
SYMLINK_TYPE_FILE,
|
||||
SYMLINK_TYPE_DIRECTORY,
|
||||
};
|
||||
|
||||
static enum symlink_type check_symlink_attr(const char *link)
|
||||
{
|
||||
static struct attr_check *check;
|
||||
const char *value;
|
||||
int r;
|
||||
|
||||
if (!check)
|
||||
check = attr_check_initl("symlink", NULL);
|
||||
|
||||
r = git_check_attr(&the_index, link, check);
|
||||
assert(!r);
|
||||
|
||||
value = check->items[0].value;
|
||||
if (value == NULL)
|
||||
;
|
||||
else if (!strcmp(value, "file"))
|
||||
return SYMLINK_TYPE_FILE;
|
||||
else if (!strcmp(value, "dir"))
|
||||
return SYMLINK_TYPE_DIRECTORY;
|
||||
|
||||
return SYMLINK_TYPE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
int symlink(const char *target, const char *link)
|
||||
{
|
||||
wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
|
||||
int len;
|
||||
|
||||
/* fail if symlinks are disabled or API is not supported (WinXP) */
|
||||
if (!has_symlinks) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((len = xutftowcs_long_path(wtarget, target)) < 0
|
||||
|| xutftowcs_long_path(wlink, link) < 0)
|
||||
return -1;
|
||||
|
||||
/* convert target dir separators to backslashes */
|
||||
while (len--)
|
||||
if (wtarget[len] == '/')
|
||||
wtarget[len] = '\\';
|
||||
|
||||
switch (check_symlink_attr(link)) {
|
||||
case SYMLINK_TYPE_UNSPECIFIED:
|
||||
/* Create a phantom symlink: it is initially created as a file
|
||||
* symlink, but may change to a directory symlink later if/when
|
||||
* the target exists. */
|
||||
return create_phantom_symlink(wtarget, wlink);
|
||||
case SYMLINK_TYPE_FILE:
|
||||
if (!CreateSymbolicLinkW(wlink, wtarget, symlink_file_flags))
|
||||
break;
|
||||
return 0;
|
||||
case SYMLINK_TYPE_DIRECTORY:
|
||||
if (!CreateSymbolicLinkW(wlink, wtarget,
|
||||
symlink_directory_flags))
|
||||
break;
|
||||
/* There may be dangling phantom symlinks that point at this
|
||||
* one, which should now morph into directory symlinks. */
|
||||
process_phantom_symlinks();
|
||||
return 0;
|
||||
default:
|
||||
BUG("unhandled symlink type");
|
||||
}
|
||||
|
||||
/* CreateSymbolicLinkW failed. */
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef _WINNT_H
|
||||
/*
|
||||
* The REPARSE_DATA_BUFFER structure is defined in the Windows DDK (in
|
||||
* ntifs.h) and in MSYS1's winnt.h (which defines _WINNT_H). So define
|
||||
* it ourselves if we are on MSYS2 (whose winnt.h defines _WINNT_).
|
||||
*/
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
DWORD ReparseTag;
|
||||
WORD ReparseDataLength;
|
||||
WORD Reserved;
|
||||
#ifndef _MSC_VER
|
||||
_ANONYMOUS_UNION
|
||||
#endif
|
||||
union {
|
||||
struct {
|
||||
WORD SubstituteNameOffset;
|
||||
WORD SubstituteNameLength;
|
||||
WORD PrintNameOffset;
|
||||
WORD PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
WORD SubstituteNameOffset;
|
||||
WORD SubstituteNameLength;
|
||||
WORD PrintNameOffset;
|
||||
WORD PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
BYTE DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
} DUMMYUNIONNAME;
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
#endif
|
||||
|
||||
int readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{
|
||||
HANDLE handle;
|
||||
WCHAR wpath[MAX_LONG_PATH], *wbuf;
|
||||
REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||||
DWORD dummy;
|
||||
char tmpbuf[MAX_LONG_PATH];
|
||||
int len;
|
||||
|
||||
if (xutftowcs_long_path(wpath, path) < 0)
|
||||
return -1;
|
||||
|
||||
/* read reparse point data */
|
||||
handle = CreateFileW(wpath, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, b,
|
||||
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dummy, NULL)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(handle);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
|
||||
/* get target path for symlinks or mount points (aka 'junctions') */
|
||||
switch (b->ReparseTag) {
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
|
||||
+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
|
||||
*(WCHAR*) (((char*) wbuf)
|
||||
+ b->SymbolicLinkReparseBuffer.SubstituteNameLength) = 0;
|
||||
break;
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
wbuf = (WCHAR*) (((char*) b->MountPointReparseBuffer.PathBuffer)
|
||||
+ b->MountPointReparseBuffer.SubstituteNameOffset);
|
||||
*(WCHAR*) (((char*) wbuf)
|
||||
+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
|
||||
* cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
|
||||
* condition. There is no conversion function that produces invalid UTF-8,
|
||||
* so convert to a (hopefully large enough) temporary buffer, then memcpy
|
||||
* the requested number of bytes (including '\0' for robustness).
|
||||
*/
|
||||
if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
|
||||
return -1;
|
||||
memcpy(buf, tmpbuf, min(bufsiz, len + 1));
|
||||
return min(bufsiz, len);
|
||||
}
|
||||
|
||||
pid_t waitpid(pid_t pid, int *status, int options)
|
||||
{
|
||||
HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
||||
@@ -2597,6 +2961,42 @@ static void setup_windows_environment(void)
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change 'core.symlinks' default to false, unless native symlinks are
|
||||
* enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
|
||||
* run the test suite (which doesn't obey config files) with or without
|
||||
* symlink support.
|
||||
*/
|
||||
if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
|
||||
has_symlinks = 0;
|
||||
|
||||
if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
|
||||
setenv("LC_CTYPE", "C", 1);
|
||||
}
|
||||
|
||||
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
|
||||
@@ -2745,6 +3145,24 @@ static void maybe_redirect_std_handles(void)
|
||||
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
|
||||
}
|
||||
|
||||
static void adjust_symlink_flags(void)
|
||||
{
|
||||
/*
|
||||
* Starting with Windows 10 Build 14972, symbolic links can be created
|
||||
* using CreateSymbolicLink() without elevation by passing the flag
|
||||
* SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x02) as last
|
||||
* parameter, provided the Developer Mode has been enabled. Some
|
||||
* earlier Windows versions complain about this flag with an
|
||||
* ERROR_INVALID_PARAMETER, hence we have to test the build number
|
||||
* specifically.
|
||||
*/
|
||||
if (GetVersion() >= 14972 << 16) {
|
||||
symlink_file_flags |= 2;
|
||||
symlink_directory_flags |= 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef _DEBUG
|
||||
#include <crtdbg.h>
|
||||
@@ -2777,6 +3195,7 @@ int wmain(int argc, const wchar_t **wargv)
|
||||
#endif
|
||||
|
||||
maybe_redirect_std_handles();
|
||||
adjust_symlink_flags();
|
||||
|
||||
/* determine size of argv and environ conversion buffer */
|
||||
maxlen = wcslen(wargv[0]);
|
||||
@@ -2806,6 +3225,7 @@ int wmain(int argc, const wchar_t **wargv)
|
||||
|
||||
/* initialize critical section for waitpid pinfo_t list */
|
||||
InitializeCriticalSection(&pinfo_cs);
|
||||
InitializeCriticalSection(&phantom_symlinks_cs);
|
||||
|
||||
/* set up default file mode and file modes for stdin/out/err */
|
||||
_fmode = _O_BINARY;
|
||||
|
||||
@@ -123,10 +123,6 @@ struct utsname {
|
||||
* trivial stubs
|
||||
*/
|
||||
|
||||
static inline int readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{ errno = ENOSYS; return -1; }
|
||||
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
|
||||
@@ -218,6 +214,8 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out);
|
||||
int sigaction(int sig, struct sigaction *in, struct sigaction *out);
|
||||
int link(const char *oldpath, const char *newpath);
|
||||
int uname(struct utsname *buf);
|
||||
int symlink(const char *target, const char *link);
|
||||
int readlink(const char *path, char *buf, size_t bufsiz);
|
||||
|
||||
/*
|
||||
* replacements of existing functions
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#ifndef NO_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#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 +96,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 +157,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;
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static inline int file_attr_to_st_mode (DWORD attr)
|
||||
static inline int file_attr_to_st_mode (DWORD attr, DWORD tag)
|
||||
{
|
||||
int fMode = S_IREAD;
|
||||
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && tag == IO_REPARSE_TAG_SYMLINK)
|
||||
fMode |= S_IFLNK;
|
||||
else if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
fMode |= S_IFDIR;
|
||||
else
|
||||
fMode |= S_IFREG;
|
||||
|
||||
@@ -16,7 +16,10 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
|
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */
|
||||
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
&& fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK)
|
||||
ent->d_type = DT_LNK;
|
||||
else if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
ent->d_type = DT_DIR;
|
||||
else
|
||||
ent->d_type = DT_REG;
|
||||
|
||||
@@ -149,9 +149,10 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
|
||||
|
||||
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_mode = file_attr_to_st_mode(fdata->dwFileAttributes,
|
||||
fdata->dwReserved0);
|
||||
fse->st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
|
||||
fdata->nFileSizeLow | (((off_t) fdata->nFileSizeHigh) << 32);
|
||||
filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->st_atim));
|
||||
filetime_to_timespec(&(fdata->ftLastWriteTime), &(fse->st_mtim));
|
||||
filetime_to_timespec(&(fdata->ftCreationTime), &(fse->st_ctim));
|
||||
@@ -442,7 +443,8 @@ static struct dirent *fscache_readdir(DIR *base_dir)
|
||||
if (!next)
|
||||
return NULL;
|
||||
dir->pfsentry = next;
|
||||
dir->dirent.d_type = S_ISDIR(next->st_mode) ? DT_DIR : DT_REG;
|
||||
dir->dirent.d_type = S_ISREG(next->st_mode) ? DT_REG :
|
||||
S_ISDIR(next->st_mode) ? DT_DIR : DT_LNK;
|
||||
dir->dirent.d_name = (char*) next->name;
|
||||
return &(dir->dirent);
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <wingdi.h>
|
||||
#include <winreg.h>
|
||||
#include "win32.h"
|
||||
#include "win32/lazyload.h"
|
||||
|
||||
static int fd_is_interactive[3] = { 0, 0, 0 };
|
||||
#define FD_CONSOLE 0x1
|
||||
@@ -41,26 +42,21 @@ typedef struct _CONSOLE_FONT_INFOEX {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
|
||||
PCONSOLE_FONT_INFOEX);
|
||||
|
||||
static void warn_if_raster_font(void)
|
||||
{
|
||||
DWORD fontFamily = 0;
|
||||
PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
|
||||
DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
|
||||
HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
|
||||
|
||||
/* don't bother if output was ascii only */
|
||||
if (!non_ascii_used)
|
||||
return;
|
||||
|
||||
/* GetCurrentConsoleFontEx is available since Vista */
|
||||
pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
|
||||
GetModuleHandle("kernel32.dll"),
|
||||
"GetCurrentConsoleFontEx");
|
||||
if (pGetCurrentConsoleFontEx) {
|
||||
if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof(cfi);
|
||||
if (pGetCurrentConsoleFontEx(console, 0, &cfi))
|
||||
if (GetCurrentConsoleFontEx(console, 0, &cfi))
|
||||
fontFamily = cfi.FontFamily;
|
||||
} else {
|
||||
/* pre-Vista: check default console font in registry */
|
||||
|
||||
@@ -571,7 +571,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
NO_STRTOUMAX = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_SVN_TESTS = YesPlease
|
||||
NO_PERL_MAKEMAKER = YesPlease
|
||||
RUNTIME_PREFIX = YesPlease
|
||||
HAVE_WPGMPTR = YesWeDo
|
||||
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
||||
@@ -600,7 +599,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
|
||||
@@ -631,7 +630,8 @@ else
|
||||
BASIC_LDFLAGS += -Wl,--large-address-aware
|
||||
endif
|
||||
CC = gcc
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
|
||||
-fstack-protector-strong
|
||||
EXTLIBS += -lntdll
|
||||
INSTALL = /bin/install
|
||||
NO_R_TO_GCC_LINKER = YesPlease
|
||||
@@ -643,6 +643,7 @@ else
|
||||
NO_LIBPCRE1_JIT = UnfortunatelyYes
|
||||
NO_CURL =
|
||||
USE_NED_ALLOCATOR = YesPlease
|
||||
NO_PYTHON =
|
||||
else
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
|
||||
NO_CURL = YesPlease
|
||||
|
||||
4
diff.c
4
diff.c
@@ -4092,6 +4092,10 @@ static void run_external_diff(const char *pgm,
|
||||
argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
|
||||
argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
|
||||
|
||||
if (one && one->should_munmap)
|
||||
diff_free_filespec_data(one);
|
||||
if (two && two->should_munmap)
|
||||
diff_free_filespec_data(two);
|
||||
if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
|
||||
die(_("external diff died, stopping at %s"), name);
|
||||
|
||||
|
||||
@@ -11,7 +11,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>
|
||||
|
||||
2
git.rc
2
git.rc
@@ -20,3 +20,5 @@ BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
1 RT_MANIFEST "compat/win32/git.manifest"
|
||||
|
||||
@@ -228,11 +228,9 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
|
||||
struct child_process gpg = CHILD_PROCESS_INIT;
|
||||
int ret;
|
||||
size_t i, j, bottom;
|
||||
struct strbuf gpg_status = STRBUF_INIT;
|
||||
|
||||
argv_array_pushl(&gpg.args,
|
||||
use_format->program,
|
||||
"--status-fd=2",
|
||||
"-bsau", signing_key,
|
||||
NULL);
|
||||
|
||||
@@ -244,12 +242,10 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
|
||||
*/
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
ret = pipe_command(&gpg, buffer->buf, buffer->len,
|
||||
signature, 1024, &gpg_status, 0);
|
||||
signature, 1024, NULL, 0);
|
||||
sigchain_pop(SIGPIPE);
|
||||
|
||||
ret |= !strstr(gpg_status.buf, "\n[GNUPG:] SIG_CREATED ");
|
||||
strbuf_release(&gpg_status);
|
||||
if (ret)
|
||||
if (ret || signature->len == bottom)
|
||||
return error(_("gpg failed to sign the data"));
|
||||
|
||||
/* Strip CR from the line endings, in case we are on Windows. */
|
||||
|
||||
@@ -17,14 +17,14 @@ static void trim_last_path_component(struct strbuf *path)
|
||||
int i = path->len;
|
||||
|
||||
/* back up past trailing slashes, if any */
|
||||
while (i && path->buf[i - 1] == '/')
|
||||
while (i && is_dir_sep(path->buf[i - 1]))
|
||||
i--;
|
||||
|
||||
/*
|
||||
* then go backwards until a slash, or the beginning of the
|
||||
* string
|
||||
*/
|
||||
while (i && path->buf[i - 1] != '/')
|
||||
while (i && !is_dir_sep(path->buf[i - 1]))
|
||||
i--;
|
||||
|
||||
strbuf_setlen(path, i);
|
||||
|
||||
10
strbuf.c
10
strbuf.c
@@ -459,8 +459,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)
|
||||
}
|
||||
|
||||
|
||||
#define STRBUF_MAXLINK (2*PATH_MAX)
|
||||
|
||||
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
|
||||
{
|
||||
size_t oldalloc = sb->alloc;
|
||||
@@ -468,15 +466,15 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
|
||||
if (hint < 32)
|
||||
hint = 32;
|
||||
|
||||
while (hint < STRBUF_MAXLINK) {
|
||||
for (;;) {
|
||||
ssize_t len;
|
||||
|
||||
strbuf_grow(sb, hint);
|
||||
len = readlink(path, sb->buf, hint);
|
||||
strbuf_grow(sb, hint + 1);
|
||||
len = readlink(path, sb->buf, hint + 1);
|
||||
if (len < 0) {
|
||||
if (errno != ERANGE)
|
||||
break;
|
||||
} else if (len < hint) {
|
||||
} else if (len <= hint) {
|
||||
strbuf_setlen(sb, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
|
||||
# Tests for the hidden file attribute on windows
|
||||
is_hidden () {
|
||||
# Use the output of `attrib`, ignore the absolute path
|
||||
case "$(attrib "$1")" in *H*?:*) return 0;; esac
|
||||
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
51
t/t2040-checkout-symlink-attr.sh
Executable file
51
t/t2040-checkout-symlink-attr.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout symlinks with `symlink` attribute on Windows
|
||||
|
||||
Ensures that Git for Windows creates symlinks of the right type,
|
||||
as specified by the `symlink` attribute in `.gitattributes`.'
|
||||
|
||||
# Tell MSYS to create native symlinks. Without this flag test-lib's
|
||||
# prerequisite detection for SYMLINKS doesn't detect the right thing.
|
||||
MSYS=winsymlinks:nativestrict && export MSYS
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if ! test_have_prereq MINGW,SYMLINKS
|
||||
then
|
||||
skip_all='skipping $0: MinGW-only test, which requires symlink support.'
|
||||
test_done
|
||||
fi
|
||||
|
||||
# Adds a symlink to the index without clobbering the work tree.
|
||||
cache_symlink () {
|
||||
sha=$(printf '%s' "$1" | git hash-object --stdin -w) &&
|
||||
git update-index --add --cacheinfo 120000,$sha,"$2"
|
||||
}
|
||||
|
||||
# MSYS2 is very forgiving, it will resolve symlinks even if the
|
||||
# symlink type isn't correct. To make this test meaningful, try
|
||||
# them with a native, non-MSYS executable.
|
||||
cat_native () {
|
||||
filename=$(cygpath -w "$1") &&
|
||||
cmd.exe /c "type \"$filename\""
|
||||
}
|
||||
|
||||
test_expect_success 'checkout symlinks with attr' '
|
||||
cache_symlink file1 file-link &&
|
||||
cache_symlink dir dir-link &&
|
||||
|
||||
printf "file-link symlink=file\ndir-link symlink=dir\n" >.gitattributes &&
|
||||
git add .gitattributes &&
|
||||
|
||||
git checkout . &&
|
||||
|
||||
mkdir dir &&
|
||||
echo "contents1" >file1 &&
|
||||
echo "contents2" >dir/file2 &&
|
||||
|
||||
test "$(cat_native file-link)" = "contents1" &&
|
||||
test "$(cat_native dir-link/file2)" = "contents2"
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -48,7 +48,7 @@ test_expect_success 'clone -c config is available during clone' '
|
||||
# Tests for the hidden file attribute on windows
|
||||
is_hidden () {
|
||||
# Use the output of `attrib`, ignore the absolute path
|
||||
case "$(attrib "$1")" in *H*?:*) return 0;; esac
|
||||
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -1345,12 +1345,6 @@ test_expect_success GPG \
|
||||
'test_config user.signingkey BobTheMouse &&
|
||||
test_must_fail git tag -s -m tail tag-gpg-failure'
|
||||
|
||||
# try to produce invalid signature
|
||||
test_expect_success GPG \
|
||||
'git tag -s fails if gpg is misconfigured (bad signature format)' \
|
||||
'test_config gpg.program echo &&
|
||||
test_must_fail git tag -s -m tail tag-gpg-failure'
|
||||
|
||||
# try to sign with bad user.signingkey
|
||||
test_expect_success GPGSM \
|
||||
'git tag -s fails if gpgsm is misconfigured (bad key)' \
|
||||
@@ -1358,13 +1352,6 @@ test_expect_success GPGSM \
|
||||
test_config gpg.format x509 &&
|
||||
test_must_fail git tag -s -m tail tag-gpg-failure'
|
||||
|
||||
# try to produce invalid signature
|
||||
test_expect_success GPGSM \
|
||||
'git tag -s fails if gpgsm is misconfigured (bad signature format)' \
|
||||
'test_config gpg.x509.program echo &&
|
||||
test_config gpg.format x509 &&
|
||||
test_must_fail git tag -s -m tail tag-gpg-failure'
|
||||
|
||||
# try to verify without gpg:
|
||||
|
||||
rm -rf gpghome
|
||||
|
||||
@@ -1165,8 +1165,8 @@ test_expect_success $PREREQ 'in-reply-to but no threading' '
|
||||
--to=nobody@example.com \
|
||||
--in-reply-to="<in-reply-id@example.com>" \
|
||||
--no-thread \
|
||||
$patches |
|
||||
grep "In-Reply-To: <in-reply-id@example.com>"
|
||||
$patches >out &&
|
||||
grep "In-Reply-To: <in-reply-id@example.com>" out
|
||||
'
|
||||
|
||||
test_expect_success $PREREQ 'no in-reply-to and no threading' '
|
||||
|
||||
@@ -12,6 +12,12 @@ then
|
||||
test_done
|
||||
fi
|
||||
|
||||
if test_have_prereq MINGW
|
||||
then
|
||||
skip_all='skipping remote-svn tests for lack of POSIX'
|
||||
test_done
|
||||
fi
|
||||
|
||||
# Override svnrdump with our simulator
|
||||
PATH="$HOME:$PATH"
|
||||
export PATH PYTHON_PATH GIT_BUILD_DIR
|
||||
|
||||
@@ -43,14 +43,18 @@ test_expect_success 'setup repository and import' '
|
||||
|
||||
test_expect_success 'run log' "
|
||||
git reset --hard origin/a &&
|
||||
git svn log -r2 origin/trunk | grep ^r2 &&
|
||||
git svn log -r4 origin/trunk | grep ^r4 &&
|
||||
git svn log -r3 | grep ^r3
|
||||
git svn log -r2 origin/trunk >out &&
|
||||
grep ^r2 out &&
|
||||
git svn log -r4 origin/trunk >out &&
|
||||
grep ^r4 out &&
|
||||
git svn log -r3 >out &&
|
||||
grep ^r3 out
|
||||
"
|
||||
|
||||
test_expect_success 'run log against a from trunk' "
|
||||
git reset --hard origin/trunk &&
|
||||
git svn log -r3 origin/a | grep ^r3
|
||||
git svn log -r3 origin/a >out &&
|
||||
grep ^r3 out
|
||||
"
|
||||
|
||||
printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4
|
||||
|
||||
@@ -716,7 +716,12 @@ test_eval_ () {
|
||||
# be _inside_ the block to avoid polluting the "set -x" output
|
||||
#
|
||||
|
||||
test_eval_inner_ "$@" </dev/null >&3 2>&4
|
||||
if test -n "$TEST_NO_REDIRECT"
|
||||
then
|
||||
test_eval_inner_ "$@"
|
||||
else
|
||||
test_eval_inner_ "$@" </dev/null >&3 2>&4
|
||||
fi
|
||||
{
|
||||
test_eval_ret_=$?
|
||||
if want_trace
|
||||
|
||||
Reference in New Issue
Block a user