Merge branch 'inherit-only-stdhandles'

This topic branch conflicts with the next change where we teach the
`test-tool run-command` to run the test suite, so let's merge it to
avoid those merge conflicts.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2018-10-12 23:01:34 +02:00
111 changed files with 2487 additions and 541 deletions

View File

@@ -911,6 +911,25 @@ 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.unsetenvvars::
EXPERIMENTAL, Windows-only: comma-separated list of environment
variables' names that need to be unset before spawning any other
process. Defaults to `PERL5LIB` to account for the fact that Git
for Windows insists on using its own Perl interpreter.
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
@@ -2238,6 +2257,27 @@ http.sslCAPath::
with when fetching or pushing over HTTPS. Can be overridden
by the `GIT_SSL_CAPATH` environment variable.
http.sslBackend::
Name of the SSL backend to use (e.g. "openssl" or "schannel").
This option is ignored if cURL lacks support for choosing the SSL
backend at runtime.
http.schannelCheckRevoke::
Used to enforce or disable certificate revocation checks in cURL
when http.sslBackend is set to "schannel". Defaults to `true` if
unset. Only necessary to disable this if Git consistently errors
and the message is about checking the revocation status of a
certificate. This option is ignored if cURL lacks support for
setting the relevant SSL option at runtime.
http.schannelUseSSLCAInfo::
As of cURL v7.60.0, the Secure Channel backend can use the
certificate bundle provided via `http.sslCAInfo`, but that would
override the Windows Certificate Store. Since this is not desirable
by default, Git will tell cURL not to use that bundle by default
when the `schannel` backend was configured via `http.sslBackend`,
unless `http.schannelUseSSLCAInfo` overrides this behavior.
http.pinnedpubkey::
Public key of the https service. It may either be the filename of
a PEM or DER encoded public key file or a string starting with
@@ -3756,3 +3796,9 @@ worktree.guessRemote::
such a branch exists, it is checked out and set as "upstream"
for the new branch. If no such match can be found, it falls
back to creating a new branch from the current HEAD.
sendpack.sideband::
Allows to disable the side-band-64k capability for send-pack even
when it is advertised by the server. Makes it possible to work
around a limitation in the git for windows implementation together
with the dump git protocol. Defaults to true.

View File

@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git reset' [-q] [<tree-ish>] [--] <paths>...
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
EXPERIMENTAL: 'git reset' [-q] [--stdin [-z]] [<tree-ish>]
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
@@ -97,6 +98,15 @@ OPTIONS
--quiet::
Be quiet, only report errors.
--stdin::
EXPERIMENTAL: Instead of taking list of paths from the
command line, read list of paths from the standard input.
Paths are separated by LF (i.e. one path per line) by
default.
-z::
EXPERIMENTAL: Only meaningful with `--stdin`; paths are
separated with NUL character instead of LF.
EXAMPLES
--------

View File

@@ -2087,7 +2087,7 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
$(QUIET_GEN)$(cmd_munge_script) && \
mv $@+ $@
git.res: git.rc GIT-VERSION-FILE
git.res: git.rc GIT-VERSION-FILE GIT-PREFIX
$(QUIET_RC)$(RC) \
$(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
$(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
@@ -2567,6 +2567,7 @@ GIT-BUILD-OPTIONS: FORCE
@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
@echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
@echo X=\'$(X)\' >>$@+
ifdef TEST_OUTPUT_DIRECTORY
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
endif

View File

@@ -1,3 +1,5 @@
[![Build Status](https:/dev.azure.com/git/git/_apis/build/status/test-git.git)](https://dev.azure.com/git/git/_build/latest?definitionId=2)
Git - fast, scalable, distributed revision control system
=========================================================

325
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,325 @@
resources:
- repo: self
fetchDepth: 1
phases:
- phase: linux_clang
displayName: linux-clang
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2-bin &&
export CC=clang || exit 1
ci/install-dependencies.sh
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux-clang'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- phase: linux_gcc
displayName: linux-gcc
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2-bin || exit 1
ci/install-dependencies.sh
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux-gcc'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- phase: osx_clang
displayName: osx-clang
condition: succeeded()
queue:
name: Hosted macOS
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
export CC=clang
ci/install-dependencies.sh
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'osx-clang'
platform: macOS
publishRunAttachments: false
condition: succeededOrFailed()
- phase: osx_gcc
displayName: osx-gcc
condition: succeeded()
queue:
name: Hosted macOS
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
ci/install-dependencies.sh
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'osx-gcc'
platform: macOS
publishRunAttachments: false
condition: succeededOrFailed()
- phase: gettext_poison
displayName: GETTEXT_POISON
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev &&
export jobname=GETTEXT_POISON || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'gettext-poison'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- phase: windows
displayName: Windows
condition: succeeded()
queue:
name: Hosted VS2017
timeoutInMinutes: 240
steps:
- powershell: |
# Helper to check the error level of the latest command (exit with error when appropriate)
function c() { if (!$?) { exit(1) } }
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no; c
cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\; c
}
# Add build agent's MinGit to PATH
$env:PATH = $env:AGENT_HOMEDIRECTORY +"\externals\\git\cmd;" +$env:PATH
# Helper to initialize (or update) a Git worktree
function init ($path, $url, $set_origin) {
if (Test-Path $path) {
cd $path; c
if (Test-Path .git) {
git init; c
} else {
git status
}
} else {
git init $path; c
cd $path; c
}
git config core.autocrlf false; c
git config core.untrackedCache true; c
if (($set_origin -ne 0) -and !(git config remote.origin.url)) {
git remote add origin $url; c
}
git fetch --depth=1 $url master; c
git reset --hard FETCH_HEAD; c
git clean -df; c
}
# Initialize Git for Windows' SDK
$sdk_path = "$(Build.SourcesDirectory)\git-sdk-64"
init "$sdk_path" "https://dev.azure.com/git-for-windows/git-sdk-64/_git/git-sdk-64" 0
init usr\src\build-extra https://github.com/git-for-windows/build-extra 1
cd "$(Build.SourcesDirectory)"; c
$env:HOME = "$(Build.SourcesDirectory)"
$env:MSYSTEM = "MINGW64"
git-sdk-64\git-cmd --command=usr\\bin\\bash.exe -lc @"
. ci/lib.sh
make -j10 DEVELOPER=1 NO_PERL=1 || exit 1
NO_PERL=1 NO_SVN_TESTS=1 GIT_TEST_OPTS=\"--quiet --write-junit-xml\" time make -j15 -k DEVELOPER=1 test || {
NO_PERL=1 NO_SVN_TESTS=1 GIT_TEST_OPTS=\"-i -v -x\" make -k -C t failed; exit 1
}
save_good_tree
"@
c
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
}
displayName: 'build & test'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'windows'
platform: Windows
publishRunAttachments: false
condition: succeededOrFailed()
- phase: linux32
displayName: Linux32
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get -y install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common &&
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - &&
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable" &&
sudo apt-get update &&
sudo apt-get -y install docker-ce &&
sudo AGENT_OS="$AGENT_OS" BUILD_BUILDNUMBER="$BUILD_BUILDNUMBER" BUILD_REPOSITORY_URI="$BUILD_REPOSITORY_URI" BUILD_SOURCEBRANCH="$BUILD_SOURCEBRANCH" BUILD_SOURCEVERSION="$BUILD_SOURCEVERSION" SYSTEM_PHASENAME="$SYSTEM_PHASENAME" SYSTEM_TASKDEFINITIONSURI="$SYSTEM_TASKDEFINITIONSURI" SYSTEM_TEAMPROJECT="$SYSTEM_TEAMPROJECT" CC=$CC MAKEFLAGS=-j3 bash -lxc ci/run-linux32-docker.sh || exit 1
sudo chmod a+r t/out/TEST-*.xml
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-linux32-docker.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux32'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- phase: static_analysis
displayName: StaticAnalysis
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get install -y coccinelle &&
export jobname=StaticAnalysis &&
ci/run-static-analysis.sh || exit 1
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-static-analysis.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- phase: documentation
displayName: Documentation
condition: succeeded()
queue:
name: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo rm /var/lib/apt/lists/lock &&
sudo apt-get install -y asciidoc xmlto asciidoctor &&
export ALREADY_HAVE_ASCIIDOCTOR=yes. &&
export jobname=Documentation &&
ci/test-documentation.sh || exit 1
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/test-documentation.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)

View File

@@ -239,7 +239,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
rev.diffopt.use_color = 0;
rev.diffopt.flags.ignore_dirty_submodules = 1;
out = open(file, O_CREAT | O_WRONLY, 0666);
out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (out < 0)
die(_("Could not open '%s' for writing."), file);
rev.diffopt.file = xfdopen(out, "w");

View File

@@ -1166,7 +1166,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
if (!is_local && !complete_refs_before_fetch)
transport_fetch_refs(transport, mapped_refs);
if (transport_fetch_refs(transport, mapped_refs))
die(_("could not fetch refs from %s"),
transport->url);
remote_head = find_ref_by_name(refs, "HEAD");
remote_head_points_at =

View File

@@ -1355,6 +1355,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);

View File

