mirror of
https://github.com/git/git.git
synced 2026-01-17 14:21:57 +00:00
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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
--------
|
||||
|
||||
3
Makefile
3
Makefile
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
[](https://dev.azure.com/git/git/_build/latest?definitionId=2)
|
||||
|
||||
Git - fast, scalable, distributed revision control system
|
||||
=========================================================
|
||||
|
||||
|
||||
325
azure-pipelines.yml
Normal file
325
azure-pipelines.yml
Normal 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)
|
||||
@@ -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");
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
7
bundle.c
7
bundle.c
@@ -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)) {
|
||||
|
||||
8
cache.h
8
cache.h
@@ -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,
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
@@ -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
26
ci/mount-fileshare.sh
Executable 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"
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Build and test Git
|
||||
#
|
||||
|
||||
. ${0%/*}/lib-travisci.sh
|
||||
. ${0%/*}/lib.sh
|
||||
|
||||
ln -s "$cache_dir/.prove" t/.prove
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Perform various static code analysis checks
|
||||
#
|
||||
|
||||
. ${0%/*}/lib-travisci.sh
|
||||
. ${0%/*}/lib.sh
|
||||
|
||||
make --jobs=2 coccicheck
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
421
compat/mingw.c
421
compat/mingw.c
@@ -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)
|
||||
|
||||
101
compat/mingw.h
101
compat/mingw.h
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#include "../../git-compat-util.h"
|
||||
|
||||
struct DIR {
|
||||
typedef struct dirent_DIR {
|
||||
struct DIR base_dir; /* extend base struct DIR */
|
||||
struct dirent dd_dir; /* includes d_type */
|
||||
HANDLE dd_handle; /* FindFirstFile handle */
|
||||
int dd_stat; /* 0-based index */
|
||||
};
|
||||
char dd_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
} dirent_DIR;
|
||||
|
||||
DIR *(*opendir)(const char *dirname) = dirent_opendir;
|
||||
|
||||
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
/* convert UTF-16 name to UTF-8 */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
|
||||
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
|
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */
|
||||
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
@@ -18,41 +22,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
ent->d_type = DT_REG;
|
||||
}
|
||||
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length < MAX_PATH */
|
||||
if ((len = xutftowcs_path(pattern, name)) < 0)
|
||||
return NULL;
|
||||
|
||||
/* append optional '/' and wildcard '*' */
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '/';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(DIR));
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return dir;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dir)
|
||||
static struct dirent *dirent_readdir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF; /* No set_errno for mingw */
|
||||
@@ -79,7 +49,7 @@ struct dirent *readdir(DIR *dir)
|
||||
return &dir->dd_dir;
|
||||
}
|
||||
|
||||
int closedir(DIR *dir)
|
||||
static int dirent_closedir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF;
|
||||
@@ -90,3 +60,44 @@ int closedir(DIR *dir)
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR *dirent_opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
dirent_DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length */
|
||||
if ((len = xutftowcs_path_ex(pattern, name, MAX_LONG_PATH, -1,
|
||||
MAX_PATH - 2, core_long_paths)) < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* append optional '\' and wildcard '*'. Note: we need to use '\' as
|
||||
* Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
|
||||
*/
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '\\';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(dirent_DIR));
|
||||
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
|
||||
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
|
||||
dir->dd_dir.d_name = dir->dd_name;
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return (DIR*) dir;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
#ifndef DIRENT_H
|
||||
#define DIRENT_H
|
||||
|
||||
typedef struct DIR DIR;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_DIR 1
|
||||
#define DT_REG 2
|
||||
#define DT_LNK 3
|
||||
|
||||
struct dirent {
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char *d_name; /* file name */
|
||||
};
|
||||
|
||||
DIR *opendir(const char *dirname);
|
||||
struct dirent *readdir(DIR *dir);
|
||||
int closedir(DIR *dir);
|
||||
/*
|
||||
* Base DIR structure, contains pointers to readdir/closedir implementations so
|
||||
* that opendir may choose a concrete implementation on a call-by-call basis.
|
||||
*/
|
||||
typedef struct DIR {
|
||||
struct dirent *(*preaddir)(struct DIR *dir);
|
||||
int (*pclosedir)(struct DIR *dir);
|
||||
} DIR;
|
||||
|
||||
/* default dirent implementation */
|
||||
extern DIR *dirent_opendir(const char *dirname);
|
||||
|
||||
/* current dirent implementation */
|
||||
extern DIR *(*opendir)(const char *dirname);
|
||||
|
||||
#define readdir(dir) (dir->preaddir(dir))
|
||||
#define closedir(dir) (dir->pclosedir(dir))
|
||||
|
||||
#endif /* DIRENT_H */
|
||||
|
||||
492
compat/win32/fscache.c
Normal file
492
compat/win32/fscache.c
Normal 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
10
compat/win32/fscache.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef FSCACHE_H
|
||||
#define FSCACHE_H
|
||||
|
||||
int fscache_enable(int enable);
|
||||
#define enable_fscache(x) fscache_enable(x)
|
||||
|
||||
DIR *fscache_opendir(const char *dir);
|
||||
int fscache_lstat(const char *file_name, struct stat *buf);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
20
config.c
20
config.c
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 **);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
71
http.c
@@ -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");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
5
path.c
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
14
send-pack.c
14
send-pack.c
@@ -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
16
setup.c
@@ -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
1
t/.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
/test-results
|
||||
/.prove
|
||||
/chainlinttmp
|
||||
/out/
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -37,5 +37,4 @@ test_expect_success "fetch with $VERSION_B" '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
stop_git_daemon
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
30
t/t0029-core-unsetenvvars.sh
Executable 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
|
||||
@@ -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
102
t/t2029-checkout-long-paths.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout long paths on Windows
|
||||
|
||||
Ensures that Git for Windows can deal with long paths (>260) enabled via core.longpaths'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if test_have_prereq !MINGW
|
||||
then
|
||||
skip_all='skipping MINGW specific long paths test'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success setup '
|
||||
p=longpathxx && # -> 10
|
||||
p=$p$p$p$p$p && # -> 50
|
||||
p=$p$p$p$p$p && # -> 250
|
||||
|
||||
path=${p}/longtestfile && # -> 263 (MAX_PATH = 260)
|
||||
|
||||
blob=$(echo foobar | git hash-object -w --stdin) &&
|
||||
|
||||
printf "100644 %s 0\t%s\n" "$blob" "$path" |
|
||||
git update-index --add --index-info &&
|
||||
git commit -m initial -q
|
||||
'
|
||||
|
||||
test_expect_success 'checkout of long paths without core.longpaths fails' '
|
||||
git config core.longpaths false &&
|
||||
test_must_fail git checkout -f 2>error &&
|
||||
grep -q "Filename too long" error &&
|
||||
test ! -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
|
||||
@@ -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
32
t/t5319-pack-large-objects.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
@@ -211,5 +211,4 @@ test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
stop_git_daemon
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
32
t/t7108-reset-stdin.sh
Executable 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
105
t/t7418-submodule-long-path.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2013 Doug Kelly
|
||||
#
|
||||
|
||||
test_description='Test submodules with a path near PATH_MAX
|
||||
|
||||
This test verifies that "git submodule" initialization, update and clones work, including with recursive submodules and paths approaching PATH_MAX (260 characters on Windows)
|
||||
'
|
||||
|
||||
TEST_NO_CREATE_REPO=1
|
||||
. ./test-lib.sh
|
||||
|
||||
# cloning a submodule calls is_git_directory("$path/../.git/modules/$path"),
|
||||
# which effectively limits the maximum length to PATH_MAX / 2 minus some
|
||||
# overhead; start with 3 * 36 = 108 chars (test 2 fails if >= 110)
|
||||
longpath36=0123456789abcdefghijklmnopqrstuvwxyz
|
||||
longpath180=$longpath36$longpath36$longpath36$longpath36$longpath36
|
||||
|
||||
# the git database must fit within PATH_MAX, which limits the submodule name
|
||||
# to PATH_MAX - len(pwd) - ~90 (= len("/objects//") + 40-byte sha1 + some
|
||||
# overhead from the test case)
|
||||
pwd=$(pwd)
|
||||
pwdlen=$(echo "$pwd" | wc -c)
|
||||
longpath=$(echo $longpath180 | cut -c 1-$((170-$pwdlen)))
|
||||
|
||||
test_expect_success 'submodule with a long path' '
|
||||
git init --bare remote &&
|
||||
test_create_repo bundle1 &&
|
||||
(
|
||||
cd bundle1 &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
mkdir home &&
|
||||
(
|
||||
cd home &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git submodule add ../bundle1 $longpath &&
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home2 &&
|
||||
(
|
||||
cd home2 &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git checkout master &&
|
||||
git submodule update --init &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'recursive submodule with a long path' '
|
||||
git init --bare super &&
|
||||
test_create_repo child &&
|
||||
(
|
||||
cd child &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
test_create_repo parent &&
|
||||
(
|
||||
cd parent &&
|
||||
git submodule add ../child $longpath &&
|
||||
test_commit "aim"
|
||||
) &&
|
||||
mkdir home3 &&
|
||||
(
|
||||
cd home3 &&
|
||||
git clone ../super test &&
|
||||
cd test &&
|
||||
git submodule add ../parent foo &&
|
||||
git submodule update --init --recursive &&
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home4 &&
|
||||
(
|
||||
cd home4 &&
|
||||
git clone ../super test --recursive &&
|
||||
(
|
||||
cd test/foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -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
|
||||
|
||||
@@ -326,8 +326,4 @@ test_expect_success 'submit from worktree' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -333,8 +333,4 @@ test_expect_success SYMLINKS 'empty symlink target' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -105,8 +105,4 @@ test_expect_success 'branch with shell char' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -108,8 +108,4 @@ test_expect_failure 'two labels on the same changelist' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -98,8 +98,4 @@ test_expect_success 'no config, edited' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -542,8 +542,4 @@ test_expect_success 'submit --update-shelve' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -83,8 +83,4 @@ test_expect_success SYMLINKS 'p4 client root symlink should stay symbolic' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -259,9 +259,4 @@ test_expect_success 'importing labels with missing revisions' '
|
||||
)
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -211,8 +211,4 @@ test_expect_success 'wildcard files requiring keyword scrub' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -138,8 +138,4 @@ test_expect_success 'not preserving user with mixed authorship' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -242,8 +242,4 @@ test_expect_success P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW \
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -422,8 +422,4 @@ test_expect_success 'cleanup chmod after submit cancel' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -138,8 +138,4 @@ test_expect_failure 'move with lock taken' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -64,8 +64,4 @@ test_expect_success 'clone, then sync with exclude' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,8 +31,4 @@ test_expect_success 'EDITOR with options' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -67,8 +67,4 @@ test_expect_success 'Delete iso8859-1 encoded paths and clone' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'kill p4d' '
|
||||
kill_p4d
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user