mirror of
https://github.com/git-for-windows/git.git
synced 2026-03-19 02:22:21 -05:00
Merge branch 'ready-for-upstream'
This is the branch thicket of patches in Git for Windows that are considered ready for upstream. To keep them in a ready-to-submit shape, they are kept as close to the beginning of the branch thicket as possible.
This commit is contained in:
567
.github/workflows/git-artifacts.yml
vendored
Normal file
567
.github/workflows/git-artifacts.yml
vendored
Normal file
@@ -0,0 +1,567 @@
|
||||
name: git-artifacts
|
||||
|
||||
on:
|
||||
# This workflow can be triggered manually in the Actions tab, see
|
||||
# https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_only:
|
||||
description: 'Optionally restrict what artifacts to build'
|
||||
ref:
|
||||
description: 'Optionally override which branch to build'
|
||||
repository:
|
||||
description: 'Optionally override from where to fetch the specified ref'
|
||||
|
||||
env:
|
||||
GPG_OPTIONS: "--batch --yes --no-tty --list-options no-show-photos --verify-options no-show-photos --pinentry-mode loopback"
|
||||
HOME: "${{github.workspace}}\\home"
|
||||
MSYSTEM: MINGW64
|
||||
USERPROFILE: "${{github.workspace}}\\home"
|
||||
BUILD_ONLY: "${{github.event.inputs.build_only}}"
|
||||
REPOSITORY: "${{github.event.inputs.repository}}"
|
||||
REF: "${{github.event.inputs.ref}}"
|
||||
|
||||
jobs:
|
||||
bundle-artifacts:
|
||||
runs-on: windows-latest
|
||||
outputs:
|
||||
latest-sdk64-extra-build-id: ${{ steps.determine-latest-sdk64-extra-build-id.outputs.id }}
|
||||
steps:
|
||||
- name: Configure user
|
||||
shell: bash
|
||||
run:
|
||||
USER_NAME="${{github.actor}}" &&
|
||||
USER_EMAIL="${{github.actor}}@users.noreply.github.com" &&
|
||||
mkdir "$HOME" &&
|
||||
git config --global user.name "$USER_NAME" &&
|
||||
git config --global user.email "$USER_EMAIL" &&
|
||||
echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >>$GITHUB_ENV
|
||||
- name: Determine latest git-sdk-64-extra-artifacts build ID
|
||||
id: determine-latest-sdk64-extra-build-id
|
||||
shell: bash
|
||||
run: |
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=$(curl "$urlbase?definitions=29&statusFilter=completed&resultFilter=succeeded&\$top=1" |
|
||||
jq -r '.value[0].id')
|
||||
|
||||
echo "Latest ID is ${id}"
|
||||
echo "::set-output name=id::$id"
|
||||
- name: Cache git-sdk-64-build-installers
|
||||
id: cache-sdk-build-installers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: git-sdk-64-build-installers
|
||||
key: build-installers-64-${{ steps.determine-latest-sdk64-extra-build-id.outputs.id }}
|
||||
- name: Download git-sdk-64-build-installers
|
||||
if: steps.cache-sdk-build-installers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# Use Git Bash to download and unpack the artifact
|
||||
|
||||
## Get artifact
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=${{ steps.determine-latest-sdk64-extra-build-id.outputs.id }}
|
||||
download_url=$(curl "$urlbase/$id/artifacts" |
|
||||
jq -r '.value[] | select(.name == "git-sdk-64-build-installers").resource.downloadUrl')
|
||||
|
||||
curl -o artifacts.zip "$download_url"
|
||||
|
||||
## Unpack artifact
|
||||
unzip artifacts.zip
|
||||
- name: Clone build-extra
|
||||
shell: bash
|
||||
run: |
|
||||
d=git-sdk-64-build-installers/usr/src/build-extra &&
|
||||
if test ! -d $d/.git
|
||||
then
|
||||
git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d
|
||||
else
|
||||
git -C $d fetch https://github.com/git-for-windows/build-extra main &&
|
||||
git -C $d switch -C main FETCH_HEAD
|
||||
fi
|
||||
- name: Prepare home directory for GPG signing
|
||||
if: env.GPGKEY != ''
|
||||
shell: bash
|
||||
run: |
|
||||
echo '${{secrets.PRIVGPGKEY}}' | tr % '\n' | gpg $GPG_OPTIONS --import &&
|
||||
mkdir -p home &&
|
||||
git config --global gpg.program "$PWD/git-sdk-64-build-installers/usr/src/build-extra/gnupg-with-gpgkey.sh" &&
|
||||
info="$(gpg --list-keys --with-colons "${GPGKEY%% *}" | cut -d : -f 1,10 | sed -n '/^uid/{s|uid:||p;q}')" &&
|
||||
git config --global user.name "${info% <*}" &&
|
||||
git config --global user.email "<${info#*<}"
|
||||
env:
|
||||
GPGKEY: ${{secrets.GPGKEY}}
|
||||
- name: Generate bundle artifacts
|
||||
env:
|
||||
GPGKEY: ${{secrets.GPGKEY}}
|
||||
shell: powershell
|
||||
run: |
|
||||
& .\git-sdk-64-build-installers\git-cmd.exe --command=usr\bin\bash.exe -lc @"
|
||||
printf '#!/bin/sh\n\nexec /mingw64/bin/git.exe "`$@"\n' >/usr/bin/git &&
|
||||
mkdir -p bundle-artifacts &&
|
||||
|
||||
{ test -n \"`$REPOSITORY\" || REPOSITORY='${{github.repository}}'; } &&
|
||||
{ test -n \"`$REF\" || REF='${{github.ref}}'; } &&
|
||||
git -c init.defaultBranch=main init --bare &&
|
||||
git remote add -f origin https://github.com/git-for-windows/git &&
|
||||
git fetch \"https://github.com/`$REPOSITORY\" \"`$REF:`$REF\" &&
|
||||
|
||||
tag_name=\"`$(git describe --match 'v[0-9]*' FETCH_HEAD)-`$(date +%Y%m%d%H%M%S)\" &&
|
||||
echo \"prerelease-`${tag_name#v}\" >bundle-artifacts/ver &&
|
||||
echo \"`${tag_name#v}\" >bundle-artifacts/display_version &&
|
||||
echo \"`$tag_name\" >bundle-artifacts/next_version &&
|
||||
git tag `$(test -z \"`$GPGKEY\" || echo \" -s\") -m \"Snapshot build\" \"`$tag_name\" FETCH_HEAD &&
|
||||
git bundle create bundle-artifacts/git.bundle origin/main..\"`$tag_name\" &&
|
||||
|
||||
sh -x /usr/src/build-extra/please.sh mention feature \"Snapshot of `$(git show -s --pretty='tformat:%h (%s, %ad)' --date=short FETCH_HEAD)\" &&
|
||||
git -C /usr/src/build-extra bundle create \"`$PWD/bundle-artifacts/build-extra.bundle\" origin/main..main
|
||||
"@
|
||||
- name: Clean up temporary files
|
||||
if: always()
|
||||
shell: bash
|
||||
run: rm -rf home
|
||||
- name: 'Publish Pipeline Artifact: bundle-artifacts'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: bundle-artifacts
|
||||
path: bundle-artifacts
|
||||
pkg:
|
||||
runs-on: windows-latest
|
||||
needs: bundle-artifacts
|
||||
outputs:
|
||||
latest-sdk64-extra-build-id: ${{ needs.bundle-artifacts.outputs.latest-sdk64-extra-build-id }}
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- name: x86_64
|
||||
bitness: 64
|
||||
bin: /amd64
|
||||
- name: i686
|
||||
bitness: 32
|
||||
bin: ''
|
||||
steps:
|
||||
- name: Configure user
|
||||
shell: bash
|
||||
run:
|
||||
USER_NAME="${{github.actor}}" &&
|
||||
USER_EMAIL="${{github.actor}}@users.noreply.github.com" &&
|
||||
mkdir "$HOME" &&
|
||||
git config --global user.name "$USER_NAME" &&
|
||||
git config --global user.email "$USER_EMAIL" &&
|
||||
echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >>$GITHUB_ENV
|
||||
- name: Cache git-sdk-64-build-installers
|
||||
id: cache-sdk-build-installers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: git-sdk-64-build-installers
|
||||
key: build-installers-64-${{ needs.bundle-artifacts.outputs.latest-sdk64-extra-build-id }}
|
||||
- name: Download git-sdk-64-build-installers
|
||||
if: steps.cache-sdk-build-installers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# Use Git Bash to download and unpack the artifact
|
||||
|
||||
## Get artifact
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=${{ needs.pkg.outputs.latest-sdk64-extra-build-id }}
|
||||
download_url=$(curl "$urlbase/$id/artifacts" |
|
||||
jq -r '.value[] | select(.name == "git-sdk-64-build-installers").resource.downloadUrl')
|
||||
|
||||
curl -o artifacts.zip "$download_url"
|
||||
|
||||
## Unpack artifact
|
||||
unzip artifacts.zip
|
||||
- name: Download bundle-artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: bundle-artifacts
|
||||
path: bundle-artifacts
|
||||
- name: Clone and update build-extra
|
||||
shell: bash
|
||||
run: |
|
||||
d=git-sdk-64-build-installers/usr/src/build-extra &&
|
||||
if test ! -d $d/.git
|
||||
then
|
||||
git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d
|
||||
else
|
||||
git -C $d fetch https://github.com/git-for-windows/build-extra main &&
|
||||
git -C $d switch -C main FETCH_HEAD
|
||||
fi &&
|
||||
git -C $d pull "$PWD"/bundle-artifacts/build-extra.bundle main
|
||||
- name: Check out git/git
|
||||
shell: bash
|
||||
run: |
|
||||
git -c init.defaultBranch=main init &&
|
||||
git remote add -f origin https://github.com/git-for-windows/git &&
|
||||
git fetch --tags bundle-artifacts/git.bundle $(cat bundle-artifacts/next_version) &&
|
||||
git reset --hard $(cat bundle-artifacts/next_version)
|
||||
- name: Prepare home directory for code-signing
|
||||
env:
|
||||
CODESIGN_P12: ${{secrets.CODESIGN_P12}}
|
||||
CODESIGN_PASS: ${{secrets.CODESIGN_PASS}}
|
||||
if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != ''
|
||||
shell: bash
|
||||
run: |
|
||||
cd home &&
|
||||
mkdir -p .sig &&
|
||||
echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >.sig/codesign.p12 &&
|
||||
echo -n "$CODESIGN_PASS" >.sig/codesign.pass
|
||||
git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"'
|
||||
- name: Prepare home directory for GPG signing
|
||||
if: env.GPGKEY != ''
|
||||
shell: bash
|
||||
run: |
|
||||
echo '${{secrets.PRIVGPGKEY}}' | tr % '\n' | gpg $GPG_OPTIONS --import &&
|
||||
info="$(gpg --list-keys --with-colons "${GPGKEY%% *}" | cut -d : -f 1,10 | sed -n '/^uid/{s|uid:||p;q}')" &&
|
||||
git config --global user.name "${info% <*}" &&
|
||||
git config --global user.email "<${info#*<}"
|
||||
env:
|
||||
GPGKEY: ${{secrets.GPGKEY}}
|
||||
- name: Build mingw-w64-${{matrix.arch.name}}-git
|
||||
env:
|
||||
GPGKEY: "${{secrets.GPGKEY}}"
|
||||
shell: powershell
|
||||
run: |
|
||||
& git-sdk-64-build-installers\usr\bin\sh.exe -lc @"
|
||||
set -x
|
||||
# Let `cv2pdb` find the DLLs
|
||||
PATH=\"`$PATH:/C/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin${{matrix.arch.bin}}\"
|
||||
type -p mspdb140.dll || exit 1
|
||||
sh -x /usr/src/build-extra/please.sh build-mingw-w64-git --only-${{matrix.arch.bitness}}-bit --build-src-pkg -o artifacts HEAD &&
|
||||
cp bundle-artifacts/ver artifacts/ &&
|
||||
if test -n \"`$GPGKEY\"
|
||||
then
|
||||
for tar in artifacts/*.tar*
|
||||
do
|
||||
/usr/src/build-extra/gnupg-with-gpgkey.sh --detach-sign --no-armor `$tar
|
||||
done
|
||||
fi &&
|
||||
|
||||
b=`$PWD/artifacts &&
|
||||
version=`$(cat bundle-artifacts/next_version) &&
|
||||
(cd /usr/src/MINGW-packages/mingw-w64-git &&
|
||||
cp PKGBUILD.`$version PKGBUILD &&
|
||||
git commit -s -m \"mingw-w64-git: new version (`$version)\" PKGBUILD &&
|
||||
git bundle create \"`$b\"/MINGW-packages.bundle origin/main..main)
|
||||
"@
|
||||
- name: Clean up temporary files
|
||||
if: always()
|
||||
shell: bash
|
||||
run: rm -rf home
|
||||
- name: Publish mingw-w64-${{matrix.arch.name}}-git
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: pkg-${{matrix.arch.name}}
|
||||
path: artifacts
|
||||
build-arm64:
|
||||
needs: bundle-artifacts
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Configure user
|
||||
shell: bash
|
||||
run:
|
||||
USER_NAME="${{github.actor}}" &&
|
||||
USER_EMAIL="${{github.actor}}@users.noreply.github.com" &&
|
||||
mkdir -p "$HOME" &&
|
||||
git config --global user.name "$USER_NAME" &&
|
||||
git config --global user.email "$USER_EMAIL"
|
||||
- uses: actions/checkout@v2
|
||||
- name: initialize vcpkg
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vcpkg'
|
||||
path: 'compat/vcbuild/vcpkg'
|
||||
- name: download vcpkg artifacts
|
||||
shell: powershell
|
||||
run: |
|
||||
$urlbase = "https://dev.azure.com/git/git/_apis/build/builds"
|
||||
$id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=9&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
|
||||
$downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[0].resource.downloadUrl
|
||||
(New-Object Net.WebClient).DownloadFile($downloadUrl, "compat.zip")
|
||||
Expand-Archive compat.zip -DestinationPath . -Force
|
||||
Remove-Item compat.zip
|
||||
- name: add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
- name: copy dlls to root
|
||||
shell: powershell
|
||||
run: |
|
||||
& compat\vcbuild\vcpkg_copy_dlls.bat release arm64-windows
|
||||
if (!$?) { exit(1) }
|
||||
- name: generate Visual Studio solution
|
||||
shell: bash
|
||||
run: |
|
||||
cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/arm64-windows \
|
||||
-DNO_GETTEXT=YesPlease -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON -DCMAKE_GENERATOR_PLATFORM=arm64 -DVCPKG_ARCH=arm64-windows \
|
||||
-DCMAKE_INSTALL_PREFIX="`pwd`/git-arm64"
|
||||
- name: MSBuild
|
||||
run: msbuild git.sln -property:Configuration=Release
|
||||
- name: Link the Git executables
|
||||
run: msbuild INSTALL.vcxproj -property:Configuration=Release
|
||||
- name: upload build artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: arm64-artifacts
|
||||
path: ./git-arm64
|
||||
artifacts:
|
||||
runs-on: windows-latest
|
||||
needs: [pkg, build-arm64]
|
||||
strategy:
|
||||
matrix:
|
||||
artifact:
|
||||
- name: installer
|
||||
- name: portable
|
||||
fileprefix: PortableGit
|
||||
fileextension: exe
|
||||
- name: archive
|
||||
fileprefix: Git
|
||||
fileextension: tar.bz2
|
||||
- name: mingit
|
||||
fileprefix: MinGit
|
||||
fileextension: zip
|
||||
- name: mingit-busybox
|
||||
fileprefix: MinGit
|
||||
fileextension: zip
|
||||
arch:
|
||||
- name: x86_64
|
||||
bitness: 64
|
||||
arm64: false
|
||||
- name: i686
|
||||
bitness: 32
|
||||
arm64: false
|
||||
- name: i686
|
||||
bitness: 32
|
||||
arm64: true
|
||||
fail-fast: false
|
||||
env:
|
||||
MSYSTEM: MINGW${{matrix.arch.bitness}}
|
||||
steps:
|
||||
- name: Determine whether this job should be skipped
|
||||
shell: bash
|
||||
run: |
|
||||
case " $BUILD_ONLY " in
|
||||
' ') ;; # not set; build all
|
||||
*" ${{matrix.artifact.name}} "*|*" ${{matrix.artifact.name}}-${{matrix.arch.name}} "*) ;; # build this artifact
|
||||
*) echo "SKIP=true" >>$GITHUB_ENV;;
|
||||
esac
|
||||
- name: Download pkg-${{matrix.arch.name}}
|
||||
if: env.SKIP != 'true'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: pkg-${{matrix.arch.name}}
|
||||
path: pkg-${{matrix.arch.name}}
|
||||
- name: Download bundle-artifacts
|
||||
if: env.SKIP != 'true'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: bundle-artifacts
|
||||
path: bundle-artifacts
|
||||
- name: Cache git-sdk-64-build-installers
|
||||
if: env.SKIP != 'true' && matrix.arch.bitness == '64'
|
||||
id: cache-sdk64-build-installers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: git-sdk-64-build-installers
|
||||
key: build-installers-64-${{ needs.pkg.outputs.latest-sdk64-extra-build-id }}
|
||||
- name: Download git-sdk-64-build-installers
|
||||
if: env.SKIP != 'true' && matrix.arch.bitness == '64' && steps.cache-sdk64-build-installers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# Use Git Bash to download and unpack the artifact
|
||||
|
||||
## Get artifact
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=${{ needs.pkg.outputs.latest-sdk64-extra-build-id }}
|
||||
download_url="$(curl "$urlbase/$id/artifacts" |
|
||||
jq -r '.value[] | select(.name == "git-sdk-64-build-installers").resource.downloadUrl')"
|
||||
|
||||
curl -o artifacts.zip "$download_url"
|
||||
|
||||
## Unpack artifact
|
||||
unzip artifacts.zip
|
||||
- name: Determine latest git-sdk-32-extra-artifacts build ID
|
||||
if: env.SKIP != 'true' && matrix.arch.bitness == '32'
|
||||
id: determine-latest-sdk32-extra-build-id
|
||||
shell: bash
|
||||
run: |
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=$(curl "$urlbase?definitions=30&statusFilter=completed&resultFilter=succeeded&\$top=1" |
|
||||
jq -r '.value[0].id')
|
||||
|
||||
echo "Latest ID is ${id}"
|
||||
echo "::set-output name=id::$id"
|
||||
- name: Cache git-sdk-32-build-installers
|
||||
if: env.SKIP != 'true' && matrix.arch.bitness == '32'
|
||||
id: cache-sdk32-build-installers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: git-sdk-32-build-installers
|
||||
key: build-installers-32-${{ steps.determine-latest-sdk32-extra-build-id.outputs.id }}
|
||||
- name: Download git-sdk-32-build-installers
|
||||
if: env.SKIP != 'true' && matrix.arch.bitness == '32' && steps.cache-sdk32-build-installers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# Use Git Bash to download and unpack the artifact
|
||||
|
||||
## Get artifact
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=${{ steps.determine-latest-sdk32-extra-build-id.outputs.id }}
|
||||
download_url=$(curl "$urlbase/$id/artifacts" |
|
||||
jq -r '.value[] | select(.name == "git-sdk-32-build-installers").resource.downloadUrl')
|
||||
|
||||
curl -o artifacts.zip "$download_url"
|
||||
|
||||
## Unpack artifact
|
||||
unzip artifacts.zip
|
||||
- name: Download arm64 artifact
|
||||
if: matrix.arch.arm64 == true
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: arm64-artifacts
|
||||
path: ${{github.workspace}}/arm64
|
||||
- name: Clone and update build-extra
|
||||
if: env.SKIP != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
d=git-sdk-${{matrix.arch.bitness}}-build-installers/usr/src/build-extra &&
|
||||
if test ! -d $d/.git
|
||||
then
|
||||
git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d
|
||||
else
|
||||
git -C $d fetch https://github.com/git-for-windows/build-extra main &&
|
||||
git -C $d switch -C main FETCH_HEAD
|
||||
fi &&
|
||||
git -C $d pull "$PWD"/bundle-artifacts/build-extra.bundle main
|
||||
- name: Prepare home directory for code-signing
|
||||
env:
|
||||
CODESIGN_P12: ${{secrets.CODESIGN_P12}}
|
||||
CODESIGN_PASS: ${{secrets.CODESIGN_PASS}}
|
||||
if: env.SKIP != 'true' && (matrix.artifact.name == 'installer' || matrix.artifact.name == 'portable') && env.CODESIGN_P12 != '' && env.CODESIGN_PASS != ''
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p home/.sig &&
|
||||
echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >home/.sig/codesign.p12 &&
|
||||
echo -n "$CODESIGN_PASS" >home/.sig/codesign.pass &&
|
||||
git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"'
|
||||
- name: Build ${{matrix.arch.bitness}}-bit ${{matrix.artifact.name}}
|
||||
if: env.SKIP != 'true'
|
||||
shell: powershell
|
||||
run: |
|
||||
& .\git-sdk-${{matrix.arch.bitness}}-build-installers\usr\bin\bash.exe -lc @"
|
||||
set -x
|
||||
[[ \"${{matrix.arch.arm64}}\" = true ]] && ARM64=\"--include-arm64-artifacts=\\\"`$PWD/arm64\\\"\" || ARM64=
|
||||
/usr/src/build-extra/please.sh make_installers_from_mingw_w64_git `$ARM64 --version=`$(cat pkg-${{matrix.arch.name}}/ver) -o artifacts --${{matrix.artifact.name}} --pkg=pkg-${{matrix.arch.name}}/mingw-w64-${{matrix.arch.name}}-git-[0-9]*.tar.xz --pkg=pkg-${{matrix.arch.name}}/mingw-w64-${{matrix.arch.name}}-git-doc-html-[0-9]*.tar.xz &&
|
||||
if test portable = '${{matrix.artifact.name}}' && test -n \"`$(git config alias.signtool)\"
|
||||
then
|
||||
git signtool artifacts/PortableGit-*.exe
|
||||
fi &&
|
||||
openssl dgst -sha256 artifacts/${{matrix.artifact.fileprefix}}-*.${{matrix.artifact.fileextension}} | sed \"s/.* //\" >artifacts/sha-256.txt
|
||||
"@
|
||||
- name: Copy package-versions and pdbs
|
||||
if: env.SKIP != 'true' && matrix.artifact.name == 'installer'
|
||||
shell: powershell
|
||||
run: |
|
||||
& .\git-sdk-${{matrix.arch.bitness}}-build-installers\usr\bin\bash.exe -lc @"
|
||||
cp /usr/src/build-extra/installer/package-versions.txt artifacts/ &&
|
||||
|
||||
a=`$PWD/artifacts &&
|
||||
p=`$PWD/pkg-${{matrix.arch.name}} &&
|
||||
(cd /usr/src/build-extra &&
|
||||
mkdir -p cached-source-packages &&
|
||||
cp \"`$p\"/*-pdb* cached-source-packages/ &&
|
||||
GIT_CONFIG_PARAMETERS=\"'windows.sdk${{matrix.arch.bitness}}.path='\" ./please.sh bundle_pdbs --arch=${{matrix.arch.name}} --directory=\"`$a\" installer/package-versions.txt)
|
||||
"@
|
||||
- name: Clean up temporary files
|
||||
if: always() && env.SKIP != 'true'
|
||||
shell: bash
|
||||
run: rm -rf home
|
||||
|
||||
- name: Publish ${{matrix.artifact.name}}-${{matrix.arch.name}}
|
||||
if: env.SKIP != 'true' && matrix.arch.arm64 != true
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{matrix.artifact.name}}-${{matrix.arch.name}}
|
||||
path: artifacts
|
||||
|
||||
- name: Publish ${{matrix.artifact.name}}-arm64
|
||||
if: env.SKIP != 'true' && matrix.arch.arm64 == true
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ${{matrix.artifact.name}}-arm64
|
||||
path: artifacts
|
||||
nuget:
|
||||
runs-on: windows-latest
|
||||
needs: pkg
|
||||
steps:
|
||||
- name: Determine whether this job should be skipped
|
||||
shell: bash
|
||||
run: |
|
||||
case " $BUILD_ONLY " in
|
||||
' ') ;; # not set; build all
|
||||
*" nuget "*) ;; # build this artifact
|
||||
*) echo "SKIP=true" >>$GITHUB_ENV;;
|
||||
esac
|
||||
- name: Download pkg-x86_64
|
||||
if: env.SKIP != 'true'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: pkg-x86_64
|
||||
path: pkg-x86_64
|
||||
- name: Download bundle-artifacts
|
||||
if: env.SKIP != 'true'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: bundle-artifacts
|
||||
path: bundle-artifacts
|
||||
- name: Cache git-sdk-64-build-installers
|
||||
if: env.SKIP != 'true'
|
||||
id: cache-sdk-build-installers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: git-sdk-64-build-installers
|
||||
key: build-installers-64-${{ needs.pkg.outputs.latest-sdk64-extra-build-id }}
|
||||
- name: Download git-sdk-64-build-installers
|
||||
if: env.SKIP != 'true' && steps.cache-sdk-build-installers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# Use Git Bash to download and unpack the artifact
|
||||
|
||||
## Get artifact
|
||||
urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
|
||||
id=${{ needs.pkg.outputs.latest-sdk64-extra-build-id }}
|
||||
download_url=$(curl "$urlbase/$id/artifacts" |
|
||||
jq -r '.value[] | select(.name == "git-sdk-64-build-installers").resource.downloadUrl')
|
||||
|
||||
curl -o artifacts.zip "$download_url"
|
||||
|
||||
## Unpack artifact
|
||||
unzip artifacts.zip
|
||||
- name: Clone and update build-extra
|
||||
if: env.SKIP != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
d=git-sdk-64-build-installers/usr/src/build-extra &&
|
||||
if test ! -d $d/.git
|
||||
then
|
||||
git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d
|
||||
else
|
||||
git -C $d fetch https://github.com/git-for-windows/build-extra main &&
|
||||
git -C $d switch -C main FETCH_HEAD
|
||||
fi &&
|
||||
git -C $d pull "$PWD"/bundle-artifacts/build-extra.bundle main
|
||||
- uses: nuget/setup-nuget@v1
|
||||
if: env.SKIP != 'true'
|
||||
- name: Build 64-bit NuGet packages
|
||||
if: env.SKIP != 'true'
|
||||
shell: powershell
|
||||
run: |
|
||||
& .\git-sdk-64-build-installers\usr\bin\bash.exe -lc @"
|
||||
/usr/src/build-extra/please.sh make_installers_from_mingw_w64_git --version=`$(cat pkg-x86_64/ver) -o artifacts --nuget --pkg=pkg-x86_64/mingw-w64-x86_64-git-[0-9]*.tar.xz --pkg=pkg-x86_64/mingw-w64-x86_64-git-doc-html-[0-9]*.tar.xz &&
|
||||
/usr/src/build-extra/please.sh make_installers_from_mingw_w64_git --version=`$(cat pkg-x86_64/ver) -o artifacts --nuget-mingit &&
|
||||
openssl dgst -sha256 artifacts/Git*.nupkg | sed \"s/.* //\" >artifacts/sha-256.txt
|
||||
"@
|
||||
- name: Publish nuget-x86_64
|
||||
if: env.SKIP != 'true'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: nuget-x86_64
|
||||
path: artifacts
|
||||
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
@@ -164,11 +164,15 @@ jobs:
|
||||
vs-build:
|
||||
needs: ci-config
|
||||
if: needs.ci-config.outputs.enabled == 'yes'
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
MSYSTEM: MINGW64
|
||||
NO_PERL: 1
|
||||
GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64, arm64]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: download git-sdk-64-minimal
|
||||
@@ -186,6 +190,11 @@ jobs:
|
||||
## Unzip and remove the artifact
|
||||
unzip artifacts.zip
|
||||
rm artifacts.zip
|
||||
- name: initialize vcpkg
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'microsoft/vcpkg'
|
||||
path: 'compat/vcbuild/vcpkg'
|
||||
- name: download vcpkg artifacts
|
||||
shell: powershell
|
||||
run: |
|
||||
@@ -200,15 +209,15 @@ jobs:
|
||||
- name: copy dlls to root
|
||||
shell: powershell
|
||||
run: |
|
||||
& compat\vcbuild\vcpkg_copy_dlls.bat release
|
||||
& compat\vcbuild\vcpkg_copy_dlls.bat release ${{ matrix.arch }}-windows
|
||||
if (!$?) { exit(1) }
|
||||
- name: generate Visual Studio solution
|
||||
shell: bash
|
||||
run: |
|
||||
cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/x64-windows \
|
||||
-DMSGFMT_EXE=`pwd`/git-sdk-64-minimal/mingw64/bin/msgfmt.exe -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON
|
||||
cmake `pwd`/contrib/buildsystems/ -DCMAKE_PREFIX_PATH=`pwd`/compat/vcbuild/vcpkg/installed/${{ matrix.arch }}-windows \
|
||||
-DMSGFMT_EXE=`pwd`/git-sdk-64-minimal/mingw64/bin/msgfmt.exe -DPERL_TESTS=OFF -DPYTHON_TESTS=OFF -DCURL_NO_CURL_CMAKE=ON -DCMAKE_GENERATOR_PLATFORM=${{ matrix.arch }} -DVCPKG_ARCH=${{ matrix.arch }}-windows
|
||||
- name: MSBuild
|
||||
run: msbuild git.sln -property:Configuration=Release -property:Platform=x64 -maxCpuCount:4 -property:PlatformToolset=v142
|
||||
run: msbuild git.sln -property:Configuration=Release -property:Platform=${{ matrix.arch }} -maxCpuCount:4 -property:PlatformToolset=v142
|
||||
- name: bundle artifact tar
|
||||
shell: powershell
|
||||
env:
|
||||
@@ -222,7 +231,7 @@ jobs:
|
||||
- name: upload build artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: vs-artifacts
|
||||
name: vs-artifacts-${{ matrix.arch }}
|
||||
path: artifacts
|
||||
vs-test:
|
||||
runs-on: windows-latest
|
||||
@@ -241,7 +250,7 @@ jobs:
|
||||
- name: download build artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: vs-artifacts
|
||||
name: vs-artifacts-x64
|
||||
path: ${{github.workspace}}
|
||||
- name: extract build artifacts
|
||||
shell: bash
|
||||
|
||||
@@ -440,6 +440,8 @@ include::config/reset.txt[]
|
||||
|
||||
include::config/sendemail.txt[]
|
||||
|
||||
include::config/sendpack.txt[]
|
||||
|
||||
include::config/sequencer.txt[]
|
||||
|
||||
include::config/showbranch.txt[]
|
||||
|
||||
@@ -173,11 +173,13 @@ http.sslBackend::
|
||||
|
||||
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.
|
||||
when http.sslBackend is set to "schannel" via "true" and "false",
|
||||
respectively. Another accepted value is "best-effort" (the default)
|
||||
in which case revocation checks are performed, but errors due to
|
||||
revocation list distribution points that are offline are silently
|
||||
ignored, as well as errors due to certificates missing revocation
|
||||
list distribution points. 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
|
||||
|
||||
5
Documentation/config/sendpack.txt
Normal file
5
Documentation/config/sendpack.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
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.
|
||||
@@ -218,6 +218,122 @@ Further, the `git gc` command should not be combined with
|
||||
but does not take the lock in the same way as `git maintenance run`. If
|
||||
possible, use `git maintenance run --task=gc` instead of `git gc`.
|
||||
|
||||
The following sections describe the mechanisms put in place to run
|
||||
background maintenance by `git maintenance start` and how to customize
|
||||
them.
|
||||
|
||||
BACKGROUND MAINTENANCE ON POSIX SYSTEMS
|
||||
---------------------------------------
|
||||
|
||||
The standard mechanism for scheduling background tasks on POSIX systems
|
||||
is cron(8). This tool executes commands based on a given schedule. The
|
||||
current list of user-scheduled tasks can be found by running `crontab -l`.
|
||||
The schedule written by `git maintenance start` is similar to this:
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
# BEGIN GIT MAINTENANCE SCHEDULE
|
||||
# The following schedule was created by Git
|
||||
# Any edits made in this region might be
|
||||
# replaced in the future by a Git command.
|
||||
|
||||
0 1-23 * * * "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=hourly
|
||||
0 0 * * 1-6 "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=daily
|
||||
0 0 * * 0 "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=weekly
|
||||
|
||||
# END GIT MAINTENANCE SCHEDULE
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
The comments are used as a region to mark the schedule as written by Git.
|
||||
Any modifications within this region will be completely deleted by
|
||||
`git maintenance stop` or overwritten by `git maintenance start`.
|
||||
|
||||
The `crontab` entry specifies the full path of the `git` executable to
|
||||
ensure that the executed `git` command is the same one with which
|
||||
`git maintenance start` was issued independent of `PATH`. If the same user
|
||||
runs `git maintenance start` with multiple Git executables, then only the
|
||||
latest executable is used.
|
||||
|
||||
These commands use `git for-each-repo --config=maintenance.repo` to run
|
||||
`git maintenance run --schedule=<frequency>` on each repository listed in
|
||||
the multi-valued `maintenance.repo` config option. These are typically
|
||||
loaded from the user-specific global config. The `git maintenance` process
|
||||
then determines which maintenance tasks are configured to run on each
|
||||
repository with each `<frequency>` using the `maintenance.<task>.schedule`
|
||||
config options. These values are loaded from the global or repository
|
||||
config values.
|
||||
|
||||
If the config values are insufficient to achieve your desired background
|
||||
maintenance schedule, then you can create your own schedule. If you run
|
||||
`crontab -e`, then an editor will load with your user-specific `cron`
|
||||
schedule. In that editor, you can add your own schedule lines. You could
|
||||
start by adapting the default schedule listed earlier, or you could read
|
||||
the crontab(5) documentation for advanced scheduling techniques. Please
|
||||
do use the full path and `--exec-path` techniques from the default
|
||||
schedule to ensure you are executing the correct binaries in your
|
||||
schedule.
|
||||
|
||||
|
||||
BACKGROUND MAINTENANCE ON MACOS SYSTEMS
|
||||
---------------------------------------
|
||||
|
||||
While macOS technically supports `cron`, using `crontab -e` requires
|
||||
elevated privileges and the executed process does not have a full user
|
||||
context. Without a full user context, Git and its credential helpers
|
||||
cannot access stored credentials, so some maintenance tasks are not
|
||||
functional.
|
||||
|
||||
Instead, `git maintenance start` interacts with the `launchctl` tool,
|
||||
which is the recommended way to schedule timed jobs in macOS. Scheduling
|
||||
maintenance through `git maintenance (start|stop)` requires some
|
||||
`launchctl` features available only in macOS 10.11 or later.
|
||||
|
||||
Your user-specific scheduled tasks are stored as XML-formatted `.plist`
|
||||
files in `~/Library/LaunchAgents/`. You can see the currently-registered
|
||||
tasks using the following command:
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
$ ls ~/Library/LaunchAgents/org.git-scm.git*
|
||||
org.git-scm.git.daily.plist
|
||||
org.git-scm.git.hourly.plist
|
||||
org.git-scm.git.weekly.plist
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
One task is registered for each `--schedule=<frequency>` option. To
|
||||
inspect how the XML format describes each schedule, open one of these
|
||||
`.plist` files in an editor and inspect the `<array>` element following
|
||||
the `<key>StartCalendarInterval</key>` element.
|
||||
|
||||
`git maintenance start` will overwrite these files and register the
|
||||
tasks again with `launchctl`, so any customizations should be done by
|
||||
creating your own `.plist` files with distinct names. Similarly, the
|
||||
`git maintenance stop` command will unregister the tasks with `launchctl`
|
||||
and delete the `.plist` files.
|
||||
|
||||
To create more advanced customizations to your background tasks, see
|
||||
launchctl.plist(5) for more information.
|
||||
|
||||
|
||||
BACKGROUND MAINTENANCE ON WINDOWS SYSTEMS
|
||||
-----------------------------------------
|
||||
|
||||
Windows does not support `cron` and instead has its own system for
|
||||
scheduling background tasks. The `git maintenance start` command uses
|
||||
the `schtasks` command to submit tasks to this system. You can inspect
|
||||
all background tasks using the Task Scheduler application. The tasks
|
||||
added by Git have names of the form `Git Maintenance (<frequency>)`.
|
||||
The Task Scheduler GUI has ways to inspect these tasks, but you can also
|
||||
export the tasks to XML files and view the details there.
|
||||
|
||||
Note that since Git is a console application, these background tasks
|
||||
create a console window visible to the current user. This can be changed
|
||||
manually by selecting the "Run whether user is logged in or not" option
|
||||
in Task Scheduler. This change requires a password input, which is why
|
||||
`git maintenance start` does not select it by default.
|
||||
|
||||
If you want to customize the background tasks, please rename the tasks
|
||||
so future calls to `git maintenance (start|stop)` do not overwrite your
|
||||
custom tasks.
|
||||
|
||||
|
||||
GIT
|
||||
---
|
||||
|
||||
12
Makefile
12
Makefile
@@ -2509,6 +2509,13 @@ compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
|
||||
compat/nedmalloc/nedmalloc.sp: SP_EXTRA_FLAGS += -Wno-non-pointer-null
|
||||
endif
|
||||
|
||||
headless-git.o: compat/win32/headless.c
|
||||
$(QUIET_CC)$(CC) $(ALL_CFLAGS) $(COMPAT_CFLAGS) \
|
||||
-fno-stack-protector -o $@ -c -Wall -Wwrite-strings $<
|
||||
|
||||
headless-git$X: headless-git.o git.res
|
||||
$(QUIET_LINK)$(CC) $(ALL_LDFLAGS) -mwindows $(COMPAT_CFLAGS) -o $@ $^
|
||||
|
||||
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
@@ -2772,6 +2779,11 @@ ifdef GIT_TEST_INDEX_VERSION
|
||||
endif
|
||||
ifdef GIT_TEST_PERL_FATAL_WARNINGS
|
||||
@echo GIT_TEST_PERL_FATAL_WARNINGS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_PERL_FATAL_WARNINGS)))'\' >>$@+
|
||||
endif
|
||||
ifdef RUNTIME_PREFIX
|
||||
@echo RUNTIME_PREFIX=\'true\' >>$@+
|
||||
else
|
||||
@echo RUNTIME_PREFIX=\'false\' >>$@+
|
||||
endif
|
||||
@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
|
||||
|
||||
|
||||
@@ -95,6 +95,9 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (platform_strbuf_realpath(resolved, path))
|
||||
return resolved->buf;
|
||||
|
||||
strbuf_addstr(&remaining, path);
|
||||
get_root_part(resolved, &remaining);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ static unsigned long offset;
|
||||
|
||||
static int tar_umask = 002;
|
||||
|
||||
static gzFile gzip;
|
||||
|
||||
static int write_tar_filter_archive(const struct archiver *ar,
|
||||
struct archiver_args *args);
|
||||
|
||||
@@ -38,11 +40,21 @@ static int write_tar_filter_archive(const struct archiver *ar,
|
||||
#define USTAR_MAX_MTIME 077777777777ULL
|
||||
#endif
|
||||
|
||||
/* writes out the whole block, or dies if fails */
|
||||
static void write_block_or_die(const char *block) {
|
||||
if (gzip) {
|
||||
if (gzwrite(gzip, block, (unsigned) BLOCKSIZE) != BLOCKSIZE)
|
||||
die(_("gzwrite failed"));
|
||||
} else {
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* writes out the whole block, but only if it is full */
|
||||
static void write_if_needed(void)
|
||||
{
|
||||
if (offset == BLOCKSIZE) {
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +78,7 @@ static void do_write_blocked(const void *data, unsigned long size)
|
||||
write_if_needed();
|
||||
}
|
||||
while (size >= BLOCKSIZE) {
|
||||
write_or_die(1, buf, BLOCKSIZE);
|
||||
write_block_or_die(buf);
|
||||
size -= BLOCKSIZE;
|
||||
buf += BLOCKSIZE;
|
||||
}
|
||||
@@ -101,10 +113,10 @@ static void write_trailer(void)
|
||||
{
|
||||
int tail = BLOCKSIZE - offset;
|
||||
memset(block + offset, 0, tail);
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
if (tail < 2 * RECORDSIZE) {
|
||||
memset(block, 0, offset);
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,18 +458,34 @@ static int write_tar_filter_archive(const struct archiver *ar,
|
||||
filter.use_shell = 1;
|
||||
filter.in = -1;
|
||||
|
||||
if (start_command(&filter) < 0)
|
||||
die_errno(_("unable to start '%s' filter"), argv[0]);
|
||||
close(1);
|
||||
if (dup2(filter.in, 1) < 0)
|
||||
die_errno(_("unable to redirect descriptor"));
|
||||
close(filter.in);
|
||||
if (!strcmp("gzip -cn", ar->data)) {
|
||||
char outmode[4] = "wb\0";
|
||||
|
||||
if (args->compression_level >= 0 && args->compression_level <= 9)
|
||||
outmode[2] = '0' + args->compression_level;
|
||||
|
||||
gzip = gzdopen(fileno(stdout), outmode);
|
||||
if (!gzip)
|
||||
die(_("Could not gzdopen stdout"));
|
||||
} else {
|
||||
if (start_command(&filter) < 0)
|
||||
die_errno(_("unable to start '%s' filter"), argv[0]);
|
||||
close(1);
|
||||
if (dup2(filter.in, 1) < 0)
|
||||
die_errno(_("unable to redirect descriptor"));
|
||||
close(filter.in);
|
||||
}
|
||||
|
||||
r = write_tar_archive(ar, args);
|
||||
|
||||
close(1);
|
||||
if (finish_command(&filter) != 0)
|
||||
die(_("'%s' filter reported error"), argv[0]);
|
||||
if (gzip) {
|
||||
if (gzclose(gzip) != Z_OK)
|
||||
die(_("gzclose failed"));
|
||||
} else {
|
||||
close(1);
|
||||
if (finish_command(&filter) != 0)
|
||||
die(_("'%s' filter reported error"), argv[0]);
|
||||
}
|
||||
|
||||
strbuf_release(&cmd);
|
||||
return r;
|
||||
|
||||
@@ -34,6 +34,10 @@ static const char *msg_remove = N_("Removing %s\n");
|
||||
static const char *msg_would_remove = N_("Would remove %s\n");
|
||||
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
|
||||
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
|
||||
#ifndef CAN_UNLINK_MOUNT_POINTS
|
||||
static const char *msg_skip_mount_point = N_("Skipping mount point %s\n");
|
||||
static const char *msg_would_skip_mount_point = N_("Would skip mount point %s\n");
|
||||
#endif
|
||||
static const char *msg_warn_remove_failed = N_("failed to remove %s");
|
||||
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
|
||||
|
||||
@@ -171,6 +175,29 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
#ifndef CAN_UNLINK_MOUNT_POINTS
|
||||
if (!quiet) {
|
||||
quote_path(path->buf, prefix, "ed, 0);
|
||||
printf(dry_run ?
|
||||
_(msg_would_skip_mount_point) :
|
||||
_(msg_skip_mount_point), quoted.buf);
|
||||
}
|
||||
*dir_gone = 0;
|
||||
#else
|
||||
if (!dry_run && unlink(path->buf)) {
|
||||
int saved_errno = errno;
|
||||
quote_path(path->buf, prefix, "ed, 0);
|
||||
errno = saved_errno;
|
||||
warning_errno(_(msg_warn_remove_failed), quoted.buf);
|
||||
*dir_gone = 0;
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir = opendir(path->buf);
|
||||
if (!dir) {
|
||||
/* an empty dir could be removed even if it is unreadble */
|
||||
|
||||
424
builtin/gc.c
424
builtin/gc.c
@@ -1493,38 +1493,368 @@ static int maintenance_unregister(void)
|
||||
return run_command(&config_unset);
|
||||
}
|
||||
|
||||
static const char *get_frequency(enum schedule_priority schedule)
|
||||
{
|
||||
switch (schedule) {
|
||||
case SCHEDULE_HOURLY:
|
||||
return "hourly";
|
||||
case SCHEDULE_DAILY:
|
||||
return "daily";
|
||||
case SCHEDULE_WEEKLY:
|
||||
return "weekly";
|
||||
default:
|
||||
BUG("invalid schedule %d", schedule);
|
||||
}
|
||||
}
|
||||
|
||||
static char *launchctl_service_name(const char *frequency)
|
||||
{
|
||||
struct strbuf label = STRBUF_INIT;
|
||||
strbuf_addf(&label, "org.git-scm.git.%s", frequency);
|
||||
return strbuf_detach(&label, NULL);
|
||||
}
|
||||
|
||||
static char *launchctl_service_filename(const char *name)
|
||||
{
|
||||
char *expanded;
|
||||
struct strbuf filename = STRBUF_INIT;
|
||||
strbuf_addf(&filename, "~/Library/LaunchAgents/%s.plist", name);
|
||||
|
||||
expanded = expand_user_path(filename.buf, 1);
|
||||
if (!expanded)
|
||||
die(_("failed to expand path '%s'"), filename.buf);
|
||||
|
||||
strbuf_release(&filename);
|
||||
return expanded;
|
||||
}
|
||||
|
||||
static char *launchctl_get_uid(void)
|
||||
{
|
||||
return xstrfmt("gui/%d", getuid());
|
||||
}
|
||||
|
||||
static int launchctl_boot_plist(int enable, const char *filename, const char *cmd)
|
||||
{
|
||||
int result;
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
char *uid = launchctl_get_uid();
|
||||
|
||||
strvec_split(&child.args, cmd);
|
||||
if (enable)
|
||||
strvec_push(&child.args, "bootstrap");
|
||||
else
|
||||
strvec_push(&child.args, "bootout");
|
||||
strvec_push(&child.args, uid);
|
||||
strvec_push(&child.args, filename);
|
||||
|
||||
child.no_stderr = 1;
|
||||
child.no_stdout = 1;
|
||||
|
||||
if (start_command(&child))
|
||||
die(_("failed to start launchctl"));
|
||||
|
||||
result = finish_command(&child);
|
||||
|
||||
free(uid);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int launchctl_remove_plist(enum schedule_priority schedule, const char *cmd)
|
||||
{
|
||||
const char *frequency = get_frequency(schedule);
|
||||
char *name = launchctl_service_name(frequency);
|
||||
char *filename = launchctl_service_filename(name);
|
||||
int result = launchctl_boot_plist(0, filename, cmd);
|
||||
unlink(filename);
|
||||
free(filename);
|
||||
free(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int launchctl_remove_plists(const char *cmd)
|
||||
{
|
||||
return launchctl_remove_plist(SCHEDULE_HOURLY, cmd) ||
|
||||
launchctl_remove_plist(SCHEDULE_DAILY, cmd) ||
|
||||
launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
|
||||
}
|
||||
|
||||
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
|
||||
{
|
||||
FILE *plist;
|
||||
int i;
|
||||
const char *preamble, *repeat;
|
||||
const char *frequency = get_frequency(schedule);
|
||||
char *name = launchctl_service_name(frequency);
|
||||
char *filename = launchctl_service_filename(name);
|
||||
|
||||
if (safe_create_leading_directories(filename))
|
||||
die(_("failed to create directories for '%s'"), filename);
|
||||
plist = xfopen(filename, "w");
|
||||
|
||||
preamble = "<?xml version=\"1.0\"?>\n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||
"<plist version=\"1.0\">"
|
||||
"<dict>\n"
|
||||
"<key>Label</key><string>%s</string>\n"
|
||||
"<key>ProgramArguments</key>\n"
|
||||
"<array>\n"
|
||||
"<string>%s/git</string>\n"
|
||||
"<string>--exec-path=%s</string>\n"
|
||||
"<string>for-each-repo</string>\n"
|
||||
"<string>--config=maintenance.repo</string>\n"
|
||||
"<string>maintenance</string>\n"
|
||||
"<string>run</string>\n"
|
||||
"<string>--schedule=%s</string>\n"
|
||||
"</array>\n"
|
||||
"<key>StartCalendarInterval</key>\n"
|
||||
"<array>\n";
|
||||
fprintf(plist, preamble, name, exec_path, exec_path, frequency);
|
||||
|
||||
switch (schedule) {
|
||||
case SCHEDULE_HOURLY:
|
||||
repeat = "<dict>\n"
|
||||
"<key>Hour</key><integer>%d</integer>\n"
|
||||
"<key>Minute</key><integer>0</integer>\n"
|
||||
"</dict>\n";
|
||||
for (i = 1; i <= 23; i++)
|
||||
fprintf(plist, repeat, i);
|
||||
break;
|
||||
|
||||
case SCHEDULE_DAILY:
|
||||
repeat = "<dict>\n"
|
||||
"<key>Day</key><integer>%d</integer>\n"
|
||||
"<key>Hour</key><integer>0</integer>\n"
|
||||
"<key>Minute</key><integer>0</integer>\n"
|
||||
"</dict>\n";
|
||||
for (i = 1; i <= 6; i++)
|
||||
fprintf(plist, repeat, i);
|
||||
break;
|
||||
|
||||
case SCHEDULE_WEEKLY:
|
||||
fprintf(plist,
|
||||
"<dict>\n"
|
||||
"<key>Day</key><integer>0</integer>\n"
|
||||
"<key>Hour</key><integer>0</integer>\n"
|
||||
"<key>Minute</key><integer>0</integer>\n"
|
||||
"</dict>\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unreachable */
|
||||
break;
|
||||
}
|
||||
fprintf(plist, "</array>\n</dict>\n</plist>\n");
|
||||
fclose(plist);
|
||||
|
||||
/* bootout might fail if not already running, so ignore */
|
||||
launchctl_boot_plist(0, filename, cmd);
|
||||
if (launchctl_boot_plist(1, filename, cmd))
|
||||
die(_("failed to bootstrap service %s"), filename);
|
||||
|
||||
free(filename);
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int launchctl_add_plists(const char *cmd)
|
||||
{
|
||||
const char *exec_path = git_exec_path();
|
||||
|
||||
return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY, cmd) ||
|
||||
launchctl_schedule_plist(exec_path, SCHEDULE_DAILY, cmd) ||
|
||||
launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY, cmd);
|
||||
}
|
||||
|
||||
static int launchctl_update_schedule(int run_maintenance, int fd, const char *cmd)
|
||||
{
|
||||
if (run_maintenance)
|
||||
return launchctl_add_plists(cmd);
|
||||
else
|
||||
return launchctl_remove_plists(cmd);
|
||||
}
|
||||
|
||||
static char *schtasks_task_name(const char *frequency)
|
||||
{
|
||||
struct strbuf label = STRBUF_INIT;
|
||||
strbuf_addf(&label, "Git Maintenance (%s)", frequency);
|
||||
return strbuf_detach(&label, NULL);
|
||||
}
|
||||
|
||||
static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd)
|
||||
{
|
||||
int result;
|
||||
struct strvec args = STRVEC_INIT;
|
||||
const char *frequency = get_frequency(schedule);
|
||||
char *name = schtasks_task_name(frequency);
|
||||
|
||||
strvec_split(&args, cmd);
|
||||
strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
|
||||
|
||||
result = run_command_v_opt(args.v, 0);
|
||||
|
||||
strvec_clear(&args);
|
||||
free(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int schtasks_remove_tasks(const char *cmd)
|
||||
{
|
||||
return schtasks_remove_task(SCHEDULE_HOURLY, cmd) ||
|
||||
schtasks_remove_task(SCHEDULE_DAILY, cmd) ||
|
||||
schtasks_remove_task(SCHEDULE_WEEKLY, cmd);
|
||||
}
|
||||
|
||||
static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule, const char *cmd)
|
||||
{
|
||||
int result;
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
const char *xml;
|
||||
struct tempfile *tfile;
|
||||
const char *frequency = get_frequency(schedule);
|
||||
char *name = schtasks_task_name(frequency);
|
||||
struct strbuf tfilename = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX",
|
||||
get_git_common_dir(), frequency);
|
||||
tfile = xmks_tempfile(tfilename.buf);
|
||||
strbuf_release(&tfilename);
|
||||
|
||||
if (!fdopen_tempfile(tfile, "w"))
|
||||
die(_("failed to create temp xml file"));
|
||||
|
||||
xml = "<?xml version=\"1.0\" ?>\n"
|
||||
"<Task version=\"1.4\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n"
|
||||
"<Triggers>\n"
|
||||
"<CalendarTrigger>\n";
|
||||
fputs(xml, tfile->fp);
|
||||
|
||||
switch (schedule) {
|
||||
case SCHEDULE_HOURLY:
|
||||
fprintf(tfile->fp,
|
||||
"<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n"
|
||||
"<Enabled>true</Enabled>\n"
|
||||
"<ScheduleByDay>\n"
|
||||
"<DaysInterval>1</DaysInterval>\n"
|
||||
"</ScheduleByDay>\n"
|
||||
"<Repetition>\n"
|
||||
"<Interval>PT1H</Interval>\n"
|
||||
"<Duration>PT23H</Duration>\n"
|
||||
"<StopAtDurationEnd>false</StopAtDurationEnd>\n"
|
||||
"</Repetition>\n");
|
||||
break;
|
||||
|
||||
case SCHEDULE_DAILY:
|
||||
fprintf(tfile->fp,
|
||||
"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
|
||||
"<Enabled>true</Enabled>\n"
|
||||
"<ScheduleByWeek>\n"
|
||||
"<DaysOfWeek>\n"
|
||||
"<Monday />\n"
|
||||
"<Tuesday />\n"
|
||||
"<Wednesday />\n"
|
||||
"<Thursday />\n"
|
||||
"<Friday />\n"
|
||||
"<Saturday />\n"
|
||||
"</DaysOfWeek>\n"
|
||||
"<WeeksInterval>1</WeeksInterval>\n"
|
||||
"</ScheduleByWeek>\n");
|
||||
break;
|
||||
|
||||
case SCHEDULE_WEEKLY:
|
||||
fprintf(tfile->fp,
|
||||
"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
|
||||
"<Enabled>true</Enabled>\n"
|
||||
"<ScheduleByWeek>\n"
|
||||
"<DaysOfWeek>\n"
|
||||
"<Sunday />\n"
|
||||
"</DaysOfWeek>\n"
|
||||
"<WeeksInterval>1</WeeksInterval>\n"
|
||||
"</ScheduleByWeek>\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
xml = "</CalendarTrigger>\n"
|
||||
"</Triggers>\n"
|
||||
"<Principals>\n"
|
||||
"<Principal id=\"Author\">\n"
|
||||
"<LogonType>InteractiveToken</LogonType>\n"
|
||||
"<RunLevel>LeastPrivilege</RunLevel>\n"
|
||||
"</Principal>\n"
|
||||
"</Principals>\n"
|
||||
"<Settings>\n"
|
||||
"<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n"
|
||||
"<Enabled>true</Enabled>\n"
|
||||
"<Hidden>true</Hidden>\n"
|
||||
"<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>\n"
|
||||
"<WakeToRun>false</WakeToRun>\n"
|
||||
"<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>\n"
|
||||
"<Priority>7</Priority>\n"
|
||||
"</Settings>\n"
|
||||
"<Actions Context=\"Author\">\n"
|
||||
"<Exec>\n"
|
||||
"<Command>\"%s\\headless-git.exe\"</Command>\n"
|
||||
"<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
|
||||
"</Exec>\n"
|
||||
"</Actions>\n"
|
||||
"</Task>\n";
|
||||
fprintf(tfile->fp, xml, exec_path, exec_path, frequency);
|
||||
strvec_split(&child.args, cmd);
|
||||
strvec_pushl(&child.args, "/create", "/tn", name, "/f", "/xml",
|
||||
get_tempfile_path(tfile), NULL);
|
||||
close_tempfile_gently(tfile);
|
||||
|
||||
child.no_stdout = 1;
|
||||
child.no_stderr = 1;
|
||||
|
||||
if (start_command(&child))
|
||||
die(_("failed to start schtasks"));
|
||||
result = finish_command(&child);
|
||||
|
||||
delete_tempfile(&tfile);
|
||||
free(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int schtasks_schedule_tasks(const char *cmd)
|
||||
{
|
||||
const char *exec_path = git_exec_path();
|
||||
|
||||
return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY, cmd) ||
|
||||
schtasks_schedule_task(exec_path, SCHEDULE_DAILY, cmd) ||
|
||||
schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY, cmd);
|
||||
}
|
||||
|
||||
static int schtasks_update_schedule(int run_maintenance, int fd, const char *cmd)
|
||||
{
|
||||
if (run_maintenance)
|
||||
return schtasks_schedule_tasks(cmd);
|
||||
else
|
||||
return schtasks_remove_tasks(cmd);
|
||||
}
|
||||
|
||||
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
|
||||
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
|
||||
|
||||
static int update_background_schedule(int run_maintenance)
|
||||
static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
|
||||
{
|
||||
int result = 0;
|
||||
int in_old_region = 0;
|
||||
struct child_process crontab_list = CHILD_PROCESS_INIT;
|
||||
struct child_process crontab_edit = CHILD_PROCESS_INIT;
|
||||
FILE *cron_list, *cron_in;
|
||||
const char *crontab_name;
|
||||
struct strbuf line = STRBUF_INIT;
|
||||
struct lock_file lk;
|
||||
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
|
||||
|
||||
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
|
||||
return error(_("another process is scheduling background maintenance"));
|
||||
|
||||
crontab_name = getenv("GIT_TEST_CRONTAB");
|
||||
if (!crontab_name)
|
||||
crontab_name = "crontab";
|
||||
|
||||
strvec_split(&crontab_list.args, crontab_name);
|
||||
strvec_split(&crontab_list.args, cmd);
|
||||
strvec_push(&crontab_list.args, "-l");
|
||||
crontab_list.in = -1;
|
||||
crontab_list.out = dup(get_lock_file_fd(&lk));
|
||||
crontab_list.out = dup(fd);
|
||||
crontab_list.git_cmd = 0;
|
||||
|
||||
if (start_command(&crontab_list)) {
|
||||
result = error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
|
||||
goto cleanup;
|
||||
}
|
||||
if (start_command(&crontab_list))
|
||||
return error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
|
||||
|
||||
/* Ignore exit code, as an empty crontab will return error. */
|
||||
finish_command(&crontab_list);
|
||||
@@ -1533,17 +1863,15 @@ static int update_background_schedule(int run_maintenance)
|
||||
* Read from the .lock file, filtering out the old
|
||||
* schedule while appending the new schedule.
|
||||
*/
|
||||
cron_list = fdopen(get_lock_file_fd(&lk), "r");
|
||||
cron_list = fdopen(fd, "r");
|
||||
rewind(cron_list);
|
||||
|
||||
strvec_split(&crontab_edit.args, crontab_name);
|
||||
strvec_split(&crontab_edit.args, cmd);
|
||||
crontab_edit.in = -1;
|
||||
crontab_edit.git_cmd = 0;
|
||||
|
||||
if (start_command(&crontab_edit)) {
|
||||
result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
|
||||
goto cleanup;
|
||||
}
|
||||
if (start_command(&crontab_edit))
|
||||
return error(_("failed to run 'crontab'; your system might not support 'cron'"));
|
||||
|
||||
cron_in = fdopen(crontab_edit.in, "w");
|
||||
if (!cron_in) {
|
||||
@@ -1587,14 +1915,54 @@ static int update_background_schedule(int run_maintenance)
|
||||
close(crontab_edit.in);
|
||||
|
||||
done_editing:
|
||||
if (finish_command(&crontab_edit)) {
|
||||
if (finish_command(&crontab_edit))
|
||||
result = error(_("'crontab' died"));
|
||||
goto cleanup;
|
||||
}
|
||||
fclose(cron_list);
|
||||
else
|
||||
fclose(cron_list);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
static const char platform_scheduler[] = "launchctl";
|
||||
#elif defined(GIT_WINDOWS_NATIVE)
|
||||
static const char platform_scheduler[] = "schtasks";
|
||||
#else
|
||||
static const char platform_scheduler[] = "crontab";
|
||||
#endif
|
||||
|
||||
static int update_background_schedule(int enable)
|
||||
{
|
||||
int result;
|
||||
const char *scheduler = platform_scheduler;
|
||||
const char *cmd = scheduler;
|
||||
char *testing;
|
||||
struct lock_file lk;
|
||||
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
|
||||
|
||||
testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
|
||||
if (testing) {
|
||||
char *sep = strchr(testing, ':');
|
||||
if (!sep)
|
||||
die("GIT_TEST_MAINT_SCHEDULER unparseable: %s", testing);
|
||||
*sep = '\0';
|
||||
scheduler = testing;
|
||||
cmd = sep + 1;
|
||||
}
|
||||
|
||||
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
|
||||
return error(_("another process is scheduling background maintenance"));
|
||||
|
||||
if (!strcmp(scheduler, "launchctl"))
|
||||
result = launchctl_update_schedule(enable, lk.tempfile->fd, cmd);
|
||||
else if (!strcmp(scheduler, "schtasks"))
|
||||
result = schtasks_update_schedule(enable, lk.tempfile->fd, cmd);
|
||||
else if (!strcmp(scheduler, "crontab"))
|
||||
result = crontab_update_schedule(enable, lk.tempfile->fd, cmd);
|
||||
else
|
||||
die("unknown background scheduler: %s", scheduler);
|
||||
|
||||
cleanup:
|
||||
rollback_lock_file(&lk);
|
||||
free(testing);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
1
cache.h
1
cache.h
@@ -1240,6 +1240,7 @@ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
|
||||
int normalize_path_copy(char *dst, const char *src);
|
||||
int longest_ancestor_length(const char *path, struct string_list *prefixes);
|
||||
char *strip_path_suffix(const char *path, const char *suffix);
|
||||
int is_mount_point_via_stat(struct strbuf *path);
|
||||
int daemon_avoid_alias(const char *path);
|
||||
|
||||
/*
|
||||
|
||||
365
compat/mingw.c
365
compat/mingw.c
@@ -1007,11 +1007,19 @@ unsigned int sleep (unsigned int seconds)
|
||||
char *mingw_mktemp(char *template)
|
||||
{
|
||||
wchar_t wtemplate[MAX_PATH];
|
||||
int offset = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1074,33 +1082,97 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
|
||||
{
|
||||
wchar_t wpath[MAX_PATH];
|
||||
HANDLE h;
|
||||
DWORD ret;
|
||||
int len;
|
||||
const char *last_component = NULL;
|
||||
|
||||
if (xutftowcs_path(wpath, path) < 0)
|
||||
return NULL;
|
||||
|
||||
h = CreateFileW(wpath, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
/*
|
||||
* strbuf_realpath() allows the last path component to not exist. If
|
||||
* that is the case, now it's time to try without last component.
|
||||
*/
|
||||
if (h == INVALID_HANDLE_VALUE &&
|
||||
GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
/* cut last component off of `wpath` */
|
||||
wchar_t *p = wpath + wcslen(wpath);
|
||||
|
||||
while (p != wpath)
|
||||
if (*(--p) == L'/' || *p == L'\\')
|
||||
break; /* found start of last component */
|
||||
|
||||
if (p != wpath && (last_component = find_last_dir_sep(path))) {
|
||||
last_component++; /* skip directory separator */
|
||||
*p = L'\0';
|
||||
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
|
||||
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
|
||||
CloseHandle(h);
|
||||
if (!ret || ret >= ARRAY_SIZE(wpath))
|
||||
return NULL;
|
||||
|
||||
len = wcslen(wpath) * 3;
|
||||
strbuf_grow(resolved, len);
|
||||
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
|
||||
if (len < 0)
|
||||
return NULL;
|
||||
resolved->len = len;
|
||||
|
||||
if (last_component) {
|
||||
/* Use forward-slash, like `normalize_ntpath()` */
|
||||
strbuf_addch(resolved, '/');
|
||||
strbuf_addstr(resolved, last_component);
|
||||
}
|
||||
|
||||
return resolved->buf;
|
||||
|
||||
}
|
||||
|
||||
char *mingw_getcwd(char *pointer, int len)
|
||||
{
|
||||
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
|
||||
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
|
||||
HANDLE hnd;
|
||||
|
||||
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) {
|
||||
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;
|
||||
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) {
|
||||
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
|
||||
CloseHandle(hnd);
|
||||
if (!ret || ret >= ARRAY_SIZE(wpointer))
|
||||
return NULL;
|
||||
if (!ret || ret >= ARRAY_SIZE(wpointer)) {
|
||||
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
|
||||
if (!ret || ret >= ARRAY_SIZE(wpointer)) {
|
||||
errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
|
||||
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)
|
||||
if (xwcstoutf(pointer, cwd, len) < 0)
|
||||
return NULL;
|
||||
convert_slashes(pointer);
|
||||
return pointer;
|
||||
@@ -1973,18 +2045,150 @@ static void ensure_socket_initialization(void)
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
static int winsock_error_to_errno(DWORD err)
|
||||
{
|
||||
switch (err) {
|
||||
case WSAEINTR: return EINTR;
|
||||
case WSAEBADF: return EBADF;
|
||||
case WSAEACCES: return EACCES;
|
||||
case WSAEFAULT: return EFAULT;
|
||||
case WSAEINVAL: return EINVAL;
|
||||
case WSAEMFILE: return EMFILE;
|
||||
case WSAEWOULDBLOCK: return EWOULDBLOCK;
|
||||
case WSAEINPROGRESS: return EINPROGRESS;
|
||||
case WSAEALREADY: return EALREADY;
|
||||
case WSAENOTSOCK: return ENOTSOCK;
|
||||
case WSAEDESTADDRREQ: return EDESTADDRREQ;
|
||||
case WSAEMSGSIZE: return EMSGSIZE;
|
||||
case WSAEPROTOTYPE: return EPROTOTYPE;
|
||||
case WSAENOPROTOOPT: return ENOPROTOOPT;
|
||||
case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
|
||||
case WSAEOPNOTSUPP: return EOPNOTSUPP;
|
||||
case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
|
||||
case WSAEADDRINUSE: return EADDRINUSE;
|
||||
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
|
||||
case WSAENETDOWN: return ENETDOWN;
|
||||
case WSAENETUNREACH: return ENETUNREACH;
|
||||
case WSAENETRESET: return ENETRESET;
|
||||
case WSAECONNABORTED: return ECONNABORTED;
|
||||
case WSAECONNRESET: return ECONNRESET;
|
||||
case WSAENOBUFS: return ENOBUFS;
|
||||
case WSAEISCONN: return EISCONN;
|
||||
case WSAENOTCONN: return ENOTCONN;
|
||||
case WSAETIMEDOUT: return ETIMEDOUT;
|
||||
case WSAECONNREFUSED: return ECONNREFUSED;
|
||||
case WSAELOOP: return ELOOP;
|
||||
case WSAENAMETOOLONG: return ENAMETOOLONG;
|
||||
case WSAEHOSTUNREACH: return EHOSTUNREACH;
|
||||
case WSAENOTEMPTY: return ENOTEMPTY;
|
||||
/* No errno equivalent; default to EIO */
|
||||
case WSAESOCKTNOSUPPORT:
|
||||
case WSAEPFNOSUPPORT:
|
||||
case WSAESHUTDOWN:
|
||||
case WSAETOOMANYREFS:
|
||||
case WSAEHOSTDOWN:
|
||||
case WSAEPROCLIM:
|
||||
case WSAEUSERS:
|
||||
case WSAEDQUOT:
|
||||
case WSAESTALE:
|
||||
case WSAEREMOTE:
|
||||
case WSASYSNOTREADY:
|
||||
case WSAVERNOTSUPPORTED:
|
||||
case WSANOTINITIALISED:
|
||||
case WSAEDISCON:
|
||||
case WSAENOMORE:
|
||||
case WSAECANCELLED:
|
||||
case WSAEINVALIDPROCTABLE:
|
||||
case WSAEINVALIDPROVIDER:
|
||||
case WSAEPROVIDERFAILEDINIT:
|
||||
case WSASYSCALLFAILURE:
|
||||
case WSASERVICE_NOT_FOUND:
|
||||
case WSATYPE_NOT_FOUND:
|
||||
case WSA_E_NO_MORE:
|
||||
case WSA_E_CANCELLED:
|
||||
case WSAEREFUSED:
|
||||
case WSAHOST_NOT_FOUND:
|
||||
case WSATRY_AGAIN:
|
||||
case WSANO_RECOVERY:
|
||||
case WSANO_DATA:
|
||||
case WSA_QOS_RECEIVERS:
|
||||
case WSA_QOS_SENDERS:
|
||||
case WSA_QOS_NO_SENDERS:
|
||||
case WSA_QOS_NO_RECEIVERS:
|
||||
case WSA_QOS_REQUEST_CONFIRMED:
|
||||
case WSA_QOS_ADMISSION_FAILURE:
|
||||
case WSA_QOS_POLICY_FAILURE:
|
||||
case WSA_QOS_BAD_STYLE:
|
||||
case WSA_QOS_BAD_OBJECT:
|
||||
case WSA_QOS_TRAFFIC_CTRL_ERROR:
|
||||
case WSA_QOS_GENERIC_ERROR:
|
||||
case WSA_QOS_ESERVICETYPE:
|
||||
case WSA_QOS_EFLOWSPEC:
|
||||
case WSA_QOS_EPROVSPECBUF:
|
||||
case WSA_QOS_EFILTERSTYLE:
|
||||
case WSA_QOS_EFILTERTYPE:
|
||||
case WSA_QOS_EFILTERCOUNT:
|
||||
case WSA_QOS_EOBJLENGTH:
|
||||
case WSA_QOS_EFLOWCOUNT:
|
||||
#ifndef _MSC_VER
|
||||
case WSA_QOS_EUNKNOWNPSOBJ:
|
||||
#endif
|
||||
case WSA_QOS_EPOLICYOBJ:
|
||||
case WSA_QOS_EFLOWDESC:
|
||||
case WSA_QOS_EPSFLOWSPEC:
|
||||
case WSA_QOS_EPSFILTERSPEC:
|
||||
case WSA_QOS_ESDMODEOBJ:
|
||||
case WSA_QOS_ESHAPERATEOBJ:
|
||||
case WSA_QOS_RESERVED_PETYPE:
|
||||
default: return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On Windows, `errno` is a global macro to a function call.
|
||||
* This makes it difficult to debug and single-step our mappings.
|
||||
*/
|
||||
static inline void set_wsa_errno(void)
|
||||
{
|
||||
DWORD wsa = WSAGetLastError();
|
||||
int e = winsock_error_to_errno(wsa);
|
||||
errno = e;
|
||||
|
||||
#ifdef DEBUG_WSA_ERRNO
|
||||
fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int winsock_return(int ret)
|
||||
{
|
||||
if (ret < 0)
|
||||
set_wsa_errno();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WINSOCK_RETURN(x) do { return winsock_return(x); } while (0)
|
||||
|
||||
#undef gethostname
|
||||
int mingw_gethostname(char *name, int namelen)
|
||||
{
|
||||
ensure_socket_initialization();
|
||||
return gethostname(name, namelen);
|
||||
ensure_socket_initialization();
|
||||
WINSOCK_RETURN(gethostname(name, namelen));
|
||||
}
|
||||
|
||||
#undef gethostbyname
|
||||
struct hostent *mingw_gethostbyname(const char *host)
|
||||
{
|
||||
struct hostent *ret;
|
||||
|
||||
ensure_socket_initialization();
|
||||
return gethostbyname(host);
|
||||
|
||||
ret = gethostbyname(host);
|
||||
if (!ret)
|
||||
set_wsa_errno();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef getaddrinfo
|
||||
@@ -1992,7 +2196,7 @@ int mingw_getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo *hints, struct addrinfo **res)
|
||||
{
|
||||
ensure_socket_initialization();
|
||||
return getaddrinfo(node, service, hints, res);
|
||||
WINSOCK_RETURN(getaddrinfo(node, service, hints, res));
|
||||
}
|
||||
|
||||
int mingw_socket(int domain, int type, int protocol)
|
||||
@@ -2012,7 +2216,7 @@ int mingw_socket(int domain, int type, int protocol)
|
||||
* in errno so that _if_ someone looks up the code somewhere,
|
||||
* then it is at least the number that are usually listed.
|
||||
*/
|
||||
errno = WSAGetLastError();
|
||||
set_wsa_errno();
|
||||
return -1;
|
||||
}
|
||||
/* convert into a file descriptor */
|
||||
@@ -2028,35 +2232,35 @@ int mingw_socket(int domain, int type, int protocol)
|
||||
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
|
||||
{
|
||||
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
||||
return connect(s, sa, sz);
|
||||
WINSOCK_RETURN(connect(s, sa, sz));
|
||||
}
|
||||
|
||||
#undef bind
|
||||
int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
|
||||
{
|
||||
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
||||
return bind(s, sa, sz);
|
||||
WINSOCK_RETURN(bind(s, sa, sz));
|
||||
}
|
||||
|
||||
#undef setsockopt
|
||||
int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
|
||||
{
|
||||
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
||||
return setsockopt(s, lvl, optname, (const char*)optval, optlen);
|
||||
WINSOCK_RETURN(setsockopt(s, lvl, optname, (const char*)optval, optlen));
|
||||
}
|
||||
|
||||
#undef shutdown
|
||||
int mingw_shutdown(int sockfd, int how)
|
||||
{
|
||||
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
||||
return shutdown(s, how);
|
||||
WINSOCK_RETURN(shutdown(s, how));
|
||||
}
|
||||
|
||||
#undef listen
|
||||
int mingw_listen(int sockfd, int backlog)
|
||||
{
|
||||
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
||||
return listen(s, backlog);
|
||||
WINSOCK_RETURN(listen(s, backlog));
|
||||
}
|
||||
|
||||
#undef accept
|
||||
@@ -2067,6 +2271,11 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
|
||||
SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
|
||||
SOCKET s2 = accept(s1, sa, sz);
|
||||
|
||||
if (s2 == INVALID_SOCKET) {
|
||||
set_wsa_errno();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* convert into a file descriptor */
|
||||
if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
|
||||
int err = errno;
|
||||
@@ -2461,6 +2670,28 @@ pid_t waitpid(pid_t pid, int *status, int options)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mingw_is_mount_point(struct strbuf *path)
|
||||
{
|
||||
WIN32_FIND_DATAW findbuf = { 0 };
|
||||
HANDLE handle;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
int wlen = xutftowcs_path(wfilename, path->buf);
|
||||
if (wlen < 0)
|
||||
die(_("could not get long path for '%s'"), path->buf);
|
||||
|
||||
/* remove trailing slash, if any */
|
||||
if (wlen > 0 && wfilename[wlen - 1] == L'/')
|
||||
wfilename[--wlen] = L'\0';
|
||||
|
||||
handle = FindFirstFileW(wfilename, &findbuf);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
FindClose(handle);
|
||||
|
||||
return (findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
||||
(findbuf.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT);
|
||||
}
|
||||
|
||||
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
|
||||
{
|
||||
int upos = 0, wpos = 0;
|
||||
@@ -2546,6 +2777,59 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef ENSURE_MSYSTEM_IS_SET
|
||||
static size_t append_system_bin_dirs(char *path, size_t size)
|
||||
{
|
||||
#if !defined(RUNTIME_PREFIX) || !defined(HAVE_WPGMPTR)
|
||||
return 0;
|
||||
#else
|
||||
char prefix[32768];
|
||||
const char *slash;
|
||||
size_t len = xwcstoutf(prefix, _wpgmptr, sizeof(prefix)), off = 0;
|
||||
|
||||
if (len == 0 || len >= sizeof(prefix) ||
|
||||
!(slash = find_last_dir_sep(prefix)))
|
||||
return 0;
|
||||
/* strip trailing `git.exe` */
|
||||
len = slash - prefix;
|
||||
|
||||
/* strip trailing `cmd` or `mingw64\bin` or `mingw32\bin` or `bin` or `libexec\git-core` */
|
||||
if (strip_suffix_mem(prefix, &len, "\\mingw64\\libexec\\git-core") ||
|
||||
strip_suffix_mem(prefix, &len, "\\mingw64\\bin"))
|
||||
off += xsnprintf(path + off, size - off,
|
||||
"%.*s\\mingw64\\bin;", (int)len, prefix);
|
||||
else if (strip_suffix_mem(prefix, &len, "\\mingw32\\libexec\\git-core") ||
|
||||
strip_suffix_mem(prefix, &len, "\\mingw32\\bin"))
|
||||
off += xsnprintf(path + off, size - off,
|
||||
"%.*s\\mingw32\\bin;", (int)len, prefix);
|
||||
else if (strip_suffix_mem(prefix, &len, "\\cmd") ||
|
||||
strip_suffix_mem(prefix, &len, "\\bin") ||
|
||||
strip_suffix_mem(prefix, &len, "\\libexec\\git-core"))
|
||||
off += xsnprintf(path + off, size - off,
|
||||
"%.*s\\mingw%d\\bin;", (int)len, prefix,
|
||||
(int)(sizeof(void *) * 8));
|
||||
else
|
||||
return 0;
|
||||
|
||||
off += xsnprintf(path + off, size - off,
|
||||
"%.*s\\usr\\bin;", (int)len, prefix);
|
||||
return off;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static int is_system32_path(const char *path)
|
||||
{
|
||||
WCHAR system32[MAX_LONG_PATH], wpath[MAX_LONG_PATH];
|
||||
|
||||
if (xutftowcs_long_path(wpath, path) < 0 ||
|
||||
!GetSystemDirectoryW(system32, ARRAY_SIZE(system32)) ||
|
||||
_wcsicmp(system32, wpath))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void setup_windows_environment(void)
|
||||
{
|
||||
char *tmp = getenv("TMPDIR");
|
||||
@@ -2586,7 +2870,8 @@ static void setup_windows_environment(void)
|
||||
strbuf_addstr(&buf, tmp);
|
||||
if ((tmp = getenv("HOMEPATH"))) {
|
||||
strbuf_addstr(&buf, tmp);
|
||||
if (is_directory(buf.buf))
|
||||
if (!is_system32_path(buf.buf) &&
|
||||
is_directory(buf.buf))
|
||||
setenv("HOME", buf.buf, 1);
|
||||
else
|
||||
tmp = NULL; /* use $USERPROFILE */
|
||||
@@ -2597,6 +2882,37 @@ static void setup_windows_environment(void)
|
||||
if (!tmp && (tmp = getenv("USERPROFILE")))
|
||||
setenv("HOME", tmp, 1);
|
||||
}
|
||||
|
||||
if (!getenv("PLINK_PROTOCOL"))
|
||||
setenv("PLINK_PROTOCOL", "ssh", 0);
|
||||
|
||||
#ifdef ENSURE_MSYSTEM_IS_SET
|
||||
if (!(tmp = getenv("MSYSTEM")) || !tmp[0]) {
|
||||
const char *home = getenv("HOME"), *path = getenv("PATH");
|
||||
char buf[32768];
|
||||
size_t off = 0;
|
||||
|
||||
xsnprintf(buf, sizeof(buf),
|
||||
"MINGW%d", (int)(sizeof(void *) * 8));
|
||||
setenv("MSYSTEM", buf, 1);
|
||||
|
||||
if (home)
|
||||
off += xsnprintf(buf + off, sizeof(buf) - off,
|
||||
"%s\\bin;", home);
|
||||
off += append_system_bin_dirs(buf + off, sizeof(buf) - off);
|
||||
if (path)
|
||||
off += xsnprintf(buf + off, sizeof(buf) - off,
|
||||
"%s", path);
|
||||
else if (off > 0)
|
||||
buf[off - 1] = '\0';
|
||||
else
|
||||
buf[0] = '\0';
|
||||
setenv("PATH", buf, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
|
||||
setenv("LC_CTYPE", "C.UTF-8", 1);
|
||||
}
|
||||
|
||||
int is_valid_win32_path(const char *path, int allow_literal_nul)
|
||||
@@ -2841,6 +3157,7 @@ int wmain(int argc, const wchar_t **wargv)
|
||||
#endif
|
||||
|
||||
maybe_redirect_std_handles();
|
||||
fsync_object_files = 1;
|
||||
|
||||
/* determine size of argv and environ conversion buffer */
|
||||
maxlen = wcslen(wargv[0]);
|
||||
|
||||
@@ -442,9 +442,15 @@ static inline void convert_slashes(char *path)
|
||||
if (*path == '\\')
|
||||
*path = '/';
|
||||
}
|
||||
struct strbuf;
|
||||
int mingw_is_mount_point(struct strbuf *path);
|
||||
#define is_mount_point mingw_is_mount_point
|
||||
#define CAN_UNLINK_MOUNT_POINTS 1
|
||||
#define PATH_SEP ';'
|
||||
char *mingw_query_user_email(void);
|
||||
#define query_user_email mingw_query_user_email
|
||||
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
|
||||
#define platform_strbuf_realpath mingw_strbuf_realpath
|
||||
#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRId64 "I64d"
|
||||
|
||||
@@ -6,7 +6,11 @@ The Steps to Build Git with VS2015 or VS2017 from the command line.
|
||||
Prompt or from an SDK bash window:
|
||||
|
||||
$ cd <repo_root>
|
||||
$ ./compat/vcbuild/vcpkg_install.bat
|
||||
$ ./compat/vcbuild/vcpkg_install.bat x64-windows
|
||||
|
||||
or
|
||||
|
||||
$ ./compat/vcbuild/vcpkg_install.bat arm64-windows
|
||||
|
||||
The vcpkg tools and all of the third-party sources will be installed
|
||||
in this folder:
|
||||
|
||||
@@ -56,7 +56,8 @@ while (@ARGV) {
|
||||
# need to use that instead?
|
||||
foreach my $flag (@lflags) {
|
||||
if ($flag =~ /^-LIBPATH:(.*)/) {
|
||||
foreach my $l ("libcurl_imp.lib", "libcurl.lib") {
|
||||
my $libcurl = $is_debug ? "libcurl-d.lib" : "libcurl.lib";
|
||||
foreach my $l ("libcurl_imp.lib", $libcurl) {
|
||||
if (-f "$1/$l") {
|
||||
$lib = $l;
|
||||
last;
|
||||
|
||||
@@ -15,7 +15,12 @@ REM ================================================================
|
||||
@FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
|
||||
cd %cwd%
|
||||
|
||||
SET arch=x64-windows
|
||||
SET arch=%2
|
||||
IF NOT DEFINED arch (
|
||||
echo defaulting to 'x64-windows`. Invoke %0 with 'x86-windows', 'x64-windows', or 'arm64-windows'
|
||||
set arch=x64-windows
|
||||
)
|
||||
|
||||
SET inst=%cwd%vcpkg\installed\%arch%
|
||||
|
||||
IF [%1]==[release] (
|
||||
|
||||
@@ -31,11 +31,24 @@ REM ================================================================
|
||||
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
SET arch=%1
|
||||
IF NOT DEFINED arch (
|
||||
echo defaulting to 'x64-windows`. Invoke %0 with 'x86-windows', 'x64-windows', or 'arm64-windows'
|
||||
set arch=x64-windows
|
||||
)
|
||||
|
||||
@FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
|
||||
cd %cwd%
|
||||
|
||||
dir vcpkg\vcpkg.exe >nul 2>nul && GOTO :install_libraries
|
||||
|
||||
git.exe version 2>nul
|
||||
IF ERRORLEVEL 1 (
|
||||
echo "***"
|
||||
echo "Git not found. Please adjust your CMD path or Git install option."
|
||||
echo "***"
|
||||
EXIT /B 1 )
|
||||
|
||||
echo Fetching vcpkg in %cwd%vcpkg
|
||||
git.exe clone https://github.com/Microsoft/vcpkg vcpkg
|
||||
IF ERRORLEVEL 1 ( EXIT /B 1 )
|
||||
@@ -48,9 +61,8 @@ REM ================================================================
|
||||
echo Successfully installed %cwd%vcpkg\vcpkg.exe
|
||||
|
||||
:install_libraries
|
||||
SET arch=x64-windows
|
||||
|
||||
echo Installing third-party libraries...
|
||||
echo Installing third-party libraries(%arch%)...
|
||||
FOR %%i IN (zlib expat libiconv openssl libssh2 curl) DO (
|
||||
cd %cwd%vcpkg
|
||||
IF NOT EXIST "packages\%%i_%arch%" CALL :sub__install_one %%i
|
||||
@@ -73,8 +85,47 @@ REM ================================================================
|
||||
:sub__install_one
|
||||
echo Installing package %1...
|
||||
|
||||
.\vcpkg.exe install %1:%arch%
|
||||
call :%1_features
|
||||
|
||||
REM vcpkg may not be reliable on slow, intermittent or proxy
|
||||
REM connections, see e.g.
|
||||
REM https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/4a8f7be5-5e15-4213-a7bb-ddf424a954e6/winhttpsendrequest-ends-with-12002-errorhttptimeout-after-21-seconds-no-matter-what-timeout?forum=windowssdk
|
||||
REM which explains the hidden 21 second timeout
|
||||
REM (last post by Dave : Microsoft - Windows Networking team)
|
||||
|
||||
.\vcpkg.exe install %1%features%:%arch%
|
||||
IF ERRORLEVEL 1 ( EXIT /B 1 )
|
||||
|
||||
echo Finished %1
|
||||
goto :EOF
|
||||
|
||||
::
|
||||
:: features for each vcpkg to install
|
||||
:: there should be an entry here for each package to install
|
||||
:: 'set features=' means use the default otherwise
|
||||
:: 'set features=[comma-delimited-feature-set]' is the syntax
|
||||
::
|
||||
|
||||
:zlib_features
|
||||
set features=
|
||||
goto :EOF
|
||||
|
||||
:expat_features
|
||||
set features=
|
||||
goto :EOF
|
||||
|
||||
:libiconv_features
|
||||
set features=
|
||||
goto :EOF
|
||||
|
||||
:openssl_features
|
||||
set features=
|
||||
goto :EOF
|
||||
|
||||
:libssh2_features
|
||||
set features=
|
||||
goto :EOF
|
||||
|
||||
:curl_features
|
||||
set features=[core,openssl,schannel]
|
||||
goto :EOF
|
||||
|
||||
114
compat/win32/headless.c
Normal file
114
compat/win32/headless.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* headless Git - run Git without opening a console window on Windows
|
||||
*/
|
||||
|
||||
#define STRICT
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
/*
|
||||
* If `dir` contains the path to a Git exec directory, extend `PATH` to
|
||||
* include the corresponding `bin/` directory (which is where all those
|
||||
* `.dll` files needed by `git.exe` are, on Windows).
|
||||
*/
|
||||
static int extend_path(wchar_t *dir, size_t dir_len)
|
||||
{
|
||||
const wchar_t *suffix = L"\\libexec\\git-core";
|
||||
size_t suffix_len = wcslen(suffix);
|
||||
wchar_t *env;
|
||||
DWORD len;
|
||||
|
||||
if (dir_len < suffix_len)
|
||||
return 0;
|
||||
|
||||
dir_len -= suffix_len;
|
||||
if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t)))
|
||||
return 0;
|
||||
|
||||
len = GetEnvironmentVariableW(L"PATH", NULL, 0);
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
env = _alloca((dir_len + 5 + len) * sizeof(wchar_t));
|
||||
wcsncpy(env, dir, dir_len);
|
||||
wcscpy(env + dir_len, L"\\bin;");
|
||||
if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len))
|
||||
return 0;
|
||||
|
||||
SetEnvironmentVariableW(L"PATH", env);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous_instance,
|
||||
wchar_t *command_line, int show)
|
||||
{
|
||||
wchar_t git_command_line[32768];
|
||||
size_t size = sizeof(git_command_line) / sizeof(wchar_t);
|
||||
const wchar_t *needs_quotes = L"";
|
||||
int slash = 0, i;
|
||||
|
||||
STARTUPINFO startup_info = {
|
||||
.dwFlags = STARTF_USESHOWWINDOW,
|
||||
.wShowWindow = SW_HIDE,
|
||||
};
|
||||
PROCESS_INFORMATION process_info = { 0 };
|
||||
DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT |
|
||||
CREATE_NEW_CONSOLE | CREATE_NO_WINDOW;
|
||||
DWORD exit_code;
|
||||
|
||||
/* First, determine the full path of argv[0] */
|
||||
for (i = 0; _wpgmptr[i]; i++)
|
||||
if (_wpgmptr[i] == L' ')
|
||||
needs_quotes = L"\"";
|
||||
else if (_wpgmptr[i] == L'\\')
|
||||
slash = i;
|
||||
|
||||
if (slash + 11 >= sizeof(git_command_line) / sizeof(wchar_t))
|
||||
return 127; /* Too long path */
|
||||
|
||||
/* If it is in Git's exec path, add the bin/ directory to the PATH */
|
||||
extend_path(_wpgmptr, slash);
|
||||
|
||||
/* Then, add the full path of `git.exe` as argv[0] */
|
||||
i = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls",
|
||||
needs_quotes, slash, _wpgmptr, needs_quotes);
|
||||
if (i < 0)
|
||||
return 127; /* Too long path */
|
||||
|
||||
if (*command_line) {
|
||||
/* Now, append the command-line arguments */
|
||||
i = swprintf_s(git_command_line + i, size - i,
|
||||
L" %ls", command_line);
|
||||
if (i < 0)
|
||||
return 127;
|
||||
}
|
||||
|
||||
startup_info.cb = sizeof(STARTUPINFO);
|
||||
|
||||
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (!CreateProcess(NULL, /* infer argv[0] from the command line */
|
||||
git_command_line, /* modified command line */
|
||||
NULL, /* inherit process handles? */
|
||||
NULL, /* inherit thread handles? */
|
||||
FALSE, /* handles inheritable? */
|
||||
creation_flags,
|
||||
NULL, /* use this process' environment */
|
||||
NULL, /* use this process' working directory */
|
||||
&startup_info, &process_info))
|
||||
return 129; /* could not start */
|
||||
WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||
if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) {
|
||||
CloseHandle(process_info.hProcess);
|
||||
return 130; /* Could not determine exit code? */
|
||||
}
|
||||
|
||||
return (int)exit_code;
|
||||
}
|
||||
6
config.c
6
config.c
@@ -1678,9 +1678,11 @@ static int git_config_from_blob_ref(config_fn_t fn,
|
||||
|
||||
const char *git_etc_gitconfig(void)
|
||||
{
|
||||
static const char *system_wide;
|
||||
if (!system_wide)
|
||||
static char *system_wide;
|
||||
if (!system_wide) {
|
||||
system_wide = system_path(ETC_GITCONFIG);
|
||||
normalize_path_copy(system_wide, system_wide);
|
||||
}
|
||||
return system_wide;
|
||||
}
|
||||
|
||||
|
||||
@@ -433,6 +433,11 @@ ifeq ($(uname_S),Windows)
|
||||
NO_POSIX_GOODIES = UnfortunatelyYes
|
||||
NATIVE_CRLF = YesPlease
|
||||
DEFAULT_HELP_FORMAT = html
|
||||
ifeq (/mingw64,$(subst 32,64,$(prefix)))
|
||||
# Move system config into top-level /etc/
|
||||
ETC_GITCONFIG = ../etc/gitconfig
|
||||
ETC_GITATTRIBUTES = ../etc/gitattributes
|
||||
endif
|
||||
|
||||
CC = compat/vcbuild/scripts/clink.pl
|
||||
AR = compat/vcbuild/scripts/lib.pl
|
||||
@@ -443,7 +448,7 @@ ifeq ($(uname_S),Windows)
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/trace2_win32_process_info.o \
|
||||
compat/win32/dirent.o
|
||||
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
|
||||
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DENSURE_MSYSTEM_IS_SET -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
|
||||
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE
|
||||
# invalidcontinue.obj allows Git's source code to close the same file
|
||||
# handle twice, or to access the osfhandle of an already-closed stdout
|
||||
@@ -481,6 +486,8 @@ else
|
||||
endif
|
||||
X = .exe
|
||||
|
||||
EXTRA_PROGRAMS += headless-git$X
|
||||
|
||||
compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS
|
||||
endif
|
||||
ifeq ($(uname_S),Interix)
|
||||
@@ -622,6 +629,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
RC = windres -O coff
|
||||
NATIVE_CRLF = YesPlease
|
||||
X = .exe
|
||||
EXTRA_PROGRAMS += headless-git$X
|
||||
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
|
||||
htmldir = doc/git/html/
|
||||
prefix =
|
||||
@@ -656,17 +664,22 @@ else
|
||||
endif
|
||||
CC = gcc
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
|
||||
-fstack-protector-strong
|
||||
-DENSURE_MSYSTEM_IS_SET -fstack-protector-strong
|
||||
EXTLIBS += -lntdll
|
||||
INSTALL = /bin/install
|
||||
INTERNAL_QSORT = YesPlease
|
||||
HAVE_LIBCHARSET_H = YesPlease
|
||||
NO_GETTEXT =
|
||||
USE_GETTEXT_SCHEME = fallthrough
|
||||
USE_LIBPCRE= YesPlease
|
||||
NO_LIBPCRE1_JIT = UnfortunatelyYes
|
||||
USE_LIBPCRE = YesPlease
|
||||
NO_CURL =
|
||||
USE_NED_ALLOCATOR = YesPlease
|
||||
NO_PYTHON =
|
||||
ifeq (/mingw64,$(subst 32,64,$(prefix)))
|
||||
# Move system config into top-level /etc/
|
||||
ETC_GITCONFIG = ../etc/gitconfig
|
||||
ETC_GITATTRIBUTES = ../etc/gitattributes
|
||||
endif
|
||||
else
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
|
||||
NO_CURL = YesPlease
|
||||
@@ -697,7 +710,7 @@ vcxproj:
|
||||
# Make .vcxproj files and add them
|
||||
unset QUIET_GEN QUIET_BUILT_IN; \
|
||||
perl contrib/buildsystems/generate -g Vcxproj
|
||||
git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
|
||||
git add -f git.sln {*,*/lib.proj,t/helper/*}/*.vcxproj
|
||||
|
||||
# Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file
|
||||
(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
|
||||
@@ -707,7 +720,7 @@ vcxproj:
|
||||
echo ' <Copy SourceFiles="$$(OutDir)\git.exe" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
|
||||
done && \
|
||||
echo ' </Target>' && \
|
||||
echo '</Project>') >git/LinkOrCopyBuiltins.targets
|
||||
echo '</Project>') >git.proj/LinkOrCopyBuiltins.targets
|
||||
(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
|
||||
echo ' <Target Name="CopyBuiltins_AfterBuild" AfterTargets="AfterBuild">' && \
|
||||
for name in $(REMOTE_CURL_ALIASES); \
|
||||
@@ -715,8 +728,8 @@ vcxproj:
|
||||
echo ' <Copy SourceFiles="$$(OutDir)\'"$(REMOTE_CURL_PRIMARY)"'" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
|
||||
done && \
|
||||
echo ' </Target>' && \
|
||||
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
|
||||
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
|
||||
echo '</Project>') >git-remote-http.proj/LinkOrCopyRemoteHttp.targets
|
||||
git add -f git.proj/LinkOrCopyBuiltins.targets git-remote-http.proj/LinkOrCopyRemoteHttp.targets
|
||||
|
||||
# Add command-list.h and config-list.h
|
||||
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 config-list.h command-list.h
|
||||
|
||||
@@ -52,12 +52,16 @@ if(WIN32)
|
||||
set(VCPKG_DIR "${CMAKE_SOURCE_DIR}/compat/vcbuild/vcpkg")
|
||||
if(MSVC AND NOT EXISTS ${VCPKG_DIR})
|
||||
message("Initializing vcpkg and building the Git's dependencies (this will take a while...)")
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/compat/vcbuild/vcpkg_install.bat)
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/compat/vcbuild/vcpkg_install.bat ${VCPKG_ARCH})
|
||||
endif()
|
||||
list(APPEND CMAKE_PREFIX_PATH "${VCPKG_DIR}/installed/x64-windows")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${VCPKG_DIR}/installed/${VCPKG_ARCH}")
|
||||
|
||||
# In the vcpkg edition, we need this to be able to link to libcurl
|
||||
set(CURL_NO_CURL_CMAKE ON)
|
||||
|
||||
# Copy the necessary vcpkg DLLs (like iconv) to the install dir
|
||||
set(X_VCPKG_APPLOCAL_DEPS_INSTALL ON)
|
||||
set(CMAKE_TOOLCHAIN_FILE ${VCPKG_DIR}/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file")
|
||||
endif()
|
||||
|
||||
find_program(SH_EXE sh PATHS "C:/Program Files/Git/bin")
|
||||
@@ -642,6 +646,9 @@ if(WIN32)
|
||||
else()
|
||||
message(FATAL_ERROR "Unhandled compiler: ${CMAKE_C_COMPILER_ID}")
|
||||
endif()
|
||||
|
||||
add_executable(headless-git ${CMAKE_SOURCE_DIR}/compat/win32/headless.c)
|
||||
target_link_options(headless-git PUBLIC /NOLOGO /ENTRY:wWinMainCRTStartup /SUBSYSTEM:WINDOWS)
|
||||
elseif(UNIX)
|
||||
target_link_libraries(common-main pthread rt)
|
||||
endif()
|
||||
@@ -807,15 +814,19 @@ list(TRANSFORM git_shell_scripts PREPEND "${CMAKE_BINARY_DIR}/")
|
||||
list(TRANSFORM git_perl_scripts PREPEND "${CMAKE_BINARY_DIR}/")
|
||||
|
||||
#install
|
||||
install(TARGETS git git-shell
|
||||
foreach(program ${PROGRAMS_BUILT})
|
||||
if(${program} STREQUAL git OR ${program} STREQUAL git-shell)
|
||||
install(TARGETS ${program}
|
||||
RUNTIME DESTINATION bin)
|
||||
else()
|
||||
install(TARGETS ${program}
|
||||
RUNTIME DESTINATION libexec/git-core)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/git-cvsserver
|
||||
DESTINATION bin)
|
||||
|
||||
list(REMOVE_ITEM PROGRAMS_BUILT git git-shell)
|
||||
install(TARGETS ${PROGRAMS_BUILT}
|
||||
RUNTIME DESTINATION libexec/git-core)
|
||||
|
||||
set(bin_links
|
||||
git-receive-pack git-upload-archive git-upload-pack)
|
||||
|
||||
@@ -828,12 +839,12 @@ install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git-shell${EXE_EXTENS
|
||||
|
||||
foreach(b ${git_links})
|
||||
string(REPLACE "${CMAKE_BINARY_DIR}" "" b ${b})
|
||||
install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b}${EXE_EXTENSION})")
|
||||
install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/git${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b})")
|
||||
endforeach()
|
||||
|
||||
foreach(b ${git_http_links})
|
||||
string(REPLACE "${CMAKE_BINARY_DIR}" "" b ${b})
|
||||
install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/libexec/git-core/git-remote-http${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b}${EXE_EXTENSION})")
|
||||
install(CODE "file(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/libexec/git-core/git-remote-http${EXE_EXTENSION} ${CMAKE_INSTALL_PREFIX}/libexec/git-core/${b})")
|
||||
endforeach()
|
||||
|
||||
install(PROGRAMS ${git_shell_scripts} ${git_perl_scripts} ${CMAKE_BINARY_DIR}/git-p4
|
||||
@@ -961,7 +972,7 @@ file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_GETTEXT='${NO_GETTEXT}'\n"
|
||||
file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "RUNTIME_PREFIX='${RUNTIME_PREFIX}'\n")
|
||||
file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PYTHON='${NO_PYTHON}'\n")
|
||||
if(WIN32)
|
||||
file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/x64-windows/bin\"\n")
|
||||
file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PATH=\"$PATH:$TEST_DIRECTORY/../compat/vcbuild/vcpkg/installed/${VCPKG_ARCH}/bin\"\n")
|
||||
endif()
|
||||
|
||||
#Make the tests work when building out of the source tree
|
||||
|
||||
@@ -58,8 +58,8 @@ sub createProject {
|
||||
my $uuid = generate_guid($name);
|
||||
$$build_structure{"$prefix${target}_GUID"} = $uuid;
|
||||
my $vcxproj = $target;
|
||||
$vcxproj =~ s/(.*\/)?(.*)/$&\/$2.vcxproj/;
|
||||
$vcxproj =~ s/([^\/]*)(\/lib)\/(lib.vcxproj)/$1$2\/$1_$3/;
|
||||
$vcxproj =~ s/(.*\/)?(.*)/$&.proj\/$2.vcxproj/;
|
||||
$vcxproj =~ s/([^\/]*)(\/lib\.proj)\/(lib.vcxproj)/$1$2\/$1_$3/;
|
||||
$$build_structure{"$prefix${target}_VCXPROJ"} = $vcxproj;
|
||||
|
||||
my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"$prefix${name}_SOURCES"}}));
|
||||
@@ -89,7 +89,9 @@ sub createProject {
|
||||
$defines =~ s/>/>/g;
|
||||
$defines =~ s/\'//g;
|
||||
|
||||
die "Could not create the directory $target for $label project!\n" unless (-d "$target" || mkdir "$target");
|
||||
my $dir = $vcxproj;
|
||||
$dir =~ s/\/[^\/]*$//;
|
||||
die "Could not create the directory $dir for $label project!\n" unless (-d "$dir" || mkdir "$dir");
|
||||
|
||||
open F, ">$vcxproj" or die "Could not open $vcxproj for writing!\n";
|
||||
binmode F, ":crlf :utf8";
|
||||
@@ -114,12 +116,21 @@ sub createProject {
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>$uuid</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<VCPKGArch Condition="'\$(Platform)'=='Win32'">x86-windows</VCPKGArch>
|
||||
<VCPKGArch Condition="'\$(Platform)'!='Win32'">x64-windows</VCPKGArch>
|
||||
<VCPKGArch Condition="'\$(Platform)'=='x64'">x64-windows</VCPKGArch>
|
||||
<VCPKGArch Condition="'\$(Platform)'=='ARM64'">arm64-windows</VCPKGArch>
|
||||
<VCPKGArchDirectory>$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)</VCPKGArchDirectory>
|
||||
<VCPKGBinDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\bin</VCPKGBinDirectory>
|
||||
<VCPKGLibDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\lib</VCPKGLibDirectory>
|
||||
@@ -140,7 +151,7 @@ sub createProject {
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>$config_type</ConfigurationType>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<!-- <CharacterSet>UTF-8</CharacterSet> -->
|
||||
<OutDir>..\\</OutDir>
|
||||
<!-- <IntDir>\$(ProjectDir)\$(Configuration)\\</IntDir> -->
|
||||
@@ -184,7 +195,7 @@ EOM
|
||||
<PreBuildEvent Condition="!Exists('$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)\\include\\openssl\\ssl.h')">
|
||||
<Message>Initialize VCPKG</Message>
|
||||
<Command>del "$cdup\\compat\\vcbuild\\vcpkg"</Command>
|
||||
<Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat"</Command>
|
||||
<Command>call "$cdup\\compat\\vcbuild\\vcpkg_install.bat" \$(VCPKGArch)</Command>
|
||||
</PreBuildEvent>
|
||||
EOM
|
||||
}
|
||||
@@ -236,14 +247,14 @@ EOM
|
||||
|
||||
print F << "EOM";
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$cdup\\libgit\\libgit.vcxproj">
|
||||
<ProjectReference Include="$cdup\\libgit.proj\\libgit.vcxproj">
|
||||
<Project>$uuid_libgit</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
EOM
|
||||
if (!($name =~ 'xdiff')) {
|
||||
print F << "EOM";
|
||||
<ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
|
||||
<ProjectReference Include="$cdup\\xdiff\\lib.proj\\xdiff_lib.vcxproj">
|
||||
<Project>$uuid_xdiff_lib</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
@@ -252,7 +263,7 @@ EOM
|
||||
if ($name =~ /(test-(line-buffer|svn-fe)|^git-remote-testsvn)\.exe$/) {
|
||||
my $uuid_vcs_svn_lib = $$build_structure{"LIBS_vcs-svn/lib_GUID"};
|
||||
print F << "EOM";
|
||||
<ProjectReference Include="$cdup\\vcs-svn\\lib\\vcs-svn_lib.vcxproj">
|
||||
<ProjectReference Include="$cdup\\vcs-svn\\lib.proj\\vcs-svn_lib.vcxproj">
|
||||
<Project>$uuid_vcs_svn_lib</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
@@ -329,7 +340,7 @@ sub createGlueProject {
|
||||
my $vcxproj = $build_structure{"APPS_${appname}_VCXPROJ"};
|
||||
$vcxproj =~ s/\//\\/g;
|
||||
$appname =~ s/.*\///;
|
||||
print F "\"${appname}\", \"${vcxproj}\", \"${uuid}\"";
|
||||
print F "\"${appname}.proj\", \"${vcxproj}\", \"${uuid}\"";
|
||||
print F "$SLN_POST";
|
||||
}
|
||||
foreach (@libs) {
|
||||
@@ -339,15 +350,17 @@ sub createGlueProject {
|
||||
my $vcxproj = $build_structure{"LIBS_${libname}_VCXPROJ"};
|
||||
$vcxproj =~ s/\//\\/g;
|
||||
$libname =~ s/\//_/g;
|
||||
print F "\"${libname}\", \"${vcxproj}\", \"${uuid}\"";
|
||||
print F "\"${libname}.proj\", \"${vcxproj}\", \"${uuid}\"";
|
||||
print F "$SLN_POST";
|
||||
}
|
||||
|
||||
print F << "EOM";
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
@@ -358,10 +371,14 @@ EOM
|
||||
foreach (@apps) {
|
||||
my $appname = $_;
|
||||
my $uuid = $build_structure{"APPS_${appname}_GUID"};
|
||||
print F "\t\t${uuid}.Debug|ARM64.ActiveCfg = Debug|ARM64\n";
|
||||
print F "\t\t${uuid}.Debug|ARM64.Build.0 = Debug|ARM64\n";
|
||||
print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
|
||||
print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
|
||||
print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
|
||||
print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
|
||||
print F "\t\t${uuid}.Release|ARM64.ActiveCfg = Release|ARM64\n";
|
||||
print F "\t\t${uuid}.Release|ARM64.Build.0 = Release|ARM64\n";
|
||||
print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
|
||||
print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
|
||||
print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
|
||||
@@ -370,10 +387,14 @@ EOM
|
||||
foreach (@libs) {
|
||||
my $libname = $_;
|
||||
my $uuid = $build_structure{"LIBS_${libname}_GUID"};
|
||||
print F "\t\t${uuid}.Debug|ARM64.ActiveCfg = Debug|ARM64\n";
|
||||
print F "\t\t${uuid}.Debug|ARM64.Build.0 = Debug|ARM64\n";
|
||||
print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n";
|
||||
print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n";
|
||||
print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n";
|
||||
print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n";
|
||||
print F "\t\t${uuid}.Release|ARM64.ActiveCfg = Release|ARM64\n";
|
||||
print F "\t\t${uuid}.Release|ARM64.Build.0 = Release|ARM64\n";
|
||||
print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n";
|
||||
print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n";
|
||||
print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n";
|
||||
|
||||
@@ -433,8 +433,8 @@ __git_ps1 ()
|
||||
fi
|
||||
|
||||
local sparse=""
|
||||
if [ -z "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
|
||||
[ -z "${GIT_PS1_OMITSPARSESTATE}" ] &&
|
||||
if [ -z "${GIT_PS1_COMPRESSSPARSESTATE-}" ] &&
|
||||
[ -z "${GIT_PS1_OMITSPARSESTATE-}" ] &&
|
||||
[ "$(git config --bool core.sparseCheckout)" = "true" ]; then
|
||||
sparse="|SPARSE"
|
||||
fi
|
||||
@@ -543,7 +543,7 @@ __git_ps1 ()
|
||||
u="%${ZSH_VERSION+%}"
|
||||
fi
|
||||
|
||||
if [ -n "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
|
||||
if [ -n "${GIT_PS1_COMPRESSSPARSESTATE-}" ] &&
|
||||
[ "$(git config --bool core.sparseCheckout)" = "true" ]; then
|
||||
h="?"
|
||||
fi
|
||||
|
||||
@@ -84,6 +84,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 = first + ((last - first) >> 1);
|
||||
struct diff_rename_src *src = &(rename_src[next]);
|
||||
@@ -97,6 +109,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++;
|
||||
|
||||
@@ -174,6 +174,24 @@ sub run_cmd_pipe {
|
||||
die "$^O does not support: @invalid\n" if @invalid;
|
||||
my @args = map { m/ /o ? "\"$_\"": $_ } @_;
|
||||
return qx{@args};
|
||||
} elsif (($^O eq 'MSWin32' || $^O eq 'msys') && (scalar @_ > 200) &&
|
||||
grep $_ eq '--', @_) {
|
||||
use File::Temp qw(tempfile);
|
||||
my ($fhargs, $filename) =
|
||||
tempfile('git-args-XXXXXX', UNLINK => 1);
|
||||
|
||||
my $cmd = 'cat '.$filename.' | xargs -0 -s 20000 ';
|
||||
while ($_[0] ne '--') {
|
||||
$cmd = $cmd . shift(@_) . ' ';
|
||||
}
|
||||
|
||||
shift(@_);
|
||||
print $fhargs join("\0", @_);
|
||||
close($fhargs);
|
||||
|
||||
my $fh = undef;
|
||||
open($fh, '-|', $cmd) or die;
|
||||
return <$fh>;
|
||||
} else {
|
||||
my $fh = undef;
|
||||
open($fh, '-|', @_) or die;
|
||||
|
||||
@@ -406,10 +406,18 @@ static inline int git_has_dir_sep(const char *path)
|
||||
#define has_dir_sep(path) git_has_dir_sep(path)
|
||||
#endif
|
||||
|
||||
#ifndef is_mount_point
|
||||
#define is_mount_point is_mount_point_via_stat
|
||||
#endif
|
||||
|
||||
#ifndef query_user_email
|
||||
#define query_user_email() NULL
|
||||
#endif
|
||||
|
||||
#ifndef platform_strbuf_realpath
|
||||
#define platform_strbuf_realpath(resolved, path) NULL
|
||||
#endif
|
||||
|
||||
#ifdef __TANDEM
|
||||
#include <floss.h(floss_execl,floss_execlp,floss_execv,floss_execvp)>
|
||||
#include <floss.h(floss_getpwuid)>
|
||||
|
||||
@@ -2087,6 +2087,7 @@ set all_icons(U$ui_index) file_merge
|
||||
set all_icons(T$ui_index) file_statechange
|
||||
|
||||
set all_icons(_$ui_workdir) file_plain
|
||||
set all_icons(A$ui_workdir) file_plain
|
||||
set all_icons(M$ui_workdir) file_mod
|
||||
set all_icons(D$ui_workdir) file_question
|
||||
set all_icons(U$ui_workdir) file_merge
|
||||
@@ -2113,6 +2114,7 @@ foreach i {
|
||||
{A_ {mc "Staged for commit"}}
|
||||
{AM {mc "Portions staged for commit"}}
|
||||
{AD {mc "Staged for commit, missing"}}
|
||||
{AA {mc "Intended to be added"}}
|
||||
|
||||
{_D {mc "Missing"}}
|
||||
{D_ {mc "Staged for removal"}}
|
||||
|
||||
@@ -582,7 +582,8 @@ proc apply_or_revert_hunk {x y revert} {
|
||||
if {$current_diff_side eq $ui_index} {
|
||||
set failed_msg [mc "Failed to unstage selected hunk."]
|
||||
lappend apply_cmd --reverse --cached
|
||||
if {[string index $mi 0] ne {M}} {
|
||||
set file_state [string index $mi 0]
|
||||
if {$file_state ne {M} && $file_state ne {A}} {
|
||||
unlock_index
|
||||
return
|
||||
}
|
||||
@@ -595,7 +596,8 @@ proc apply_or_revert_hunk {x y revert} {
|
||||
lappend apply_cmd --cached
|
||||
}
|
||||
|
||||
if {[string index $mi 1] ne {M}} {
|
||||
set file_state [string index $mi 1]
|
||||
if {$file_state ne {M} && $file_state ne {A}} {
|
||||
unlock_index
|
||||
return
|
||||
}
|
||||
@@ -687,7 +689,8 @@ proc apply_or_revert_range_or_line {x y revert} {
|
||||
set failed_msg [mc "Failed to unstage selected line."]
|
||||
set to_context {+}
|
||||
lappend apply_cmd --reverse --cached
|
||||
if {[string index $mi 0] ne {M}} {
|
||||
set file_state [string index $mi 0]
|
||||
if {$file_state ne {M} && $file_state ne {A}} {
|
||||
unlock_index
|
||||
return
|
||||
}
|
||||
@@ -702,7 +705,8 @@ proc apply_or_revert_range_or_line {x y revert} {
|
||||
lappend apply_cmd --cached
|
||||
}
|
||||
|
||||
if {[string index $mi 1] ne {M}} {
|
||||
set file_state [string index $mi 1]
|
||||
if {$file_state ne {M} && $file_state ne {A}} {
|
||||
unlock_index
|
||||
return
|
||||
}
|
||||
|
||||
@@ -353,6 +353,16 @@ proc parseviewrevs {view revs} {
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Escapes a list of filter paths to be passed to git log via stdin. Note that
|
||||
# paths must not be quoted.
|
||||
proc escape_filter_paths {paths} {
|
||||
set escaped [list]
|
||||
foreach path $paths {
|
||||
lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path]
|
||||
}
|
||||
return $escaped
|
||||
}
|
||||
|
||||
# Start off a git log process and arrange to read its output
|
||||
proc start_rev_list {view} {
|
||||
global startmsecs commitidx viewcomplete curview
|
||||
@@ -405,14 +415,17 @@ proc start_rev_list {view} {
|
||||
if {$revs eq {}} {
|
||||
return 0
|
||||
}
|
||||
set args [concat $vflags($view) $revs]
|
||||
set args $vflags($view)
|
||||
} else {
|
||||
set revs {}
|
||||
set args $vorigargs($view)
|
||||
}
|
||||
|
||||
if {[catch {
|
||||
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
|
||||
--parents --boundary $args "--" $files] r]
|
||||
--parents --boundary $args --stdin \
|
||||
"<<[join [concat $revs "--" \
|
||||
[escape_filter_paths $files]] "\\n"]"] r]
|
||||
} err]} {
|
||||
error_popup "[mc "Error executing git log:"] $err"
|
||||
return 0
|
||||
@@ -554,13 +567,20 @@ proc updatecommits {} {
|
||||
set revs $newrevs
|
||||
set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
|
||||
}
|
||||
set args [concat $vflags($view) $revs --not $oldpos]
|
||||
set args $vflags($view)
|
||||
foreach r $oldpos {
|
||||
lappend revs "^$r"
|
||||
}
|
||||
} else {
|
||||
set revs {}
|
||||
set args $vorigargs($view)
|
||||
}
|
||||
if {[catch {
|
||||
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
|
||||
--parents --boundary $args "--" $vfilelimit($view)] r]
|
||||
--parents --boundary $args --stdin \
|
||||
"<<[join [concat $revs "--" \
|
||||
[escape_filter_paths \
|
||||
$vfilelimit($view)]] "\\n"]"] r]
|
||||
} err]} {
|
||||
error_popup "[mc "Error executing git log:"] $err"
|
||||
return
|
||||
@@ -10228,10 +10248,16 @@ proc getallcommits {} {
|
||||
foreach id $seeds {
|
||||
lappend ids "^$id"
|
||||
}
|
||||
lappend ids "--"
|
||||
}
|
||||
}
|
||||
if {$ids ne {}} {
|
||||
set fd [open [concat $cmd $ids] r]
|
||||
if {$ids eq "--all"} {
|
||||
set cmd [concat $cmd "--all"]
|
||||
} else {
|
||||
set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"]
|
||||
}
|
||||
set fd [open $cmd r]
|
||||
fconfigure $fd -blocking 0
|
||||
incr allcommits
|
||||
nowbusy allcommits
|
||||
|
||||
26
http.c
26
http.c
@@ -165,7 +165,13 @@ static char *cached_accept_language;
|
||||
|
||||
static char *http_ssl_backend;
|
||||
|
||||
static int http_schannel_check_revoke = 1;
|
||||
static int http_schannel_check_revoke_mode =
|
||||
#ifdef CURLSSLOPT_REVOKE_BEST_EFFORT
|
||||
CURLSSLOPT_REVOKE_BEST_EFFORT;
|
||||
#else
|
||||
CURLSSLOPT_NO_REVOKE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -330,7 +336,19 @@ static int http_options(const char *var, const char *value, void *cb)
|
||||
}
|
||||
|
||||
if (!strcmp("http.schannelcheckrevoke", var)) {
|
||||
http_schannel_check_revoke = git_config_bool(var, value);
|
||||
if (value && !strcmp(value, "best-effort")) {
|
||||
http_schannel_check_revoke_mode =
|
||||
#ifdef CURLSSLOPT_REVOKE_BEST_EFFORT
|
||||
CURLSSLOPT_REVOKE_BEST_EFFORT;
|
||||
#else
|
||||
CURLSSLOPT_NO_REVOKE;
|
||||
warning(_("%s=%s unsupported by current cURL"),
|
||||
var, value);
|
||||
#endif
|
||||
} else
|
||||
http_schannel_check_revoke_mode =
|
||||
(git_config_bool(var, value) ?
|
||||
0 : CURLSSLOPT_NO_REVOKE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -903,9 +921,9 @@ static CURL *get_curl_handle(void)
|
||||
#endif
|
||||
|
||||
if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
|
||||
!http_schannel_check_revoke) {
|
||||
http_schannel_check_revoke_mode) {
|
||||
#if LIBCURL_VERSION_NUM >= 0x072c00
|
||||
curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
|
||||
curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, http_schannel_check_revoke_mode);
|
||||
#else
|
||||
warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));
|
||||
#endif
|
||||
|
||||
44
path.c
44
path.c
@@ -12,6 +12,7 @@
|
||||
#include "packfile.h"
|
||||
#include "object-store.h"
|
||||
#include "lockfile.h"
|
||||
#include "exec-cmd.h"
|
||||
|
||||
static int get_st_mode_bits(const char *path, int *mode)
|
||||
{
|
||||
@@ -732,6 +733,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;
|
||||
@@ -1289,6 +1294,45 @@ char *strip_path_suffix(const char *path, const char *suffix)
|
||||
return offset == -1 ? NULL : xstrndup(path, offset);
|
||||
}
|
||||
|
||||
int is_mount_point_via_stat(struct strbuf *path)
|
||||
{
|
||||
size_t len = path->len;
|
||||
unsigned int current_dev;
|
||||
struct stat st;
|
||||
|
||||
if (!strcmp("/", path->buf))
|
||||
return 1;
|
||||
|
||||
strbuf_addstr(path, "/.");
|
||||
if (lstat(path->buf, &st)) {
|
||||
/*
|
||||
* If we cannot access the current directory, we cannot say
|
||||
* that it is a bind mount.
|
||||
*/
|
||||
strbuf_setlen(path, len);
|
||||
return 0;
|
||||
}
|
||||
current_dev = st.st_dev;
|
||||
|
||||
/* Now look at the parent directory */
|
||||
strbuf_addch(path, '.');
|
||||
if (lstat(path->buf, &st)) {
|
||||
/*
|
||||
* If we cannot access the parent directory, we cannot say
|
||||
* that it is a bind mount.
|
||||
*/
|
||||
strbuf_setlen(path, len);
|
||||
return 0;
|
||||
}
|
||||
strbuf_setlen(path, len);
|
||||
|
||||
/*
|
||||
* If the device ID differs between current and parent directory,
|
||||
* then it is a bind mount.
|
||||
*/
|
||||
return current_dev != st.st_dev;
|
||||
}
|
||||
|
||||
int daemon_avoid_alias(const char *p)
|
||||
{
|
||||
int sl, ndot;
|
||||
|
||||
2
prompt.c
2
prompt.c
@@ -80,7 +80,7 @@ int git_read_line_interactively(struct strbuf *line)
|
||||
int ret;
|
||||
|
||||
fflush(stdout);
|
||||
ret = strbuf_getline_lf(line, stdin);
|
||||
ret = strbuf_getline(line, stdin);
|
||||
if (ret != EOF)
|
||||
strbuf_trim_trailing_newline(line);
|
||||
|
||||
|
||||
13
send-pack.c
13
send-pack.c
@@ -39,6 +39,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 &&
|
||||
@@ -437,6 +447,7 @@ int send_pack(struct send_pack_args *args,
|
||||
const char *push_cert_nonce = NULL;
|
||||
struct packet_reader reader;
|
||||
|
||||
git_config(send_pack_config, NULL);
|
||||
git_config_get_bool("transfer.advertisesid", &advertise_sid);
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
@@ -448,7 +459,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;
|
||||
|
||||
@@ -38,10 +38,10 @@ test_expect_success 'looping aliases - internal execution' '
|
||||
#'
|
||||
|
||||
test_expect_success 'run-command formats empty args properly' '
|
||||
test_must_fail env GIT_TRACE=1 git frotz a "" b " " c 2>actual.raw &&
|
||||
sed -ne "/run_command:/s/.*trace: run_command: //p" actual.raw >actual &&
|
||||
echo "git-frotz a '\'''\'' b '\'' '\'' c" >expect &&
|
||||
test_cmp expect actual
|
||||
test_must_fail env GIT_TRACE=1 git frotz a "" b " " c 2>actual.raw &&
|
||||
sed -ne "/run_command:/s/.*trace: run_command: //p" actual.raw >actual &&
|
||||
echo "git-frotz a '\'''\'' b '\'' '\'' c" >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -495,4 +495,42 @@ test_expect_success MINGW 'is_valid_path() on Windows' '
|
||||
"PRN./abc"
|
||||
'
|
||||
|
||||
test_expect_success MINGW 'MSYSTEM/PATH is adjusted if necessary' '
|
||||
mkdir -p "$HOME"/bin pretend/mingw64/bin \
|
||||
pretend/mingw64/libexec/git-core pretend/usr/bin &&
|
||||
cp "$GIT_EXEC_PATH"/git.exe pretend/mingw64/bin/ &&
|
||||
cp "$GIT_EXEC_PATH"/git.exe pretend/mingw64/libexec/git-core/ &&
|
||||
echo "env | grep MSYSTEM=" | write_script "$HOME"/bin/git-test-home &&
|
||||
echo "echo mingw64" | write_script pretend/mingw64/bin/git-test-bin &&
|
||||
echo "echo usr" | write_script pretend/usr/bin/git-test-bin2 &&
|
||||
|
||||
(
|
||||
MSYSTEM= &&
|
||||
GIT_EXEC_PATH= &&
|
||||
pretend/mingw64/libexec/git-core/git.exe test-home >actual &&
|
||||
pretend/mingw64/libexec/git-core/git.exe test-bin >>actual &&
|
||||
pretend/mingw64/bin/git.exe test-bin2 >>actual
|
||||
) &&
|
||||
test_write_lines MSYSTEM=$MSYSTEM mingw64 usr >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_lazy_prereq RUNTIME_PREFIX '
|
||||
test true = "$RUNTIME_PREFIX"
|
||||
'
|
||||
|
||||
test_lazy_prereq CAN_EXEC_IN_PWD '
|
||||
cp "$GIT_EXEC_PATH"/git$X ./ &&
|
||||
./git rev-parse
|
||||
'
|
||||
|
||||
test_expect_success RUNTIME_PREFIX,CAN_EXEC_IN_PWD 'RUNTIME_PREFIX works' '
|
||||
mkdir -p pretend/git pretend/libexec/git-core &&
|
||||
echo "echo HERE" | write_script pretend/libexec/git-core/git-here &&
|
||||
cp "$GIT_EXEC_PATH"/git$X pretend/git/ &&
|
||||
GIT_EXEC_PATH= ./pretend/git/git here >actual &&
|
||||
echo HERE >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -423,4 +423,15 @@ test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
|
||||
git add "$downcased"
|
||||
'
|
||||
|
||||
test_expect_success MINGW 'can add files via NTFS junctions' '
|
||||
test_when_finished "cmd //c rmdir junction && rm -rf target" &&
|
||||
test_create_repo target &&
|
||||
cmd //c "mklink /j junction target" &&
|
||||
>target/via-junction &&
|
||||
git -C junction add "$(pwd)/junction/via-junction" &&
|
||||
echo via-junction >expect &&
|
||||
git -C target diff --cached --name-only >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -944,6 +944,27 @@ test_expect_success 'checkout -p patch editing of added file' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success EXPENSIVE 'add -i with a lot of files' '
|
||||
git reset --hard &&
|
||||
x160=0123456789012345678901234567890123456789 &&
|
||||
x160=$x160$x160$x160$x160 &&
|
||||
y= &&
|
||||
i=0 &&
|
||||
while test $i -le 200
|
||||
do
|
||||
name=$(printf "%s%03d" $x160 $i) &&
|
||||
echo $name >$name &&
|
||||
git add -N $name &&
|
||||
y="${y}y$LF" &&
|
||||
i=$(($i+1)) ||
|
||||
break
|
||||
done &&
|
||||
echo "$y" | git add -p -- . &&
|
||||
git diff --cached >staged &&
|
||||
test_line_count = 1407 staged &&
|
||||
git reset --hard
|
||||
'
|
||||
|
||||
test_expect_success 'show help from add--helper' '
|
||||
git reset --hard &&
|
||||
cat >expect <<-EOF &&
|
||||
|
||||
@@ -734,8 +734,8 @@ test_expect_success '"remote show" does not show symbolic refs' '
|
||||
(
|
||||
cd three &&
|
||||
git remote show origin >output &&
|
||||
! grep "^ *HEAD$" < output &&
|
||||
! grep -i stale < output
|
||||
! grep "^ *HEAD$" <output &&
|
||||
! grep -i stale <output
|
||||
)
|
||||
'
|
||||
|
||||
@@ -923,6 +923,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
|
||||
(
|
||||
cd six &&
|
||||
git remote rm origin &&
|
||||
mkdir -p .git/branches &&
|
||||
echo "$origin_url#main" >.git/branches/origin &&
|
||||
git remote rename origin origin &&
|
||||
test_path_is_missing .git/branches/origin &&
|
||||
@@ -937,7 +938,8 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)'
|
||||
(
|
||||
cd seven &&
|
||||
git remote rm origin &&
|
||||
echo "quux#foom" > .git/branches/origin &&
|
||||
mkdir -p .git/branches &&
|
||||
echo "quux#foom" >.git/branches/origin &&
|
||||
git remote rename origin origin &&
|
||||
test_path_is_missing .git/branches/origin &&
|
||||
test "$(git config remote.origin.url)" = "quux" &&
|
||||
|
||||
@@ -872,7 +872,8 @@ test_expect_success 'fetch with branches' '
|
||||
mk_empty testrepo &&
|
||||
git branch second $the_first_commit &&
|
||||
git checkout second &&
|
||||
echo ".." > testrepo/.git/branches/branch1 &&
|
||||
mkdir -p testrepo/.git/branches &&
|
||||
echo ".." >testrepo/.git/branches/branch1 &&
|
||||
(
|
||||
cd testrepo &&
|
||||
git fetch branch1 &&
|
||||
@@ -885,7 +886,8 @@ test_expect_success 'fetch with branches' '
|
||||
|
||||
test_expect_success 'fetch with branches containing #' '
|
||||
mk_empty testrepo &&
|
||||
echo "..#second" > testrepo/.git/branches/branch2 &&
|
||||
mkdir -p testrepo/.git/branches &&
|
||||
echo "..#second" >testrepo/.git/branches/branch2 &&
|
||||
(
|
||||
cd testrepo &&
|
||||
git fetch branch2 &&
|
||||
@@ -899,7 +901,8 @@ test_expect_success 'fetch with branches containing #' '
|
||||
test_expect_success 'push with branches' '
|
||||
mk_empty testrepo &&
|
||||
git checkout second &&
|
||||
echo "testrepo" > .git/branches/branch1 &&
|
||||
mkdir -p .git/branches &&
|
||||
echo "testrepo" >.git/branches/branch1 &&
|
||||
git push branch1 &&
|
||||
(
|
||||
cd testrepo &&
|
||||
@@ -911,7 +914,8 @@ test_expect_success 'push with branches' '
|
||||
|
||||
test_expect_success 'push with branches containing #' '
|
||||
mk_empty testrepo &&
|
||||
echo "testrepo#branch3" > .git/branches/branch2 &&
|
||||
mkdir -p .git/branches &&
|
||||
echo "testrepo#branch3" >.git/branches/branch2 &&
|
||||
git push branch2 &&
|
||||
(
|
||||
cd testrepo &&
|
||||
@@ -1436,7 +1440,7 @@ EOF
|
||||
git init no-thin &&
|
||||
git --git-dir=no-thin/.git config receive.unpacklimit 0 &&
|
||||
git push no-thin/.git refs/heads/master:refs/heads/foo &&
|
||||
echo modified >> path1 &&
|
||||
echo modified >>path1 &&
|
||||
git commit -am modified &&
|
||||
git repack -adf &&
|
||||
rcvpck="git receive-pack --reject-thin-pack-for-testing" &&
|
||||
|
||||
@@ -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
|
||||
'
|
||||
|
||||
@@ -68,6 +68,13 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success CASE_INSENSITIVE_FS 'core.worktree is not added due to path case' '
|
||||
|
||||
mkdir UPPERCASE &&
|
||||
git clone src "$(pwd)/uppercase" &&
|
||||
test "unset" = "$(git -C UPPERCASE config --default unset core.worktree)"
|
||||
'
|
||||
|
||||
test_expect_success 'clone from hooks' '
|
||||
|
||||
test_create_repo r0 &&
|
||||
|
||||
@@ -236,7 +236,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
|
||||
|
||||
@@ -746,4 +746,14 @@ test_expect_success 'clean untracked paths by pathspec' '
|
||||
test_must_be_empty actual
|
||||
'
|
||||
|
||||
test_expect_success MINGW 'clean does not traverse mount points' '
|
||||
mkdir target &&
|
||||
>target/dont-clean-me &&
|
||||
git init with-mountpoint &&
|
||||
cmd //c "mklink /j with-mountpoint\\mountpoint target" &&
|
||||
git -C with-mountpoint clean -dfx &&
|
||||
test_path_is_missing with-mountpoint/mountpoint &&
|
||||
test_path_is_file target/dont-clean-me
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -271,6 +271,48 @@ test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on
|
||||
test_must_be_empty actual
|
||||
'
|
||||
|
||||
test_expect_success 'helper-editor' '
|
||||
|
||||
write_script lf-to-crlf.sh <<-\EOF
|
||||
sed "s/\$/Q/" <"$1" | tr Q "\\015" >"$1".new &&
|
||||
mv -f "$1".new "$1"
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success 'cleanup commit messages (scissors option,-F,-e, CR/LF line endings)' '
|
||||
|
||||
test_config core.editor "\"$PWD/lf-to-crlf.sh\"" &&
|
||||
scissors="# ------------------------ >8 ------------------------" &&
|
||||
|
||||
test_write_lines >text \
|
||||
"# Keep this comment" "" " $scissors" \
|
||||
"# Keep this comment, too" "$scissors" \
|
||||
"# Remove this comment" "$scissors" \
|
||||
"Remove this comment, too" &&
|
||||
|
||||
test_write_lines >expect \
|
||||
"# Keep this comment" "" " $scissors" \
|
||||
"# Keep this comment, too" &&
|
||||
|
||||
git commit --cleanup=scissors -e -F text --allow-empty &&
|
||||
git cat-file -p HEAD >raw &&
|
||||
sed -e "1,/^\$/d" raw >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line, CR/LF line endings)' '
|
||||
|
||||
scissors="# ------------------------ >8 ------------------------" &&
|
||||
test_write_lines >text \
|
||||
"$scissors" \
|
||||
"# Remove this comment and any following lines" &&
|
||||
cp text /tmp/test2-text &&
|
||||
git commit --cleanup=scissors -e -F text --allow-empty --allow-empty-message &&
|
||||
git cat-file -p HEAD >raw &&
|
||||
sed -e "1,/^\$/d" raw >actual &&
|
||||
test_must_be_empty actual
|
||||
'
|
||||
|
||||
test_expect_success 'cleanup commit messages (strip option,-F)' '
|
||||
|
||||
echo >>negative &&
|
||||
|
||||
@@ -7,6 +7,19 @@ test_description='git maintenance builtin'
|
||||
GIT_TEST_COMMIT_GRAPH=0
|
||||
GIT_TEST_MULTI_PACK_INDEX=0
|
||||
|
||||
test_lazy_prereq XMLLINT '
|
||||
xmllint --version
|
||||
'
|
||||
|
||||
test_xmllint () {
|
||||
if test_have_prereq XMLLINT
|
||||
then
|
||||
xmllint --noout "$@"
|
||||
else
|
||||
true
|
||||
fi
|
||||
}
|
||||
|
||||
test_expect_success 'help text' '
|
||||
test_expect_code 129 git maintenance -h 2>err &&
|
||||
test_i18ngrep "usage: git maintenance <subcommand>" err &&
|
||||
@@ -419,7 +432,7 @@ test_expect_success !MINGW 'register and unregister with regex metacharacters' '
|
||||
'
|
||||
|
||||
test_expect_success 'start from empty cron table' '
|
||||
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
|
||||
|
||||
# start registers the repo
|
||||
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
|
||||
@@ -430,19 +443,19 @@ test_expect_success 'start from empty cron table' '
|
||||
'
|
||||
|
||||
test_expect_success 'stop from existing schedule' '
|
||||
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
|
||||
|
||||
# stop does not unregister the repo
|
||||
git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
|
||||
|
||||
# Operation is idempotent
|
||||
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
|
||||
test_must_be_empty cron.txt
|
||||
'
|
||||
|
||||
test_expect_success 'start preserves existing schedule' '
|
||||
echo "Important information!" >cron.txt &&
|
||||
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
|
||||
grep "Important information!" cron.txt
|
||||
'
|
||||
|
||||
@@ -457,11 +470,94 @@ test_expect_success 'magic markers are correct' '
|
||||
|
||||
test_expect_success 'stop preserves surrounding schedule' '
|
||||
echo "Crucial information!" >>cron.txt &&
|
||||
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
|
||||
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
|
||||
grep "Important information!" cron.txt &&
|
||||
grep "Crucial information!" cron.txt
|
||||
'
|
||||
|
||||
test_expect_success 'start and stop macOS maintenance' '
|
||||
# ensure $HOME can be compared against hook arguments on all platforms
|
||||
pfx=$(cd "$HOME" && pwd) &&
|
||||
|
||||
write_script print-args <<-\EOF &&
|
||||
echo $* | sed "s:gui/[0-9][0-9]*:gui/[UID]:" >>args
|
||||
EOF
|
||||
|
||||
rm -f args &&
|
||||
GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
|
||||
|
||||
# start registers the repo
|
||||
git config --get --global maintenance.repo "$(pwd)" &&
|
||||
|
||||
ls "$HOME/Library/LaunchAgents" >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
org.git-scm.git.daily.plist
|
||||
org.git-scm.git.hourly.plist
|
||||
org.git-scm.git.weekly.plist
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
rm -f expect &&
|
||||
for frequency in hourly daily weekly
|
||||
do
|
||||
PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" &&
|
||||
test_xmllint "$PLIST" &&
|
||||
grep schedule=$frequency "$PLIST" &&
|
||||
echo "bootout gui/[UID] $PLIST" >>expect &&
|
||||
echo "bootstrap gui/[UID] $PLIST" >>expect || return 1
|
||||
done &&
|
||||
test_cmp expect args &&
|
||||
|
||||
rm -f args &&
|
||||
GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance stop &&
|
||||
|
||||
# stop does not unregister the repo
|
||||
git config --get --global maintenance.repo "$(pwd)" &&
|
||||
|
||||
printf "bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
|
||||
hourly daily weekly >expect &&
|
||||
test_cmp expect args &&
|
||||
ls "$HOME/Library/LaunchAgents" >actual &&
|
||||
test_line_count = 0 actual
|
||||
'
|
||||
|
||||
test_expect_success 'start and stop Windows maintenance' '
|
||||
write_script print-args <<-\EOF &&
|
||||
echo $* >>args
|
||||
while test $# -gt 0
|
||||
do
|
||||
case "$1" in
|
||||
/xml) shift; xmlfile=$1; break ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
test -z "$xmlfile" || cp "$xmlfile" "$xmlfile.xml"
|
||||
EOF
|
||||
|
||||
rm -f args &&
|
||||
GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start &&
|
||||
|
||||
# start registers the repo
|
||||
git config --get --global maintenance.repo "$(pwd)" &&
|
||||
|
||||
for frequency in hourly daily weekly
|
||||
do
|
||||
grep "/create /tn Git Maintenance ($frequency) /f /xml" args &&
|
||||
file=$(ls .git/schedule_${frequency}*.xml) &&
|
||||
test_xmllint "$file" || return 1
|
||||
done &&
|
||||
|
||||
rm -f args &&
|
||||
GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance stop &&
|
||||
|
||||
# stop does not unregister the repo
|
||||
git config --get --global maintenance.repo "$(pwd)" &&
|
||||
|
||||
printf "/delete /tn Git Maintenance (%s) /f\n" \
|
||||
hourly daily weekly >expect &&
|
||||
test_cmp expect args
|
||||
'
|
||||
|
||||
test_expect_success 'register preserves existing strategy' '
|
||||
git config maintenance.strategy none &&
|
||||
git maintenance register &&
|
||||
|
||||
@@ -747,4 +747,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
|
||||
|
||||
@@ -1713,7 +1713,8 @@ test_lazy_prereq REBASE_P '
|
||||
'
|
||||
|
||||
# Ensure that no test accidentally triggers a Git command
|
||||
# that runs 'crontab', affecting a user's cron schedule.
|
||||
# Tests that verify the cron integration must set this locally
|
||||
# that runs the actual maintenance scheduler, affecting a user's
|
||||
# system permanently.
|
||||
# Tests that verify the scheduler integration must set this locally
|
||||
# to avoid errors.
|
||||
GIT_TEST_CRONTAB="exit 1"
|
||||
GIT_TEST_MAINT_SCHEDULER="none:exit 1"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "protocol.h"
|
||||
|
||||
static int debug;
|
||||
/* TODO: put somewhere sensible, e.g. git_transport_options? */
|
||||
static int auto_gc = 1;
|
||||
|
||||
struct helper_data {
|
||||
const char *name;
|
||||
@@ -478,10 +480,25 @@ static int get_exporter(struct transport *transport,
|
||||
for (i = 0; i < revlist_args->nr; i++)
|
||||
strvec_push(&fastexport->args, revlist_args->items[i].string);
|
||||
|
||||
strvec_push(&fastexport->args, "--");
|
||||
|
||||
fastexport->git_cmd = 1;
|
||||
return start_command(fastexport);
|
||||
}
|
||||
|
||||
static void check_helper_status(struct helper_data *data)
|
||||
{
|
||||
int pid, status;
|
||||
|
||||
pid = waitpid(data->helper->pid, &status, WNOHANG);
|
||||
if (pid < 0)
|
||||
die("Could not retrieve status of remote helper '%s'",
|
||||
data->name);
|
||||
if (pid > 0 && WIFEXITED(status))
|
||||
die("Remote helper '%s' died with %d",
|
||||
data->name, WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
static int fetch_with_import(struct transport *transport,
|
||||
int nr_heads, struct ref **to_fetch)
|
||||
{
|
||||
@@ -518,6 +535,7 @@ static int fetch_with_import(struct transport *transport,
|
||||
|
||||
if (finish_command(&fastimport))
|
||||
die(_("error while running fast-import"));
|
||||
check_helper_status(data);
|
||||
|
||||
/*
|
||||
* The fast-import stream of a remote helper that advertises
|
||||
@@ -551,6 +569,12 @@ static int fetch_with_import(struct transport *transport,
|
||||
}
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
if (auto_gc) {
|
||||
const char *argv_gc_auto[] = {
|
||||
"gc", "--auto", "--quiet", NULL,
|
||||
};
|
||||
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1105,6 +1129,7 @@ static int push_refs_with_export(struct transport *transport,
|
||||
|
||||
if (finish_command(&exporter))
|
||||
die(_("error while running fast-export"));
|
||||
check_helper_status(data);
|
||||
if (push_update_refs_status(data, remote_refs, flags))
|
||||
return 1;
|
||||
|
||||
|
||||
13
wt-status.c
13
wt-status.c
@@ -22,7 +22,7 @@
|
||||
#define AB_DELAY_WARNING_IN_MS (2 * 1000)
|
||||
|
||||
static const char cut_line[] =
|
||||
"------------------------ >8 ------------------------\n";
|
||||
"------------------------ >8 ------------------------";
|
||||
|
||||
static char default_wt_status_colors[][COLOR_MAXLEN] = {
|
||||
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
|
||||
@@ -999,15 +999,22 @@ conclude:
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
|
||||
}
|
||||
|
||||
static inline int starts_with_newline(const char *p)
|
||||
{
|
||||
return *p == '\n' || (*p == '\r' && p[1] == '\n');
|
||||
}
|
||||
|
||||
size_t wt_status_locate_end(const char *s, size_t len)
|
||||
{
|
||||
const char *p;
|
||||
struct strbuf pattern = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
|
||||
if (starts_with(s, pattern.buf + 1))
|
||||
if (starts_with(s, pattern.buf + 1) &&
|
||||
starts_with_newline(s + pattern.len - 1))
|
||||
len = 0;
|
||||
else if ((p = strstr(s, pattern.buf)))
|
||||
else if ((p = strstr(s, pattern.buf)) &&
|
||||
starts_with_newline(p + pattern.len))
|
||||
len = p - s + 1;
|
||||
strbuf_release(&pattern);
|
||||
return len;
|
||||
|
||||
Reference in New Issue
Block a user