@@ -874,9 +874,20 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
}
}
static void handle_reset(const char *name, struct object *object)
{
int mark = get_object_mark(object);
if (mark)
printf("reset %s\nfrom :%d\n\n", name,
get_object_mark(object));
else
printf("reset %s\nfrom %s\n\n", name,
oid_to_hex(&object->oid));
}
static void handle_tags_and_duplicates(void)
{
struct commit *commit;
int i;
for (i = extra_refs.nr - 1; i >= 0; i--) {
@@ -890,9 +901,7 @@ static void handle_tags_and_duplicates(void)
if (anonymize)
name = anonymize_refname(name);
/* create refs pointing to already seen commits */
commit = (struct commit *)object;
printf("reset %s\nfrom :%d\n\n", name,
get_object_mark(&commit->object));
handle_reset(name, object);
show_progress();
break;
}

View File

@@ -642,8 +642,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
if (pack_garbage.nr > 0)
if (pack_garbage.nr > 0) {
close_all_packs(the_repository->objects);
clean_pack_garbage();
}
if (gc_write_commit_graph)
write_commit_graph_reachable(get_object_directory(), 0);

View File

@@ -155,6 +155,9 @@ static int git_init_db_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "init.templatedir"))
return git_config_pathname(&init_db_template_dir, k, v);
if (starts_with(k, "core."))
return platform_core_config(k, v, cb);
return 0;
}
@@ -361,6 +364,9 @@ int init_db(const char *git_dir, const char *real_git_dir,
}
startup_info->have_repository = 1;
/* Just look for `init.templatedir` and `core.hidedotfiles` */
git_config(git_init_db_config, NULL);
safe_create_dir(git_dir, 0);
init_is_bare_repository = is_bare_repository();

View File

@@ -2299,7 +2299,6 @@ static void init_threaded_search(void)
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
pthread_mutex_init(&to_pack.lock, NULL);
old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}

View File

@@ -408,6 +408,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (!names.nr && !po_args.quiet)
printf("Nothing new to pack.\n");
close_all_packs(the_repository->objects);
/*
* Ok we have prepared all new packfiles.
* First see if there are packs of the same name and if so

View File

@@ -24,10 +24,13 @@
#include "cache-tree.h"
#include "submodule.h"
#include "submodule-config.h"
#include "strbuf.h"
#include "quote.h"
static const char * const git_reset_usage[] = {
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
N_("EXPERIMENTAL: git reset [-q] [--stdin [-z]] [<tree-ish>]"),
N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
NULL
};
@@ -280,7 +283,9 @@ static int git_reset_config(const char *var, const char *value, void *cb)
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int reset_type = NONE, update_ref_status = 0, quiet = 0;
int patch_mode = 0, unborn;
int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn;
char **stdin_paths = NULL;
int stdin_nr = 0, stdin_alloc = 0;
const char *rev;
struct object_id oid;
struct pathspec pathspec;
@@ -302,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
OPT_BOOL('z', NULL, &nul_term_line,
N_("EXPERIMENTAL: paths are separated with NUL character")),
OPT_BOOL(0, "stdin", &read_from_stdin,
N_("EXPERIMENTAL: read paths from <stdin>")),
OPT_END()
};
@@ -311,6 +320,42 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
if (read_from_stdin) {
strbuf_getline_fn getline_fn = nul_term_line ?
strbuf_getline_nul : strbuf_getline_lf;
int flags = PATHSPEC_PREFER_FULL;
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
if (patch_mode)
die(_("--stdin is incompatible with --patch"));
if (pathspec.nr)
die(_("--stdin is incompatible with path arguments"));
while (getline_fn(&buf, stdin) != EOF) {
if (!nul_term_line && buf.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, buf.buf, NULL))
die(_("line is badly quoted"));
strbuf_swap(&buf, &unquoted);
}
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
stdin_paths[stdin_nr++] = xstrdup(buf.buf);
strbuf_reset(&buf);
}
strbuf_release(&unquoted);
strbuf_release(&buf);
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
stdin_paths[stdin_nr++] = NULL;
flags |= PATHSPEC_LITERAL_PATH;
parse_pathspec(&pathspec, 0, flags, prefix,
(const char **)stdin_paths);
} else if (nul_term_line)
die(_("-z requires --stdin"));
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
@@ -401,5 +446,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (!pathspec.nr)
remove_branch_state();
if (stdin_paths) {
while (stdin_nr)
free(stdin_paths[--stdin_nr]);
free(stdin_paths);
}
return update_ref_status;
}

View File

@@ -457,10 +457,11 @@ int create_bundle(struct bundle_header *header, const char *path,
object_array_remove_duplicates(&revs.pending);
ref_count = write_bundle_refs(bundle_fd, &revs);
if (!ref_count)
die(_("Refusing to create empty bundle."));
else if (ref_count < 0)
if (ref_count <= 0) {
if (!ref_count)
error(_("Refusing to create empty bundle."));
goto err;
}
/* write pack */
if (write_pack_data(bundle_fd, &revs)) {

View File

@@ -901,14 +901,6 @@ int use_optional_locks(void);
extern char comment_line_char;
extern int auto_comment_line_char;
/* Windows only */
enum hide_dotfiles_type {
HIDE_DOTFILES_FALSE = 0,
HIDE_DOTFILES_TRUE,
HIDE_DOTFILES_DOTGITONLY
};
extern enum hide_dotfiles_type hide_dotfiles;
enum log_refs_config {
LOG_REFS_UNSET = -1,
LOG_REFS_NONE = 0,

View File

@@ -3,7 +3,7 @@
# Install dependencies required to build and test Git on Linux and macOS
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
@@ -28,7 +28,8 @@ osx-clang|osx-gcc)
brew update --quiet
# Uncomment this if you want to run perf tests:
# brew install gnu-time
brew install git-lfs gettext
test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
brew install caskroom/cask/perforce
;;

View File

@@ -1,5 +1,49 @@
# Library of functions shared by all CI scripts
if test true = "$TRAVIS"
then
# We are running within Travis CI
CI_BRANCH="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
CI_COMMIT="$TRAVIS_COMMIT"
CI_JOB_ID="$TRAVIS_JOB_ID"
CI_JOB_NUMBER="$TRAVIS_JOB_NUMBER"
CI_OS_NAME="$TRAVIS_OS_NAME"
CI_REPO_SLUG="$TRAVIS_REPO_SLUG"
cache_dir="$HOME/travis-cache"
url_for_job_id () {
echo "https://travis-ci.org/$CI_REPO_SLUG/jobs/$1"
}
BREW_INSTALL_PACKAGES="git-lfs gettext"
export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --immediate"
elif test -n "$SYSTEM_TASKDEFINITIONSURI"
then
# We are running in Azure Pipelines
CI_BRANCH="$BUILD_SOURCEBRANCH"
CI_COMMIT="$BUILD_SOURCEVERSION"
CI_JOB_ID="$BUILD_BUILDID"
CI_JOB_NUMBER="$BUILD_BUILDNUMBER"
CI_OS_NAME="$(echo "$AGENT_OS" | tr A-Z a-z)"
test darwin != "$CI_OS_NAME" || CI_OS_NAME=osx
CI_REPO_SLUG="$(expr "$BUILD_REPOSITORY_URI" : '.*/\([^/]*/[^/]*\)$')"
CC="${CC:-gcc}"
# use a subdirectory of the cache dir (because the file share is shared
# among *all* phases)
cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
url_for_job_id () {
echo "$SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$1"
}
BREW_INSTALL_PACKAGES=
export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
export GIT_TEST_OPTS="--quiet --write-junit-xml"
fi
skip_branch_tip_with_tag () {
# Sometimes, a branch is pushed at the same time the tag that points
# at the same commit as the tip of the branch is pushed, and building
@@ -13,10 +57,10 @@ skip_branch_tip_with_tag () {
# we can skip the build because we won't be skipping a build
# of a tag.
if TAG=$(git describe --exact-match "$TRAVIS_BRANCH" 2>/dev/null) &&
test "$TAG" != "$TRAVIS_BRANCH"
if TAG=$(git describe --exact-match "$CI_BRANCH" 2>/dev/null) &&
test "$TAG" != "$CI_BRANCH"
then
echo "$(tput setaf 2)Tip of $TRAVIS_BRANCH is exactly at $TAG$(tput sgr0)"
echo "$(tput setaf 2)Tip of $CI_BRANCH is exactly at $TAG$(tput sgr0)"
exit 0
fi
}
@@ -25,7 +69,7 @@ skip_branch_tip_with_tag () {
# job if we encounter the same tree again and can provide a useful info
# message.
save_good_tree () {
echo "$(git rev-parse $TRAVIS_COMMIT^{tree}) $TRAVIS_COMMIT $TRAVIS_JOB_NUMBER $TRAVIS_JOB_ID" >>"$good_trees_file"
echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
# limit the file size
tail -1000 "$good_trees_file" >"$good_trees_file".tmp
mv "$good_trees_file".tmp "$good_trees_file"
@@ -35,7 +79,7 @@ save_good_tree () {
# successfully before (e.g. because the branch got rebased, changing only
# the commit messages).
skip_good_tree () {
if ! good_tree_info="$(grep "^$(git rev-parse $TRAVIS_COMMIT^{tree}) " "$good_trees_file")"
if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
then
# Haven't seen this tree yet, or no cached good trees file yet.
# Continue the build job.
@@ -45,18 +89,18 @@ skip_good_tree () {
echo "$good_tree_info" | {
read tree prev_good_commit prev_good_job_number prev_good_job_id
if test "$TRAVIS_JOB_ID" = "$prev_good_job_id"
if test "$CI_JOB_ID" = "$prev_good_job_id"
then
cat <<-EOF
$(tput setaf 2)Skipping build job for commit $TRAVIS_COMMIT.$(tput sgr0)
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
This commit has already been built and tested successfully by this build job.
To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF
else
cat <<-EOF
$(tput setaf 2)Skipping build job for commit $TRAVIS_COMMIT.$(tput sgr0)
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
This commit's tree has already been built and tested successfully in build job $prev_good_job_number for commit $prev_good_commit.
The log of that build job is available at https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$prev_good_job_id
The log of that build job is available at $(url_for_job_id $prev_good_job_id)
To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF
fi
@@ -81,7 +125,6 @@ check_unignored_build_artifacts ()
# and installing dependencies.
set -ex
cache_dir="$HOME/travis-cache"
good_trees_file="$cache_dir/good-trees"
mkdir -p "$cache_dir"
@@ -91,13 +134,11 @@ skip_good_tree
if test -z "$jobname"
then
jobname="$TRAVIS_OS_NAME-$CC"
jobname="$CI_OS_NAME-$CC"
fi
export DEVELOPER=1
export DEFAULT_TEST_TARGET=prove
export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --immediate"
export GIT_TEST_CLONE_2GB=YesPlease
if [ "$jobname" = linux-gcc ]; then
export CC=gcc-8

26
ci/mount-fileshare.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
die () {
echo "$*" >&2
exit 1
}
test $# = 4 ||
die "Usage: $0 <share> <username> <password> <mountpoint"
mkdir -p "$4" || die "Could not create $4"
case "$(uname -s)" in
Linux)
sudo mount -t cifs -o vers=3.0,username="$2",password="$3",dir_mode=0777,file_mode=0777,serverino "$1" "$4"
;;
Darwin)
pass="$(echo "$3" | sed -e 's/\//%2F/g' -e 's/+/%2B/g')" &&
mount -t smbfs,soft "smb://$2:$pass@${1#//}" "$4"
;;
*)
die "No support for $(uname -s)"
;;
esac ||
die "Could not mount $4"

View File

@@ -3,7 +3,7 @@
# Print output of failing tests
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
# Tracing executed commands would produce too much noise in the loop below.
set +x
@@ -69,7 +69,7 @@ do
fi
done
if [ $combined_trash_size -gt 0 ]
if [ -n "$TRAVIS_JOB_ID" -a $combined_trash_size -gt 0 ]
then
echo "------------------------------------------------------------------------"
echo "Trash directories embedded in this log can be extracted by running:"

View File

@@ -3,7 +3,7 @@
# Build and test Git
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
ln -s "$cache_dir/.prove" t/.prove

View File

@@ -3,7 +3,7 @@
# Download and run Docker image to build and test 32-bit Git
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
docker pull daald/ubuntu32:xenial

View File

@@ -3,7 +3,7 @@
# Perform various static code analysis checks
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
make --jobs=2 coccicheck

View File

@@ -6,7 +6,7 @@
# supported) and a commit hash.
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
test $# -ne 2 && echo "Unexpected number of parameters" && exit 1
test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit

View File

@@ -3,8 +3,9 @@
# Perform sanity checks on documentation and build it.
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
gem install asciidoctor
make check-builtins

View File

@@ -6,6 +6,7 @@
#include "../run-command.h"
#include "../cache.h"
#include "win32/lazyload.h"
#include "../config.h"
#include "../string-list.h"
#define HCAST(type, handle) ((type)(intptr_t)handle)
@@ -204,11 +205,52 @@ static int ask_yes_no_if_possible(const char *format, ...)
}
}
/* Windows only */
enum hide_dotfiles_type {
HIDE_DOTFILES_FALSE = 0,
HIDE_DOTFILES_TRUE,
HIDE_DOTFILES_DOTGITONLY
};
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
static char *unset_environment_variables;
int core_fscache;
int core_long_paths;
int mingw_core_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "core.hidedotfiles")) {
if (value && !strcasecmp(value, "dotgitonly"))
hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
else
hide_dotfiles = git_config_bool(var, 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;
}
if (!strcmp(var, "core.unsetenvvars")) {
free(unset_environment_variables);
unset_environment_variables = xstrdup(value);
return 0;
}
return 0;
}
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 */
@@ -237,7 +279,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);
@@ -258,8 +300,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)) {
@@ -334,9 +376,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
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 && needs_hiding(path))
return set_hidden_flag(wpath, 1);
@@ -379,7 +424,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];
open_fn_t open_fn;
va_start(args, oflags);
@@ -394,7 +439,7 @@ int mingw_open (const char *filename, int oflags, ...)
else
open_fn = _wopen;
if (xutftowcs_path(wfilename, filename) < 0)
if (xutftowcs_long_path(wfilename, filename) < 0)
return -1;
fd = open_fn(wfilename, oflags, mode);
@@ -451,10 +496,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
{
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
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;
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
@@ -473,10 +518,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
{
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
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;
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
@@ -530,45 +575,35 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
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)
{
wchar_t wdirname[MAX_PATH];
if (xutftowcs_path(wdirname, dirname) < 0)
int result;
wchar_t wdirname[MAX_LONG_PATH];
if (xutftowcs_long_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);
}
/**
* Verifies that safe_create_leading_directories() would succeed.
*/
@@ -612,8 +647,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
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)) {
@@ -684,7 +719,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;
@@ -700,7 +735,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);
@@ -708,6 +743,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);
@@ -760,8 +797,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 */
@@ -820,11 +857,20 @@ unsigned int sleep (unsigned int seconds)
char *mingw_mktemp(char *template)
{
wchar_t wtemplate[MAX_PATH];
int offset = 0;
/* we need to return the path, thus no long paths here! */
if (xutftowcs_path(wtemplate, template) < 0)
return NULL;
if (is_dir_sep(template[0]) && !is_dir_sep(template[1]) &&
iswalpha(wtemplate[0]) && wtemplate[1] == L':') {
/* We have an absolute path missing the drive prefix */
offset = 2;
}
if (!_wmktemp(wtemplate))
return NULL;
if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
if (xwcstoutf(template, wtemplate + offset, strlen(template) + 1) < 0)
return NULL;
return template;
}
@@ -889,8 +935,32 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
char *mingw_getcwd(char *pointer, int len)
{
wchar_t wpointer[MAX_PATH];
if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
HANDLE, LPWSTR, DWORD, DWORD);
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
if (!ret || ret >= ARRAY_SIZE(cwd)) {
errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
return NULL;
}
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
if (!ret && GetLastError() == ERROR_ACCESS_DENIED &&
INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
HANDLE hnd = CreateFileW(cwd, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hnd == INVALID_HANDLE_VALUE)
return NULL;
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
CloseHandle(hnd);
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
return NULL;
return pointer;
}
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
if (xwcstoutf(pointer, wpointer, len) < 0)
return NULL;
@@ -1031,14 +1101,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
int isexe, int exe_only)
{
char path[MAX_PATH];
wchar_t wpath[MAX_PATH];
snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
if (!isexe && access(path, F_OK) == 0)
if (xutftowcs_path(wpath, path) < 0)
return NULL;
if (!isexe && _waccess(wpath, F_OK) == 0)
return xstrdup(path);
path[strlen(path)-4] = '\0';
if ((!exe_only || isexe) && access(path, F_OK) == 0)
if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
wpath[wcslen(wpath)-4] = '\0';
if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) {
if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) {
path[strlen(path)-4] = '\0';
return xstrdup(path);
}
}
return NULL;
}
@@ -1175,6 +1252,27 @@ static wchar_t *make_environment_block(char **deltaenv)
return wenvblk;
}
static void do_unset_environment_variables(void)
{
static int done;
char *p = unset_environment_variables;
if (done || !p)
return;
done = 1;
for (;;) {
char *comma = strchr(p, ',');
if (comma)
*comma = '\0';
unsetenv(p);
if (!comma)
break;
p = comma + 1;
}
}
struct pinfo_t {
struct pinfo_t *next;
pid_t pid;
@@ -1215,17 +1313,25 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
const char *dir, const char *prepend_cmd,
int fhin, int fhout, int fherr)
{
STARTUPINFOW si;
static int restrict_handle_inheritance = 1;
STARTUPINFOEXW si;
PROCESS_INFORMATION pi;
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
HANDLE stdhandles[3];
DWORD stdhandles_count = 0;
SIZE_T size;
struct strbuf args;
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
BOOL ret;
HANDLE cons;
const char *(*quote_arg)(const char *arg) =
is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
do_unset_environment_variables();
/* Determine whether or not we are associated to a console */
HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE,
cons = CreateFile("CONOUT$", GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (cons == INVALID_HANDLE_VALUE) {
@@ -1249,13 +1355,28 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
CloseHandle(cons);
}
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = winansi_get_osfhandle(fhin);
si.hStdOutput = winansi_get_osfhandle(fhout);
si.hStdError = winansi_get_osfhandle(fherr);
si.StartupInfo.cb = sizeof(si);
si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
if (xutftowcs_path(wcmd, cmd) < 0)
/* The list of handles cannot contain duplicates */
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
if (stdhandles_count)
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
/* executables and the current directory don't support long paths */
if (*argv && !strcmp(cmd, *argv))
wcmd[0] = L'\0';
else if (xutftowcs_path(wcmd, cmd) < 0)
return -1;
if (dir && xutftowcs_path(wdir, dir) < 0)
return -1;
@@ -1284,16 +1405,97 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
wenvblk = make_environment_block(deltaenv);
memset(&pi, 0, sizeof(pi));
ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
wenvblk, dir ? wdir : NULL, &si, &pi);
if (restrict_handle_inheritance && stdhandles_count &&
(InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
(HeapAlloc(GetProcessHeap(), 0, size))) &&
InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
UpdateProcThreadAttribute(attr_list, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
stdhandles,
stdhandles_count * sizeof(HANDLE),
NULL, NULL)) {
si.lpAttributeList = attr_list;
flags |= EXTENDED_STARTUPINFO_PRESENT;
}
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
stdhandles_count ? TRUE : FALSE,
flags, wenvblk, dir ? wdir : NULL,
&si.StartupInfo, &pi);
/*
* On Windows 2008 R2, it seems that specifying certain types of handles
* (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
* error. Rather than playing finicky and fragile games, let's just try
* to detect this situation and simply try again without restricting any
* handle inheritance. This is still better than failing to create
* processes.
*/
if (!ret && restrict_handle_inheritance && stdhandles_count) {
DWORD err = GetLastError();
struct strbuf buf = STRBUF_INIT;
if (err != ERROR_NO_SYSTEM_RESOURCES &&
/*
* On Windows 7 and earlier, handles on pipes and character
* devices are inherited automatically, and cannot be
* specified in the thread handle list. Rather than trying
* to catch each and every corner case (and running the
* chance of *still* forgetting a few), let's just fall
* back to creating the process without trying to limit the
* handle inheritance.
*/
!(err == ERROR_INVALID_PARAMETER &&
GetVersion() >> 16 < 9200) &&
!getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
DWORD fl = 0;
int i;
setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
for (i = 0; i < stdhandles_count; i++) {
HANDLE h = stdhandles[i];
strbuf_addf(&buf, "handle #%d: %p (type %lx, "
"handle info (%d) %lx\n", i, h,
GetFileType(h),
GetHandleInformation(h, &fl),
fl);
}
strbuf_addstr(&buf, "\nThis is a bug; please report it "
"at\nhttps://github.com/git-for-windows/"
"git/issues/new\n\n"
"To suppress this warning, please set "
"the environment variable\n\n"
"\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
"\n");
}
restrict_handle_inheritance = 0;
flags &= ~EXTENDED_STARTUPINFO_PRESENT;
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
TRUE, flags, wenvblk, dir ? wdir : NULL,
&si.StartupInfo, &pi);
if (ret && buf.len) {
errno = err_win_to_posix(GetLastError());
warning("failed to restrict file handles (%ld)\n\n%s",
err, buf.buf);
}
strbuf_release(&buf);
} else if (!ret)
errno = err_win_to_posix(GetLastError());
if (si.lpAttributeList)
DeleteProcThreadAttributeList(si.lpAttributeList);
if (attr_list)
HeapFree(GetProcessHeap(), 0, attr_list);
free(wenvblk);
free(wargs);
if (!ret) {
errno = ENOENT;
if (!ret)
return -1;
}
CloseHandle(pi.hThread);
/*
@@ -1673,7 +1875,8 @@ static void ensure_socket_initialization(void)
WSAGetLastError());
for (name = libraries; *name; name++) {
ipv6_dll = LoadLibrary(*name);
ipv6_dll = LoadLibraryExA(*name, NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!ipv6_dll)
continue;
@@ -1824,8 +2027,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;
/*
@@ -2108,24 +2312,12 @@ int mingw_raise(int sig)
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) {
create_hard_link = (T) GetProcAddress(
GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
if (!create_hard_link)
create_hard_link = (T)-1;
}
if (create_hard_link == (T)-1) {
errno = ENOSYS;
return -1;
}
if (!create_hard_link(wnewpath, woldpath, NULL)) {
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
errno = err_win_to_posix(GetLastError());
return -1;
}
@@ -2325,6 +2517,68 @@ static void setup_windows_environment(void)
setenv("TERM", "cygwin", 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;
}
}
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
* mingw startup code, see init.c in mingw runtime).
@@ -2462,6 +2716,8 @@ void mingw_startup(void)
/* fix Windows specific environment settings */
setup_windows_environment();
unset_environment_variables = xstrdup("PERL5LIB");
/* initialize critical section for waitpid pinfo_t list */
InitializeCriticalSection(&pinfo_cs);
@@ -2473,6 +2729,9 @@ void mingw_startup(void)
/* 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)

View File

@@ -11,6 +11,12 @@ typedef _sigset_t sigset_t;
#undef _POSIX_THREAD_SAFE_FUNCTIONS
#endif
extern int core_fscache;
extern int core_long_paths;
extern int mingw_core_config(const char *var, const char *value, void *cb);
#define platform_core_config mingw_core_config
/*
* things that are not available in header files
*/
@@ -326,6 +332,22 @@ 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.
*/
@@ -349,7 +371,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)
@@ -390,6 +412,9 @@ int mingw_raise(int sig);
int winansi_isatty(int fd);
#define isatty winansi_isatty
int winansi_dup2(int oldfd, int newfd);
#define dup2 winansi_dup2
void winansi_init(void);
HANDLE winansi_get_osfhandle(int fd);
@@ -435,6 +460,42 @@ extern const char *program_data_config(void);
#include <inttypes.h>
#endif
/**
* 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.
*
@@ -492,17 +553,45 @@ 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);
}
/**
* 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);
}
/**

View File

@@ -29,9 +29,6 @@
#include <sys/types.h>
/* Specification. */
#include <poll.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>
@@ -55,6 +52,9 @@
# include <unistd.h>
#endif
/* Specification. */
#include "poll.h"
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
@@ -266,6 +266,20 @@ win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
return happened;
}
#include <windows.h>
#include "compat/win32/lazyload.h"
static ULONGLONG CompatGetTickCount64(void)
{
DECLARE_PROC_ADDR(kernel32.dll, ULONGLONG, GetTickCount64, void);
if (!INIT_PROC_ADDR(GetTickCount64))
return (ULONGLONG)GetTickCount();
return GetTickCount64();
}
#define GetTickCount64 CompatGetTickCount64
#else /* !MinGW */
/* Convert select(2) returned fd_sets into poll(2) revents values. */
@@ -449,7 +463,8 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
static HANDLE hEvent;
WSANETWORKEVENTS ev;
HANDLE h, handle_array[FD_SETSIZE + 2];
DWORD ret, wait_timeout, nhandles, start = 0, elapsed, orig_timeout = 0;
DWORD ret, wait_timeout, nhandles, elapsed, orig_timeout = 0;
ULONGLONG start = 0;
fd_set rfds, wfds, xfds;
BOOL poll_again;
MSG msg;
@@ -465,7 +480,7 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
if (timeout != INFTIM)
{
orig_timeout = timeout;
start = GetTickCount();
start = GetTickCount64();
}
if (!hEvent)
@@ -614,7 +629,7 @@ restart:
if (!rc && orig_timeout && timeout != INFTIM)
{
elapsed = GetTickCount() - start;
elapsed = (DWORD)(GetTickCount64() - start);
timeout = elapsed >= orig_timeout ? 0 : orig_timeout - elapsed;
}

View File

@@ -21,6 +21,21 @@
#ifndef _GL_POLL_H
#define _GL_POLL_H
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
/* Vista has its own, socket-only poll() */
#undef POLLIN
#undef POLLPRI
#undef POLLOUT
#undef POLLERR
#undef POLLHUP
#undef POLLNVAL
#undef POLLRDNORM
#undef POLLRDBAND
#undef POLLWRNORM
#undef POLLWRBAND
#define pollfd compat_pollfd
#endif
/* fake a poll(2) environment */
#define POLLIN 0x0001 /* any readable data available */
#define POLLPRI 0x0002 /* OOB/Urgent readable data */

View File

@@ -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;
}

View File

@@ -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 */

492
compat/win32/fscache.c Normal file
View File

@@ -0,0 +1,492 @@
#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(void *unused_cmp_data,
const struct fsentry *fse1, const struct fsentry *fse2,
void *unused_keydata)
{
int res;
if (fse1 == fse2)
return 0;
/* compare the list parts first */
if (fse1->list != fse2->list &&
(res = fsentry_cmp(NULL, fse1->list ? fse1->list : fse1,
fse2->list ? fse2->list : fse2, NULL)))
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);
}
/*
* Clears the cache.
*/
static void fscache_clear(void)
{
hashmap_free(&map, 1);
hashmap_init(&map, (hashmap_cmp_fn)fsentry_cmp, NULL, 0);
}
/*
* 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, NULL, 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
View 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

View File

@@ -56,141 +56,3 @@ pthread_t pthread_self(void)
t.tid = GetCurrentThreadId();
return t;
}
int pthread_cond_init(pthread_cond_t *cond, const void *unused)
{
cond->waiters = 0;
cond->was_broadcast = 0;
InitializeCriticalSection(&cond->waiters_lock);
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
if (!cond->sema)
die("CreateSemaphore() failed");
cond->continue_broadcast = CreateEvent(NULL, /* security */
FALSE, /* auto-reset */
FALSE, /* not signaled */
NULL); /* name */
if (!cond->continue_broadcast)
die("CreateEvent() failed");
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
CloseHandle(cond->sema);
CloseHandle(cond->continue_broadcast);
DeleteCriticalSection(&cond->waiters_lock);
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
{
int last_waiter;
EnterCriticalSection(&cond->waiters_lock);
cond->waiters++;
LeaveCriticalSection(&cond->waiters_lock);
/*
* Unlock external mutex and wait for signal.
* NOTE: we've held mutex locked long enough to increment
* waiters count above, so there's no problem with
* leaving mutex unlocked before we wait on semaphore.
*/
LeaveCriticalSection(mutex);
/* let's wait - ignore return value */
WaitForSingleObject(cond->sema, INFINITE);
/*
* Decrease waiters count. If we are the last waiter, then we must
* notify the broadcasting thread that it can continue.
* But if we continued due to cond_signal, we do not have to do that
* because the signaling thread knows that only one waiter continued.
*/
EnterCriticalSection(&cond->waiters_lock);
cond->waiters--;
last_waiter = cond->was_broadcast && cond->waiters == 0;
LeaveCriticalSection(&cond->waiters_lock);
if (last_waiter) {
/*
* cond_broadcast was issued while mutex was held. This means
* that all other waiters have continued, but are contending
* for the mutex at the end of this function because the
* broadcasting thread did not leave cond_broadcast, yet.
* (This is so that it can be sure that each waiter has
* consumed exactly one slice of the semaphor.)
* The last waiter must tell the broadcasting thread that it
* can go on.
*/
SetEvent(cond->continue_broadcast);
/*
* Now we go on to contend with all other waiters for
* the mutex. Auf in den Kampf!
*/
}
/* lock external mutex again */
EnterCriticalSection(mutex);
return 0;
}
/*
* IMPORTANT: This implementation requires that pthread_cond_signal
* is called while the mutex is held that is used in the corresponding
* pthread_cond_wait calls!
*/
int pthread_cond_signal(pthread_cond_t *cond)
{
int have_waiters;
EnterCriticalSection(&cond->waiters_lock);
have_waiters = cond->waiters > 0;
LeaveCriticalSection(&cond->waiters_lock);
/*
* Signal only when there are waiters
*/
if (have_waiters)
return ReleaseSemaphore(cond->sema, 1, NULL) ?
0 : err_win_to_posix(GetLastError());
else
return 0;
}
/*
* DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
* is called while the mutex is held that is used in the corresponding
* pthread_cond_wait calls!
*/
int pthread_cond_broadcast(pthread_cond_t *cond)
{
EnterCriticalSection(&cond->waiters_lock);
if ((cond->was_broadcast = cond->waiters > 0)) {
/* wake up all waiters */
ReleaseSemaphore(cond->sema, cond->waiters, NULL);
LeaveCriticalSection(&cond->waiters_lock);
/*
* At this point all waiters continue. Each one takes its
* slice of the semaphor. Now it's our turn to wait: Since
* the external mutex is held, no thread can leave cond_wait,
* yet. For this reason, we can be sure that no thread gets
* a chance to eat *more* than one slice. OTOH, it means
* that the last waiter must send us a wake-up.
*/
WaitForSingleObject(cond->continue_broadcast, INFINITE);
/*
* Since the external mutex is held, no thread can enter
* cond_wait, and, hence, it is safe to reset this flag
* without cond->waiters_lock held.
*/
cond->was_broadcast = 0;
} else {
LeaveCriticalSection(&cond->waiters_lock);
}
return 0;
}

View File

@@ -32,27 +32,24 @@ typedef int pthread_mutexattr_t;
#define pthread_mutexattr_settype(a, t) 0
#define PTHREAD_MUTEX_RECURSIVE 0
/*
* Implement simple condition variable for Windows threads, based on ACE
* implementation.
*
* See original implementation: http://bit.ly/1vkDjo
* ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
* See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
*/
typedef struct {
LONG waiters;
int was_broadcast;
CRITICAL_SECTION waiters_lock;
HANDLE sema;
HANDLE continue_broadcast;
} pthread_cond_t;
#define pthread_cond_t CONDITION_VARIABLE
extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
extern int pthread_cond_destroy(pthread_cond_t *cond);
extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
extern int pthread_cond_signal(pthread_cond_t *cond);
extern int pthread_cond_broadcast(pthread_cond_t *cond);
WINBASEAPI VOID WINAPI
InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI VOID WINAPI
WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI VOID WINAPI
WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI BOOL WINAPI
SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable,
PCRITICAL_SECTION CriticalSection,
DWORD dwMilliseconds);
#define pthread_cond_init(a,b) InitializeConditionVariable((a))
#define pthread_cond_destroy(a) do {} while (0)
#define pthread_cond_wait(a,b) return_0(SleepConditionVariableCS((a), (b), INFINITE))
#define pthread_cond_signal WakeConditionVariable
#define pthread_cond_broadcast WakeAllConditionVariable
/*
* Simple thread creation implementation using pthread API

View File

@@ -474,6 +474,18 @@ static void die_lasterr(const char *fmt, ...)
va_end(params);
}
#undef dup2
int winansi_dup2(int oldfd, int newfd)
{
int ret = dup2(oldfd, newfd);
if (!ret && newfd >= 0 && newfd <= 2)
fd_is_interactive[newfd] = oldfd < 0 || oldfd > 2 ?
0 : fd_is_interactive[oldfd];
return ret;
}
static HANDLE duplicate_handle(HANDLE hnd)
{
HANDLE hresult, hproc = GetCurrentProcess();
@@ -639,10 +651,20 @@ void winansi_init(void)
*/
HANDLE winansi_get_osfhandle(int fd)
{
HANDLE ret;
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
return hconsole1;
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
return hconsole2;
return (HANDLE)_get_osfhandle(fd);
ret = (HANDLE)_get_osfhandle(fd);
/*
* There are obviously circumstances under which _get_osfhandle()
* returns (HANDLE)-2. This is not documented anywhere, but that is so
* clearly an invalid handle value that we can just work around this
* and return the correct value for invalid handles.
*/
return ret == (HANDLE)-2 ? INVALID_HANDLE_VALUE : ret;
}

View File

@@ -1093,7 +1093,7 @@ int git_config_color(char *dest, const char *var, const char *value)
return 0;
}
static int git_default_core_config(const char *var, const char *value)
static int git_default_core_config(const char *var, const char *value, void *cb)
{
/* This needs a better name */
if (!strcmp(var, "core.filemode")) {
@@ -1344,14 +1344,6 @@ static int git_default_core_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.hidedotfiles")) {
if (value && !strcasecmp(value, "dotgitonly"))
hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
else
hide_dotfiles = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.partialclonefilter")) {
return git_config_string(&core_partial_clone_filter_default,
var, value);
@@ -1363,7 +1355,7 @@ static int git_default_core_config(const char *var, const char *value)
}
/* Add other config variables here and to Documentation/config.txt. */
return 0;
return platform_core_config(var, value, cb);
}
static int git_default_i18n_config(const char *var, const char *value)
@@ -1448,13 +1440,13 @@ static int git_default_mailmap_config(const char *var, const char *value)
return 0;
}
int git_default_config(const char *var, const char *value, void *dummy)
int git_default_config(const char *var, const char *value, void *cb)
{
if (starts_with(var, "core."))
return git_default_core_config(var, value);
return git_default_core_config(var, value, cb);
if (starts_with(var, "user."))
return git_ident_config(var, value, dummy);
return git_ident_config(var, value, cb);
if (starts_with(var, "i18n."))
return git_default_i18n_config(var, value);
@@ -1676,6 +1668,8 @@ static int do_git_config_sequence(const struct config_options *opts,
if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir);
else if (opts->git_dir)
repo_config = mkpathdup("%s/config", opts->git_dir);
else
repo_config = NULL;

View File

@@ -381,8 +381,6 @@ ifeq ($(uname_S),Windows)
NO_PYTHON = YesPlease
BLK_SHA1 = YesPlease
ETAGS_TARGET = ETAGS
NO_INET_PTON = YesPlease
NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
NATIVE_CRLF = YesPlease
DEFAULT_HELP_FORMAT = html
@@ -393,7 +391,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
@@ -529,15 +527,13 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_REGEX = YesPlease
NO_PYTHON = YesPlease
ETAGS_TARGET = ETAGS
NO_INET_PTON = YesPlease
NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
DEFAULT_HELP_FORMAT = html
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 += -DWIN32 -DPROTECT_NTFS_DEFAULT=1
EXTLIBS += -lws2_32
GITLIBS += git.res
@@ -560,11 +556,19 @@ else
ifeq ($(shell expr "$(uname_R)" : '2\.'),2)
# MSys2
prefix = /usr/
# Enable DEP
BASIC_LDFLAGS += -Wl,--nxcompat
# Enable ASLR (unless debugging)
ifneq (,$(findstring -O,$(CFLAGS)))
BASIC_LDFLAGS += -Wl,--dynamicbase
endif
ifeq (MINGW32,$(MSYSTEM))
prefix = /mingw32
BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
endif
ifeq (MINGW64,$(MSYSTEM))
prefix = /mingw64
BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
else
COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
BASIC_LDFLAGS += -Wl,--large-address-aware

View File

@@ -918,6 +918,10 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
if (protocol == PROTO_LOCAL)
path = end;
else if (protocol == PROTO_FILE && *host != '/' &&
!has_dos_drive_prefix(host) &&
offset_1st_component(host - 2) > 1)
path = host - 2; /* include the leading "//" */
else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
else

View File

@@ -75,7 +75,8 @@ static CredDeleteWT CredDeleteW;
static void load_cred_funcs(void)
{
/* load DLLs */
advapi = LoadLibrary("advapi32.dll");
advapi = LoadLibraryExA("advapi32.dll", NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!advapi)
die("failed to load advapi32.dll");

View File

@@ -136,7 +136,9 @@ static void credential_getpass(struct credential *c)
{
if (!c->username)
c->username = credential_ask_one("Username", c,
PROMPT_ASKPASS|PROMPT_ECHO);
(getenv("GIT_ASKPASS") ?
PROMPT_ASKPASS : 0) |
PROMPT_ECHO);
if (!c->password)
c->password = credential_ask_one("Password", c,
PROMPT_ASKPASS);

View File

@@ -82,6 +82,18 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
first = 0;
last = rename_src_nr;
if (last > 0) {
struct diff_rename_src *src = &(rename_src[last-1]);
int cmp = strcmp(one->path, src->p->one->path);
if (!cmp)
return src;
if (cmp > 0) {
first = last;
goto append_it;
}
}
while (last > first) {
int next = (last + first) >> 1;
struct diff_rename_src *src = &(rename_src[next]);
@@ -95,6 +107,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
first = next+1;
}
append_it:
/* insert to make it at "first" */
ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc);
rename_src_nr++;

View File

@@ -71,7 +71,6 @@ int core_apply_sparse_checkout;
int merge_log_config = -1;
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
unsigned long pack_size_limit_cfg;
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
#ifndef PROTECT_HFS_DEFAULT

View File

@@ -146,8 +146,8 @@
#define _SGI_SOURCE 1
#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
# define _WIN32_WINNT 0x0502
# if !defined(_WIN32_WINNT)
# define _WIN32_WINNT 0x0600
# endif
#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
#include <winsock2.h>
@@ -195,8 +195,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>
@@ -342,6 +344,14 @@ typedef uintmax_t timestamp_t;
#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
#endif
#ifndef platform_core_config
static inline int noop_core_config(const char *var, const char *value, void *cb)
{
return 0;
}
#define platform_core_config noop_core_config
#endif
#ifndef has_dos_drive_prefix
static inline int git_has_dos_drive_prefix(const char *path)
{
@@ -1225,6 +1235,21 @@ static inline int is_missing_file_error(int errno_)
return (errno_ == ENOENT || errno_ == ENOTDIR);
}
/*
* 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
extern int cmd_main(int, const char **);
/*

View File

@@ -431,6 +431,7 @@ END
sub safe_pipe_capture {
my @output;
if (my $pid = open my $child, '-|') {
binmode($child, ":crlf");
@output = (<$child>);
close $child or die join(' ',@_).": $! $?";
} else {

71
http.c
View File

@@ -155,6 +155,16 @@ static struct active_request_slot *active_queue_head;
static char *cached_accept_language;
static char *http_ssl_backend;
static int http_schannel_check_revoke = 1;
/*
* With the backend being set to `schannel`, setting sslCAinfo would override
* the Certificate Store in cURL v7.60.0 and later, which is not what we want
* by default.
*/
static int http_schannel_use_ssl_cainfo;
size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
{
size_t size = eltsize * nmemb;
@@ -302,6 +312,22 @@ static int http_options(const char *var, const char *value, void *cb)
curl_ssl_try = git_config_bool(var, value);
return 0;
}
if (!strcmp("http.sslbackend", var)) {
free(http_ssl_backend);
http_ssl_backend = xstrdup_or_null(value);
return 0;
}
if (!strcmp("http.schannelcheckrevoke", var)) {
http_schannel_check_revoke = git_config_bool(var, value);
return 0;
}
if (!strcmp("http.schannelusesslcainfo", var)) {
http_schannel_use_ssl_cainfo = git_config_bool(var, value);
return 0;
}
if (!strcmp("http.minsessions", var)) {
min_curl_sessions = git_config_int(var, value);
#ifndef USE_CURL_MULTI
@@ -803,6 +829,16 @@ static CURL *get_curl_handle(void)
}
#endif
if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
!http_schannel_check_revoke) {
#if LIBCURL_VERSION_NUM >= 0x072c00
curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
#else
warning("CURLSSLOPT_NO_REVOKE not applied to curl SSL options because\n"
"your curl version is too old (>= 7.44.0)");
#endif
}
if (http_proactive_auth)
init_curl_http_auth(result);
@@ -844,7 +880,13 @@ static CURL *get_curl_handle(void)
if (ssl_pinnedkey != NULL)
curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);
#endif
if (ssl_cainfo != NULL)
if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
!http_schannel_use_ssl_cainfo) {
curl_easy_setopt(result, CURLOPT_CAINFO, NULL);
#if LIBCURL_VERSION_NUM >= 0x073400
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
#endif
} else if (ssl_cainfo != NULL)
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
@@ -995,6 +1037,33 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
git_config(urlmatch_config_entry, &config);
free(normalized_url);
#if LIBCURL_VERSION_NUM >= 0x073800
if (http_ssl_backend) {
const curl_ssl_backend **backends;
struct strbuf buf = STRBUF_INIT;
int i;
switch (curl_global_sslset(-1, http_ssl_backend, &backends)) {
case CURLSSLSET_UNKNOWN_BACKEND:
strbuf_addf(&buf, _("Unsupported SSL backend '%s'. "
"Supported SSL backends:"),
http_ssl_backend);
for (i = 0; backends[i]; i++)
strbuf_addf(&buf, "\n\t%s", backends[i]->name);
die("%s", buf.buf);
case CURLSSLSET_NO_BACKENDS:
die(_("Could not set SSL backend to '%s': "
"cURL was built without SSL backends"),
http_ssl_backend);
case CURLSSLSET_TOO_LATE:
die(_("Could not set SSL backend to '%s': already set"),
http_ssl_backend);
case CURLSSLSET_OK:
break; /* Okay! */
}
}
#endif
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
die("curl_global_init failed");

View File

@@ -148,6 +148,9 @@ void prepare_packing_data(struct packing_data *pdata)
1U << OE_SIZE_BITS);
pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
1UL << OE_DELTA_SIZE_BITS);
#ifndef NO_PTHREADS
pthread_mutex_init(&pdata->lock, NULL);
#endif
}
struct object_entry *packlist_alloc(struct packing_data *pdata,

View File

@@ -356,7 +356,7 @@ static inline unsigned long oe_delta_size(struct packing_data *pack,
return e->delta_size_;
/*
* pack->detla_size[] can't be NULL because oe_set_delta_size()
* pack->delta_size[] can't be NULL because oe_set_delta_size()
* must have been called when a new delta is saved with
* oe_set_delta().
* If oe_delta() returns NULL (i.e. default state, which means

5
path.c
View File

@@ -11,6 +11,7 @@
#include "path.h"
#include "packfile.h"
#include "object-store.h"
#include "exec-cmd.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -709,6 +710,10 @@ char *expand_user_path(const char *path, int real_home)
if (path == NULL)
goto return_null;
#ifdef __MINGW32__
if (path[0] == '/')
return system_path(path + 1);
#endif
if (path[0] == '~') {
const char *first_slash = strchrnul(path, '/');
const char *username = path + 1;

View File

@@ -93,6 +93,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;
@@ -110,6 +111,7 @@ static void preload_index(struct index_state *index,
die("unable to join threaded lstat");
}
trace_performance_since(start, "preload index");
enable_fscache(0);
}
#endif

View File

@@ -38,6 +38,16 @@ int option_parse_push_signed(const struct option *opt,
die("bad %s argument: %s", opt->long_name, arg);
}
static int config_use_sideband = 1;
static int send_pack_config(const char *var, const char *value, void *unused)
{
if (!strcmp("sendpack.sideband", var))
config_use_sideband = git_config_bool(var, value);
return 0;
}
static void feed_object(const struct object_id *oid, FILE *fh, int negative)
{
if (negative && !has_sha1_file(oid->hash))
@@ -392,6 +402,8 @@ int send_pack(struct send_pack_args *args,
struct async demux;
const char *push_cert_nonce = NULL;
git_config(send_pack_config, NULL);
/* Does the other end support the reporting? */
if (server_supports("report-status"))
status_report = 1;
@@ -399,7 +411,7 @@ int send_pack(struct send_pack_args *args,
allow_deleting_refs = 1;
if (server_supports("ofs-delta"))
args->use_ofs_delta = 1;
if (server_supports("side-band-64k"))
if (config_use_sideband && server_supports("side-band-64k"))
use_sideband = 1;
if (server_supports("quiet"))
quiet_supported = 1;

16
setup.c
View File

@@ -39,7 +39,7 @@ static int abspath_part_inside_repo(char *path)
off = offset_1st_component(path);
/* check if work tree is already the prefix */
if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
if (path[wtlen] == '/') {
memmove(path, path + wtlen + 1, len - wtlen);
return 0;
@@ -59,7 +59,7 @@ static int abspath_part_inside_repo(char *path)
path++;
if (*path == '/') {
*path = '\0';
if (strcmp(real_path(path0), work_tree) == 0) {
if (fspathcmp(real_path(path0), work_tree) == 0) {
memmove(path0, path + 1, len - (path - path0));
return 0;
}
@@ -68,7 +68,7 @@ static int abspath_part_inside_repo(char *path)
}
/* check whole path */
if (strcmp(real_path(path0), work_tree) == 0) {
if (fspathcmp(real_path(path0), work_tree) == 0) {
*path0 = '\0';
return 0;
}
@@ -760,7 +760,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
set_git_dir(gitdir);
inside_git_dir = 0;
inside_work_tree = 1;
if (offset == cwd->len)
if (offset >= cwd->len)
return NULL;
/* Make "offset" point past the '/' (already the case for root dirs) */
@@ -892,7 +892,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
const char *gitdirenv;
int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
int ceil_offset = -1, min_offset = offset_1st_component(dir->buf);
dev_t current_device = 0;
int one_filesystem = 1;
@@ -920,6 +920,12 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
if (ceil_offset < 0)
ceil_offset = min_offset - 2;
if (min_offset && min_offset == dir->len &&
!is_dir_sep(dir->buf[min_offset - 1])) {
strbuf_addch(dir, '/');
min_offset++;
}
/*
* Test in the following order (relative to the dir):
* - .git (file containing "gitdir: <path>")

1
t/.gitignore vendored
View File

@@ -2,3 +2,4 @@
/test-results
/.prove
/chainlinttmp
/out/

View File

@@ -7,6 +7,7 @@ static const char *usage_msg = "\n"
" test-tool date parse [date]...\n"
" test-tool date approxidate [date]...\n"
" test-tool date timestamp [date]...\n"
" test-tool date getnanos [start-nanos]\n"
" test-tool date is64bit\n"
" test-tool date time_t-is64bit\n";
@@ -82,6 +83,15 @@ static void parse_approx_timestamp(const char **argv, struct timeval *now)
}
}
static void getnanos(const char **argv, struct timeval *now)
{
double seconds = getnanotime() / 1.0e9;
if (*argv)
seconds -= strtod(*argv, NULL);
printf("%lf\n", seconds);
}
int cmd__date(int argc, const char **argv)
{
struct timeval now;
@@ -108,6 +118,8 @@ int cmd__date(int argc, const char **argv)
parse_approxidate(argv+1, &now);
else if (!strcmp(*argv, "timestamp"))
parse_approx_timestamp(argv+1, &now);
else if (!strcmp(*argv, "getnanos"))
getnanos(argv+1, &now);
else if (!strcmp(*argv, "is64bit"))
return sizeof(timestamp_t) == 8 ? 0 : 1;
else if (!strcmp(*argv, "time_t-is64bit"))

View File

@@ -50,11 +50,58 @@ static int task_finished(int result,
return 1;
}
static int inherit_handle(const char *argv0)
{
struct child_process cp = CHILD_PROCESS_INIT;
char path[PATH_MAX];
int tmp;
/* First, open an inheritable handle */
xsnprintf(path, sizeof(path), "out-XXXXXX");
tmp = xmkstemp(path);
argv_array_pushl(&cp.args,
"test-tool", argv0, "inherited-handle-child", NULL);
cp.in = -1;
cp.no_stdout = cp.no_stderr = 1;
if (start_command(&cp) < 0)
die("Could not start child process");
/* Then close it, and try to delete it. */
close(tmp);
if (unlink(path))
die("Could not delete '%s'", path);
if (close(cp.in) < 0 || finish_command(&cp) < 0)
die("Child did not finish");
return 0;
}
static int inherit_handle_child(void)
{
struct strbuf buf = STRBUF_INIT;
if (strbuf_read(&buf, 0, 0) < 0)
die("Could not read stdin");
printf("Received %s\n", buf.buf);
strbuf_release(&buf);
return 0;
}
int cmd__run_command(int argc, const char **argv)
{
struct child_process proc = CHILD_PROCESS_INIT;
int jobs;
if (argc < 2)
return 1;
if (!strcmp(argv[1], "inherited-handle"))
exit(inherit_handle(argv[0]));
if (!strcmp(argv[1], "inherited-handle-child"))
exit(inherit_handle_child());
if (argc < 3)
return 1;
while (!strcmp(argv[1], "env")) {

View File

@@ -37,5 +37,4 @@ test_expect_success "fetch with $VERSION_B" '
test_cmp expect actual
'
stop_git_daemon
test_done

View File

@@ -10,7 +10,12 @@ GIT_TEXTDOMAINDIR="$GIT_BUILD_DIR/po/build/locale"
GIT_PO_PATH="$GIT_BUILD_DIR/po"
export GIT_TEXTDOMAINDIR GIT_PO_PATH
. "$GIT_BUILD_DIR"/git-sh-i18n
if test -n "$GIT_TEST_INSTALLED"
then
. "$(git --exec-path)"/git-sh-i18n
else
. "$GIT_BUILD_DIR"/git-sh-i18n
fi
if test_have_prereq GETTEXT && ! test_have_prereq GETTEXT_POISON
then

View File

@@ -13,7 +13,6 @@
#
# test_expect_success ...
#
# stop_git_daemon
# test_done
test_tristate GIT_TEST_GIT_DAEMON
@@ -43,7 +42,7 @@ start_git_daemon() {
mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
test_atexit 'stop_git_daemon'
say >&3 "Starting git daemon ..."
mkfifo git_daemon_output

View File

@@ -74,15 +74,6 @@ cli="$TRASH_DIRECTORY/cli"
git="$TRASH_DIRECTORY/git"
pidfile="$TRASH_DIRECTORY/p4d.pid"
# Sometimes "prove" seems to hang on exit because p4d is still running
cleanup () {
if test -f "$pidfile"
then
kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
fi
}
trap cleanup EXIT
# git p4 submit generates a temp file, which will
# not get cleaned up if the submission fails. Don't
# clutter up /tmp on the test machine.
@@ -141,6 +132,7 @@ start_p4d () {
# p4d failed to start
return 1
fi
test_atexit kill_p4d
# build a p4 user so author@example.com has an entry
p4_add_user author

View File

@@ -134,6 +134,7 @@ check_sub_test_lib_test_err () {
)
}
cat >/dev/null <<\DDD
test_expect_success 'pretend we have a fully passing test suite' "
run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF &&
for i in 1 2 3
@@ -820,6 +821,25 @@ test_expect_success 'tests clean up even on failures' "
> 1..2
EOF
"
DDD
test_expect_success 'test_atexit is run' "
test_must_fail run_sub_test_lib_test \
atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
test_expect_success 'tests clean up even after a failure' '
> ../../clean-atexit &&
test_atexit rm ../../clean-atexit &&
> ../../also-clean-atexit &&
test_atexit rm ../../also-clean-atexit &&
> ../../dont-clean-atexit &&
(exit 1)
'
test_done
EOF
test_path_exists dont-clean-atexit &&
test_path_is_missing clean-atexit &&
test_path_is_missing also-clean-atexit
"
################################################################
# Basics of the basics

View File

@@ -453,6 +453,18 @@ test_expect_success 're-init from a linked worktree' '
)
'
test_expect_success MINGW 'core.hidedotfiles = false' '
git config --global core.hidedotfiles false &&
rm -rf newdir &&
(
sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
mkdir newdir &&
cd newdir &&
git init
) &&
! is_hidden newdir/.git
'
test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
test .git = "$(cat output.txt)" &&

30
t/t0029-core-unsetenvvars.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
test_description='test the Windows-only core.unsetenvvars setting'
. ./test-lib.sh
if ! test_have_prereq MINGW
then
skip_all='skipping Windows-specific tests'
test_done
fi
test_expect_success 'setup' '
mkdir -p "$TRASH_DIRECTORY/.git/hooks" &&
write_script "$TRASH_DIRECTORY/.git/hooks/pre-commit" <<-\EOF
echo $HOBBES >&2
EOF
'
test_expect_success 'core.unsetenvvars works' '
HOBBES=Calvin &&
export HOBBES &&
git commit --allow-empty -m with 2>err &&
grep Calvin err &&
git -c core.unsetenvvars=FINDUS,HOBBES,CALVIN \
commit --allow-empty -m without 2>err &&
! grep Calvin err
'
test_done

View File

@@ -12,6 +12,10 @@ cat >hello-script <<-EOF
cat hello-script
EOF
test_expect_success MINGW 'subprocess inherits only std handles' '
test-tool run-command inherited-handle
'
test_expect_success 'start_command reports ENOENT' '
test-tool run-command start-command-ENOENT ./does-not-exist
'

102
t/t2029-checkout-long-paths.sh Executable file
View 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 ! -d longpa*
'
test_expect_success 'checkout of long paths with core.longpaths works' '
git config core.longpaths true &&
git checkout -f &&
test_path_is_file longpa*/longtestfile
'
test_expect_success 'update of long paths' '
echo frotz >>$(ls longpa*/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)"/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

View File

@@ -402,4 +402,11 @@ test_expect_success 'all statuses changed in folder if . is given' '
test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
'
test_expect_success MINGW 'path is case-insensitive' '
path="$(pwd)/BLUB" &&
touch "$path" &&
downcased="$(echo "$path" | tr A-Z a-z)" &&
git add "$downcased"
'
test_done

32
t/t5319-pack-large-objects.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
#
# Copyright (c) 2018 Johannes Schindelin
#
test_description='git pack-object with "large" deltas
'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-pack.sh
# Two similar-ish objects that we have computed deltas between.
A=01d7713666f4de822776c7622c10f1b07de280dc
B=e68fe8129b546b101aee9510c5328e7f21ca1d18
test_expect_success 'setup' '
clear_packs &&
{
pack_header 2 &&
pack_obj $A $B &&
pack_obj $B
} >ab.pack &&
pack_trailer ab.pack &&
git index-pack --stdin <ab.pack
'
test_expect_success 'repack large deltas' '
printf "%s\\n" $A $B |
GIT_TEST_OE_DELTA_SIZE=2 git pack-objects tmp-pack
'
test_done

View File

@@ -695,13 +695,22 @@ do
# file with scheme
for p in file
do
test_expect_success "fetch-pack --diag-url $p://$h/$r" '
test_expect_success !MINGW "fetch-pack --diag-url $p://$h/$r" '
check_prot_path $p://$h/$r $p "/$r"
'
test_expect_success MINGW "fetch-pack --diag-url $p://$h/$r" '
check_prot_path $p://$h/$r $p "//$h/$r"
'
test_expect_success MINGW "fetch-pack --diag-url $p:///$r" '
check_prot_path $p:///$r $p "/$r"
'
# No "/~" -> "~" conversion for file
test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
test_expect_success !MINGW "fetch-pack --diag-url $p://$h/~$r" '
check_prot_path $p://$h/~$r $p "/~$r"
'
test_expect_success MINGW "fetch-pack --diag-url $p://$h/~$r" '
check_prot_path $p://$h/~$r $p "//$h/~$r"
'
done
# file without scheme
for h in nohost nohost:12 [::1] [::1]:23 [ [:aa

View File

@@ -211,5 +211,4 @@ test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
test_cmp expect actual
'
stop_git_daemon
test_done

View File

@@ -17,14 +17,11 @@ fi
UNCPATH="$(winpwd)"
case "$UNCPATH" in
[A-Z]:*)
WITHOUTDRIVE="${UNCPATH#?:}"
# Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
# (we use forward slashes here because MSYS2 and Git accept them, and
# they are easier on the eyes)
UNCPATH="//localhost/${UNCPATH%%:*}\$/${UNCPATH#?:}"
test -d "$UNCPATH" || {
skip_all='could not access administrative share; skipping'
test_done
}
UNCPATH="//localhost/${UNCPATH%%:*}\$$WITHOUTDRIVE"
;;
*)
skip_all='skipping UNC path tests, cannot determine current path as UNC'
@@ -32,6 +29,18 @@ case "$UNCPATH" in
;;
esac
test_expect_success 'clone into absolute path lacking a drive prefix' '
USINGBACKSLASHES="$(echo "$WITHOUTDRIVE"/without-drive-prefix |
tr / \\\\)" &&
git clone . "$USINGBACKSLASHES" &&
test -f without-drive-prefix/.git/HEAD
'
test -d "$UNCPATH" || {
skip_all='could not access administrative share; skipping'
test_done
}
test_expect_success setup '
test_commit initial
'
@@ -74,4 +83,16 @@ test_expect_success MINGW 'remote nick cannot contain backslashes' '
test_i18ngrep ! "unable to access" err
'
test_expect_success 'unc alternates' '
tree="$(git rev-parse HEAD:)" &&
mkdir test-unc-alternate &&
(
cd test-unc-alternate &&
git init &&
test_must_fail git show $tree &&
echo "$UNCPATH/.git/objects" >.git/objects/info/alternates &&
git show $tree
)
'
test_done

View File

@@ -71,4 +71,8 @@ test_expect_success 'prerequisites with an empty commit message' '
git bundle verify bundle
'
test_expect_success 'try to create a bundle with empty ref count' '
test_expect_code 1 git bundle create foobar.bundle master..master
'
test_done

View File

@@ -228,7 +228,7 @@ test_expect_success 'push update refs failure' '
echo "update fail" >>file &&
git commit -a -m "update fail" &&
git rev-parse --verify testgit/origin/heads/update >expect &&
test_expect_code 1 env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \
test_must_fail env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \
git push origin update &&
git rev-parse --verify testgit/origin/heads/update >actual &&
test_cmp expect actual

32
t/t7108-reset-stdin.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
test_description='reset --stdin'
. ./test-lib.sh
test_expect_success 'reset --stdin' '
test_commit hello &&
git rm hello.t &&
test -z "$(git ls-files hello.t)" &&
echo hello.t | git reset --stdin &&
test hello.t = "$(git ls-files hello.t)"
'
test_expect_success 'reset --stdin -z' '
test_commit world &&
git rm hello.t world.t &&
test -z "$(git ls-files hello.t world.t)" &&
printf world.tQworld.tQhello.tQ | q_to_nul | git reset --stdin -z &&
printf "hello.t\nworld.t\n" >expect &&
git ls-files >actual &&
test_cmp expect actual
'
test_expect_success '--stdin requires --mixed' '
echo hello.t >list &&
test_must_fail git reset --soft --stdin <list &&
test_must_fail git reset --hard --stdin <list &&
git reset --mixed --stdin <list
'
test_done

105
t/t7418-submodule-long-path.sh Executable file
View 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

View File

@@ -556,4 +556,15 @@ test_expect_success 'merge commit gets exported with --import-marks' '
)
'
cat > expected << EOF
reset refs/heads/master
from $(git rev-parse master)
EOF
test_expect_failure 'refs are updated even if no commits need to be exported' '
git fast-export master..master > actual &&
test_cmp expected actual
'
test_done

View File

@@ -326,8 +326,4 @@ test_expect_success 'submit from worktree' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -610,8 +610,4 @@ test_expect_success 'Update a file in git side and submit to P4 using client vie
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -333,8 +333,4 @@ test_expect_success SYMLINKS 'empty symlink target' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -105,8 +105,4 @@ test_expect_success 'branch with shell char' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -108,8 +108,4 @@ test_expect_failure 'two labels on the same changelist' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -98,8 +98,4 @@ test_expect_success 'no config, edited' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -300,9 +300,4 @@ test_expect_success 'use --git-dir option and GIT_DIR' '
test_path_is_file "$git"/cli_file2.t
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -542,8 +542,4 @@ test_expect_success 'submit --update-shelve' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -83,8 +83,4 @@ test_expect_success SYMLINKS 'p4 client root symlink should stay symbolic' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -836,8 +836,4 @@ test_expect_success 'quotes on both sides' '
git_verify "cdir 1/file11" "cdir 1/file12"
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -360,8 +360,4 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -259,9 +259,4 @@ test_expect_success 'importing labels with missing revisions' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -211,8 +211,4 @@ test_expect_success 'wildcard files requiring keyword scrub' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -138,8 +138,4 @@ test_expect_success 'not preserving user with mixed authorship' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -242,8 +242,4 @@ test_expect_success P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW \
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -422,8 +422,4 @@ test_expect_success 'cleanup chmod after submit cancel' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -138,8 +138,4 @@ test_expect_failure 'move with lock taken' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -64,8 +64,4 @@ test_expect_success 'clone, then sync with exclude' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -146,8 +146,4 @@ test_expect_success 'Clone repo with self-sizing block size' '
test_line_count \> 10 log
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -53,8 +53,4 @@ test_expect_failure 'Clone UC repo with lc name' '
test_must_fail git p4 clone //depot/uc/...
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -31,8 +31,4 @@ test_expect_success 'EDITOR with options' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -193,8 +193,4 @@ test_expect_success 'Add a new file and clone path with new file (ignorecase)' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -67,8 +67,4 @@ test_expect_success 'Delete iso8859-1 encoded paths and clone' '
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -185,8 +185,4 @@ test_expect_success 'Run git p4 submit in repo configured with large file system
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -287,8 +287,4 @@ test_expect_success 'Add big files to repo and store files in LFS based on compr
)
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

View File

@@ -43,8 +43,4 @@ test_expect_failure 'clone depot with invalid UTF-16 file in non-verbose mode' '
git p4 clone --dest="$git" //depot
'
test_expect_success 'kill p4d' '
kill_p4d
'
test_done

Some files were not shown because too many files have changed in this diff Show More