diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4017ed82ca4341..c19530b086311a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,8 +1,10 @@ - - [ ] I was not able to find an [open](https://github.com/git-for-windows/git/issues?q=is%3Aopen) or [closed](https://github.com/git-for-windows/git/issues?q=is%3Aclosed) issue matching what I'm seeing + - [ ] I was not able to find an [open](https://github.com/microsoft/git/issues?q=is%3Aopen) + or [closed](https://github.com/microsoft/git/issues?q=is%3Aclosed) issue matching + what I'm seeing, including in [the `git-for-windows/git` tracker](https://github.com/git-for-windows/git/issues). ### Setup - - Which version of Git for Windows are you using? Is it 32-bit or 64-bit? + - Which version of `microsoft/git` are you using? Is it 32-bit or 64-bit? ``` $ git --version --build-options @@ -10,24 +12,22 @@ $ git --version --build-options ** insert your machine's response here ** ``` - - Which version of Windows are you running? Vista, 7, 8, 10? Is it 32-bit or 64-bit? +Are you using Scalar or VFS for Git? + +** insert your answer here ** + +If VFS for Git, then what version? ``` -$ cmd.exe /c ver +$ gvfs version ** insert your machine's response here ** ``` - - What options did you set as part of the installation? Or did you choose the - defaults? + - Which version of Windows are you running? Vista, 7, 8, 10? Is it 32-bit or 64-bit? ``` -# One of the following: -> type "C:\Program Files\Git\etc\install-options.txt" -> type "C:\Program Files (x86)\Git\etc\install-options.txt" -> type "%USERPROFILE%\AppData\Local\Programs\Git\etc\install-options.txt" -> type "$env:USERPROFILE\AppData\Local\Programs\Git\etc\install-options.txt" -$ cat /etc/install-options.txt +$ cmd.exe /c ver ** insert your machine's response here ** ``` @@ -58,7 +58,11 @@ $ cat /etc/install-options.txt ** insert here ** - - If the problem was occurring with a specific repository, can you provide the - URL to that repository to help us with testing? + - If the problem was occurring with a specific repository, can you specify + the repository? -** insert URL here ** + * [ ] Public repo: **insert URL here** + * [ ] Windows monorepo + * [ ] Office monorepo + * [ ] Other Microsoft-internal repo: **insert name here** + * [ ] Other internal repo. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7baf31f2c471ec..3cb48d8582f31c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,22 +1,10 @@ Thanks for taking the time to contribute to Git! -Those seeking to contribute to the Git for Windows fork should see -http://gitforwindows.org/#contribute on how to contribute Windows specific -enhancements. - -If your contribution is for the core Git functions and documentation -please be aware that the Git community does not use the github.com issues -or pull request mechanism for their contributions. - -Instead, we use the Git mailing list (git@vger.kernel.org) for code and -documentation submissions, code reviews, and bug reports. The -mailing list is plain text only (anything with HTML is sent directly -to the spam folder). - -Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/) -to conveniently send your Pull Requests commits to our mailing list. - -For a single-commit pull request, please *leave the pull request description -empty*: your commit message itself should describe your changes. - -Please read the "guidelines for contributing" linked above! +This fork contains changes specific to monorepo scenarios. If you are an +external contributor, then please detail your reason for submitting to +this fork: + +* [ ] This is an early version of work already under review upstream. +* [ ] This change only applies to interactions with Azure DevOps and the + GVFS Protocol. +* [ ] This change only applies to the virtualization hook and VFS for Git. diff --git a/.github/macos-installer/Makefile b/.github/macos-installer/Makefile new file mode 100644 index 00000000000000..1a06f6200e62dc --- /dev/null +++ b/.github/macos-installer/Makefile @@ -0,0 +1,157 @@ +SHELL := /bin/bash +SUDO := sudo +C_INCLUDE_PATH := /usr/include +CPLUS_INCLUDE_PATH := /usr/include +LD_LIBRARY_PATH := /usr/lib + +OSX_VERSION := $(shell sw_vers -productVersion) +TARGET_FLAGS := -mmacosx-version-min=$(OSX_VERSION) -DMACOSX_DEPLOYMENT_TARGET=$(OSX_VERSION) + +uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') + +ARCH_UNIV := universal +ARCH_FLAGS := -arch x86_64 -arch arm64 + +CFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS) +LDFLAGS := $(TARGET_FLAGS) $(ARCH_FLAGS) + +PREFIX := /usr/local +GIT_PREFIX := $(PREFIX)/git + +BUILD_DIR := $(GITHUB_WORKSPACE)/payload +DESTDIR := $(PWD)/stage/git-$(ARCH_UNIV)-$(VERSION) +ARTIFACTDIR := build-artifacts +SUBMAKE := $(MAKE) C_INCLUDE_PATH="$(C_INCLUDE_PATH)" CPLUS_INCLUDE_PATH="$(CPLUS_INCLUDE_PATH)" LD_LIBRARY_PATH="$(LD_LIBRARY_PATH)" TARGET_FLAGS="$(TARGET_FLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" NO_GETTEXT=1 NO_DARWIN_PORTS=1 prefix=$(GIT_PREFIX) GIT_BUILT_FROM_COMMIT="$(GIT_BUILT_FROM_COMMIT)" DESTDIR=$(DESTDIR) +CORES := $(shell bash -c "sysctl hw.ncpu | awk '{print \$$2}'") + +# Guard against environment variables +APPLE_APP_IDENTITY = +APPLE_INSTALLER_IDENTITY = +APPLE_KEYCHAIN_PROFILE = + +.PHONY: image pkg payload codesign notarize + +.SECONDARY: + +$(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(ARCH_UNIV): + rm -f $(BUILD_DIR)/git-$(VERSION)/osx-installed* + mkdir -p $(DESTDIR)$(GIT_PREFIX) + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-built-keychain: + cd $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain; $(SUBMAKE) CFLAGS="$(CFLAGS) -g -O2 -Wall" + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-built: + [ -d $(DESTDIR)$(GIT_PREFIX) ] && $(SUDO) rm -rf $(DESTDIR) || echo ok + cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) -j $(CORES) all strip + echo "================" + echo "Dumping Linkage" + cd $(BUILD_DIR)/git-$(VERSION); ./git version + echo "====" + cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git + echo "====" + cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-http-fetch + echo "====" + cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-http-push + echo "====" + cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-remote-http + echo "====" + cd $(BUILD_DIR)/git-$(VERSION); /usr/bin/otool -L ./git-gvfs-helper + echo "================" + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-installed-bin: $(BUILD_DIR)/git-$(VERSION)/osx-built $(BUILD_DIR)/git-$(VERSION)/osx-built-keychain + cd $(BUILD_DIR)/git-$(VERSION); $(SUBMAKE) install + cp $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain $(DESTDIR)$(GIT_PREFIX)/bin/git-credential-osxkeychain + mkdir -p $(DESTDIR)$(GIT_PREFIX)/contrib/completion + cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.bash $(DESTDIR)$(GIT_PREFIX)/contrib/completion/ + cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-completion.zsh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/ + cp $(BUILD_DIR)/git-$(VERSION)/contrib/completion/git-prompt.sh $(DESTDIR)$(GIT_PREFIX)/contrib/completion/ + # This is needed for Git-Gui, GitK + mkdir -p $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl + [ ! -f $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl/Error.pm ] && cp $(BUILD_DIR)/git-$(VERSION)/perl/private-Error.pm $(DESTDIR)$(GIT_PREFIX)/lib/perl5/site_perl/Error.pm || echo done + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-installed-man: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin + mkdir -p $(DESTDIR)$(GIT_PREFIX)/share/man + cp -R $(GITHUB_WORKSPACE)/manpages/ $(DESTDIR)$(GIT_PREFIX)/share/man + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-built-subtree: + cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" all git-subtree.1 + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree: $(BUILD_DIR)/git-$(VERSION)/osx-built-subtree + mkdir -p $(DESTDIR) + cd $(BUILD_DIR)/git-$(VERSION)/contrib/subtree; $(SUBMAKE) XML_CATALOG_FILES="$(XML_CATALOG_FILES)" install install-man + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-installed-assets: $(BUILD_DIR)/git-$(VERSION)/osx-installed-bin + mkdir -p $(DESTDIR)$(GIT_PREFIX)/etc + cat assets/etc/gitconfig.osxkeychain >> $(DESTDIR)$(GIT_PREFIX)/etc/gitconfig + cp assets/uninstall.sh $(DESTDIR)$(GIT_PREFIX)/uninstall.sh + sh -c "echo .DS_Store >> $(DESTDIR)$(GIT_PREFIX)/share/git-core/templates/info/exclude" + +symlinks: + mkdir -p $(ARTIFACTDIR)$(PREFIX)/bin + cd $(ARTIFACTDIR)$(PREFIX)/bin; find ../git/bin -type f -exec ln -sf {} \; + for man in man1 man3 man5 man7; do mkdir -p $(ARTIFACTDIR)$(PREFIX)/share/man/$$man; (cd $(ARTIFACTDIR)$(PREFIX)/share/man/$$man; ln -sf ../../../git/share/man/$$man/* ./); done + ruby ../scripts/symlink-git-hardlinks.rb $(ARTIFACTDIR) + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-installed: $(DESTDIR)$(GIT_PREFIX)/VERSION-$(VERSION)-$(ARCH_UNIV) $(BUILD_DIR)/git-$(VERSION)/osx-installed-man $(BUILD_DIR)/git-$(VERSION)/osx-installed-assets $(BUILD_DIR)/git-$(VERSION)/osx-installed-subtree + find $(DESTDIR)$(GIT_PREFIX) -type d -exec chmod ugo+rx {} \; + find $(DESTDIR)$(GIT_PREFIX) -type f -exec chmod ugo+r {} \; + touch $@ + +$(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_UNIV): $(BUILD_DIR)/git-$(VERSION)/osx-built + File $(BUILD_DIR)/git-$(VERSION)/git + File $(BUILD_DIR)/git-$(VERSION)/contrib/credential/osxkeychain/git-credential-osxkeychain + touch $@ + +disk-image/VERSION-$(VERSION)-$(ARCH_UNIV): + rm -f disk-image/*.pkg disk-image/VERSION-* disk-image/.DS_Store + mkdir disk-image + touch "$@" + +pkg_cmd := pkgbuild --identifier com.git.pkg --version $(VERSION) \ + --root $(ARTIFACTDIR)$(PREFIX) --scripts assets/scripts \ + --install-location $(PREFIX) --component-plist ./assets/git-components.plist + +ifdef APPLE_INSTALLER_IDENTITY + pkg_cmd += --sign "$(APPLE_INSTALLER_IDENTITY)" +endif + +pkg_cmd += disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg +disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg: disk-image/VERSION-$(VERSION)-$(ARCH_UNIV) symlinks + $(pkg_cmd) + +git-%-$(ARCH_UNIV).dmg: + hdiutil create git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) $(ARCH_UNIV)" -ov 2>&1 | tee err || { \ + grep "Resource busy" err && \ + sleep 5 && \ + hdiutil create git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg -fs HFS+ -srcfolder disk-image -volname "Git $(VERSION) $(ARCH_UNIV)" -ov; } + hdiutil convert -format UDZO -o $@ git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg + rm -f git-$(VERSION)-$(ARCH_UNIV).uncompressed.dmg + +payload: $(BUILD_DIR)/git-$(VERSION)/osx-installed $(BUILD_DIR)/git-$(VERSION)/osx-built-assert-$(ARCH_UNIV) + +pkg: disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg + +image: git-$(VERSION)-$(ARCH_UNIV).dmg + +ifdef APPLE_APP_IDENTITY +codesign: + @$(CURDIR)/../scripts/codesign.sh --payload="build-artifacts/usr/local/git" \ + --identity="$(APPLE_APP_IDENTITY)" \ + --entitlements="$(CURDIR)/entitlements.xml" +endif + +# Notarization can only happen if the package is fully signed +ifdef APPLE_KEYCHAIN_PROFILE +notarize: + @$(CURDIR)/../scripts/notarize.sh \ + --package="disk-image/git-$(VERSION)-$(ARCH_UNIV).pkg" \ + --keychain-profile="$(APPLE_KEYCHAIN_PROFILE)" +endif diff --git a/.github/macos-installer/assets/etc/gitconfig.osxkeychain b/.github/macos-installer/assets/etc/gitconfig.osxkeychain new file mode 100644 index 00000000000000..788266b3a40a9d --- /dev/null +++ b/.github/macos-installer/assets/etc/gitconfig.osxkeychain @@ -0,0 +1,2 @@ +[credential] + helper = osxkeychain diff --git a/.github/macos-installer/assets/git-components.plist b/.github/macos-installer/assets/git-components.plist new file mode 100644 index 00000000000000..78db36777df3ed --- /dev/null +++ b/.github/macos-installer/assets/git-components.plist @@ -0,0 +1,18 @@ + + + + + + BundleHasStrictIdentifier + + BundleIsRelocatable + + BundleIsVersionChecked + + BundleOverwriteAction + upgrade + RootRelativeBundlePath + git/share/git-gui/lib/Git Gui.app + + + diff --git a/.github/macos-installer/assets/scripts/postinstall b/.github/macos-installer/assets/scripts/postinstall new file mode 100755 index 00000000000000..94056db9b7b864 --- /dev/null +++ b/.github/macos-installer/assets/scripts/postinstall @@ -0,0 +1,62 @@ +#!/bin/bash +INSTALL_DST="$2" +SCALAR_C_CMD="$INSTALL_DST/git/bin/scalar" +SCALAR_DOTNET_CMD="/usr/local/scalar/scalar" +SCALAR_UNINSTALL_SCRIPT="/usr/local/scalar/uninstall_scalar.sh" + +function cleanupScalar() +{ + echo "checking whether Scalar was installed" + if [ ! -f "$SCALAR_C_CMD" ]; then + echo "Scalar not installed; exiting..." + return 0 + fi + echo "Scalar is installed!" + + echo "looking for Scalar.NET" + if [ ! -f "$SCALAR_DOTNET_CMD" ]; then + echo "Scalar.NET not found; exiting..." + return 0 + fi + echo "Scalar.NET found!" + + currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }') + + # Re-register Scalar.NET repositories with the newly-installed Scalar + for repo in $($SCALAR_DOTNET_CMD list); do + ( + PATH="$INSTALL_DST/git/bin:$PATH" + sudo -u "$currentUser" scalar register $repo || \ + echo "warning: skipping re-registration of $repo" + ) + done + + # Uninstall Scalar.NET + echo "removing Scalar.NET" + + # Add /usr/local/bin to path - default install location of Homebrew + PATH="/usr/local/bin:$PATH" + if (sudo -u "$currentUser" brew list --cask scalar); then + # Remove from Homebrew + sudo -u "$currentUser" brew remove --cask scalar || echo "warning: Scalar.NET uninstall via Homebrew completed with code $?" + echo "Scalar.NET uninstalled via Homebrew!" + elif (sudo -u "$currentUser" brew list --cask scalar-azrepos); then + sudo -u "$currentUser" brew remove --cask scalar-azrepos || echo "warning: Scalar.NET with GVFS uninstall via Homebrew completed with code $?" + echo "Scalar.NET with GVFS uninstalled via Homebrew!" + elif [ -f $SCALAR_UNINSTALL_SCRIPT ]; then + # If not installed with Homebrew, manually remove package + sudo -S sh $SCALAR_UNINSTALL_SCRIPT || echo "warning: Scalar.NET uninstall completed with code $?" + echo "Scalar.NET uninstalled!" + else + echo "warning: Scalar.NET uninstall script not found" + fi + + # Re-create the Scalar symlink, in case it was removed by the Scalar.NET uninstall operation + mkdir -p $INSTALL_DST/bin + /bin/ln -Fs "$SCALAR_C_CMD" "$INSTALL_DST/bin/scalar" +} + +# Run Scalar cleanup (will exit if not applicable) +cleanupScalar + +exit 0 \ No newline at end of file diff --git a/.github/macos-installer/assets/uninstall.sh b/.github/macos-installer/assets/uninstall.sh new file mode 100755 index 00000000000000..4fc79fbaa2e652 --- /dev/null +++ b/.github/macos-installer/assets/uninstall.sh @@ -0,0 +1,34 @@ +#!/bin/bash -e +if [ ! -r "/usr/local/git" ]; then + echo "Git doesn't appear to be installed via this installer. Aborting" + exit 1 +fi + +if [ "$1" != "--yes" ]; then + echo "This will uninstall git by removing /usr/local/git/, and symlinks" + printf "Type 'yes' if you are sure you wish to continue: " + read response +else + response="yes" +fi + +if [ "$response" == "yes" ]; then + # remove all of the symlinks we've created + pkgutil --files com.git.pkg | grep bin | while read f; do + if [ -L /usr/local/$f ]; then + sudo rm /usr/local/$f + fi + done + + # forget receipts. + pkgutil --packages | grep com.git.pkg | xargs -I {} sudo pkgutil --forget {} + echo "Uninstalled" + + # The guts all go here. + sudo rm -rf /usr/local/git/ +else + echo "Aborted" + exit 1 +fi + +exit 0 diff --git a/.github/macos-installer/entitlements.xml b/.github/macos-installer/entitlements.xml new file mode 100644 index 00000000000000..46f675661149b6 --- /dev/null +++ b/.github/macos-installer/entitlements.xml @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/.github/scripts/codesign.sh b/.github/scripts/codesign.sh new file mode 100755 index 00000000000000..076b29f93be45e --- /dev/null +++ b/.github/scripts/codesign.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +sign_directory () { + ( + cd "$1" + for f in * + do + macho=$(file --mime $f | grep mach) + # Runtime sign dylibs and Mach-O binaries + if [[ $f == *.dylib ]] || [ ! -z "$macho" ]; + then + echo "Runtime Signing $f" + codesign -s "$IDENTITY" $f --timestamp --force --options=runtime --entitlements $ENTITLEMENTS_FILE + elif [ -d "$f" ]; + then + echo "Signing files in subdirectory $f" + sign_directory "$f" + + else + echo "Signing $f" + codesign -s "$IDENTITY" $f --timestamp --force + fi + done + ) +} + +for i in "$@" +do +case "$i" in + --payload=*) + SIGN_DIR="${i#*=}" + shift # past argument=value + ;; + --identity=*) + IDENTITY="${i#*=}" + shift # past argument=value + ;; + --entitlements=*) + ENTITLEMENTS_FILE="${i#*=}" + shift # past argument=value + ;; + *) + die "unknown option '$i'" + ;; +esac +done + +if [ -z "$SIGN_DIR" ]; then + echo "error: missing directory argument" + exit 1 +elif [ -z "$IDENTITY" ]; then + echo "error: missing signing identity argument" + exit 1 +elif [ -z "$ENTITLEMENTS_FILE" ]; then + echo "error: missing entitlements file argument" + exit 1 +fi + +echo "======== INPUTS ========" +echo "Directory: $SIGN_DIR" +echo "Signing identity: $IDENTITY" +echo "Entitlements: $ENTITLEMENTS_FILE" +echo "======== END INPUTS ========" + +sign_directory "$SIGN_DIR" diff --git a/.github/scripts/notarize.sh b/.github/scripts/notarize.sh new file mode 100755 index 00000000000000..9315d688afbd49 --- /dev/null +++ b/.github/scripts/notarize.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +for i in "$@" +do +case "$i" in + --package=*) + PACKAGE="${i#*=}" + shift # past argument=value + ;; + --keychain-profile=*) + KEYCHAIN_PROFILE="${i#*=}" + shift # past argument=value + ;; + *) + die "unknown option '$i'" + ;; +esac +done + +if [ -z "$PACKAGE" ]; then + echo "error: missing package argument" + exit 1 +elif [ -z "$KEYCHAIN_PROFILE" ]; then + echo "error: missing keychain profile argument" + exit 1 +fi + +# Exit as soon as any line fails +set -e + +# Send the notarization request +xcrun notarytool submit -v "$PACKAGE" -p "$KEYCHAIN_PROFILE" --wait + +# Staple the notarization ticket (to allow offline installation) +xcrun stapler staple -v "$PACKAGE" diff --git a/.github/scripts/symlink-git-hardlinks.rb b/.github/scripts/symlink-git-hardlinks.rb new file mode 100644 index 00000000000000..174802ccc85d93 --- /dev/null +++ b/.github/scripts/symlink-git-hardlinks.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +install_prefix = ARGV[0] +puts install_prefix +git_binary = File.join(install_prefix, '/usr/local/git/bin/git') + +[ + ['git' , File.join(install_prefix, '/usr/local/git/bin')], + ['../../bin/git', File.join(install_prefix, '/usr/local/git/libexec/git-core')] +].each do |link, path| + Dir.glob(File.join(path, '*')).each do |file| + next if file == git_binary + puts "#{file} #{File.size(file)} == #{File.size(git_binary)}" + next unless File.size(file) == File.size(git_binary) + puts "Symlinking #{file}" + puts `ln -sf #{link} #{file}` + exit $?.exitstatus if $?.exitstatus != 0 + end +end \ No newline at end of file diff --git a/.github/workflows/build-git-installers.yml b/.github/workflows/build-git-installers.yml new file mode 100644 index 00000000000000..74180f14c7073a --- /dev/null +++ b/.github/workflows/build-git-installers.yml @@ -0,0 +1,776 @@ +name: build-git-installers + +on: + push: + tags: + - 'v[0-9]*vfs*' # matches "vvfs" + +permissions: + id-token: write # required for Azure login via OIDC + +jobs: + # Check prerequisites for the workflow + prereqs: + runs-on: ubuntu-latest + environment: release + outputs: + tag_name: ${{ steps.tag.outputs.name }} # The full name of the tag, e.g. v2.32.0.vfs.0.0 + tag_version: ${{ steps.tag.outputs.version }} # The version number (without preceding "v"), e.g. 2.32.0.vfs.0.0 + steps: + - name: Validate tag + run: | + echo "$GITHUB_REF" | + grep -E '^refs/tags/v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.vfs\.0\.(0|[1-9][0-9]*)(\.rc[0-9])?$' || { + echo "::error::${GITHUB_REF#refs/tags/} is not of the form v2...vfs.0.[.rc]" >&2 + exit 1 + } + - name: Determine tag to build + run: | + echo "name=${GITHUB_REF#refs/tags/}" >>$GITHUB_OUTPUT + echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + id: tag + - name: Clone git + uses: actions/checkout@v4 + - name: Validate the tag identified with trigger + run: | + die () { + echo "::error::$*" >&2 + exit 1 + } + + # `actions/checkout` only downloads the peeled tag (i.e. the commit) + git fetch origin +$GITHUB_REF:$GITHUB_REF + + # Verify that the tag is annotated + test $(git cat-file -t "$GITHUB_REF") == "tag" || die "Tag ${{ steps.tag.outputs.name }} is not annotated" + + # Verify tag follows rules in GIT-VERSION-GEN (i.e., matches the specified "DEF_VER" in + # GIT-VERSION-FILE) and matches tag determined from trigger + make GIT-VERSION-FILE + test "${{ steps.tag.outputs.version }}" == "$(sed -n 's/^GIT_VERSION = //p'< GIT-VERSION-FILE)" || die "GIT-VERSION-FILE tag does not match ${{ steps.tag.outputs.name }}" + # End check prerequisites for the workflow + + # Build Windows installers (x86_64 installer & portable) + windows_pkg: + runs-on: windows-2019 + environment: release + needs: prereqs + env: + GPG_OPTIONS: "--batch --yes --no-tty --list-options no-show-photos --verify-options no-show-photos --pinentry-mode loopback" + HOME: "${{github.workspace}}\\home" + USERPROFILE: "${{github.workspace}}\\home" + 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" && + echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >>$GITHUB_ENV + - uses: git-for-windows/setup-git-for-windows-sdk@v1 + with: + flavor: build-installers + - name: Clone build-extra + shell: bash + run: | + git clone --filter=blob:none --single-branch -b main https://github.com/git-for-windows/build-extra /usr/src/build-extra + - name: Clone git + shell: bash + run: | + # Since we cannot directly clone a specified tag (as we would a branch with `git clone -b `), + # this clone has to be done manually (via init->fetch->reset). + + tag_name="${{ needs.prereqs.outputs.tag_name }}" && + git -c init.defaultBranch=main init && + git remote add -f origin https://github.com/git-for-windows/git && + git fetch "https://github.com/${{github.repository}}" refs/tags/${tag_name}:refs/tags/${tag_name} && + git reset --hard ${tag_name} + - 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: | + # This section ensures that the identity for the GPG key matches the git user identity, otherwise + # signing will fail + + 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-x86_64-git + env: + GPGKEY: "${{secrets.GPGKEY}}" + shell: bash + run: | + set -x + + # Make sure that there is a `/usr/bin/git` that can be used by `makepkg-mingw` + printf '#!/bin/sh\n\nexec /mingw64/bin/git.exe "$@"\n' >/usr/bin/git && + + # Restrict `PATH` to MSYS2 and to Visual Studio (to let `cv2pdb` find the relevant DLLs) + PATH="/mingw64/bin:/usr/bin:/C/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64:/C/Windows/system32" + + type -p mspdb140.dll || exit 1 + + sh -x /usr/src/build-extra/please.sh build-mingw-w64-git --only-64-bit --build-src-pkg -o artifacts HEAD && + 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=${{ needs.prereqs.outputs.tag_name }} && + (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: Publish mingw-w64-x86_64-git + uses: actions/upload-artifact@v4 + with: + name: pkg-x86_64 + path: artifacts + windows_artifacts: + runs-on: windows-2019 + environment: release + needs: [prereqs, windows_pkg] + env: + HOME: "${{github.workspace}}\\home" + strategy: + matrix: + artifact: + - name: installer + fileprefix: Git + - name: portable + fileprefix: PortableGit + fail-fast: false + steps: + - name: Download pkg-x86_64 + uses: actions/download-artifact@v4 + with: + name: pkg-x86_64 + path: pkg-x86_64 + - uses: git-for-windows/setup-git-for-windows-sdk@v1 + with: + flavor: build-installers + - name: Clone build-extra + shell: bash + run: | + git clone --filter=blob:none --single-branch -b main https://github.com/git-for-windows/build-extra /usr/src/build-extra + - 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: | + 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: Retarget auto-update to microsoft/git + shell: bash + run: | + set -x + + b=/usr/src/build-extra && + + filename=$b/git-update-git-for-windows.config + tr % '\t' >$filename <<-\EOF && + [update] + %fromFork = microsoft/git + EOF + + sed -i -e '/^#include "file-list.iss"/a\ + Source: {#SourcePath}\\..\\git-update-git-for-windows.config; DestDir: {app}\\mingw64\\bin; Flags: replacesameversion; AfterInstall: DeleteFromVirtualStore' \ + -e '/^Type: dirifempty; Name: {app}\\{#MINGW_BITNESS}$/i\ + Type: files; Name: {app}\\{#MINGW_BITNESS}\\bin\\git-update-git-for-windows.config\ + Type: dirifempty; Name: {app}\\{#MINGW_BITNESS}\\bin' \ + $b/installer/install.iss + - name: Set alerts to continue until upgrade is taken + shell: bash + run: | + set -x + + b=/mingw64/bin && + + sed -i -e '6 a use_recently_seen=no' \ + $b/git-update-git-for-windows + - name: Set the installer Publisher to the Git Fundamentals team + shell: bash + run: | + b=/usr/src/build-extra && + sed -i -e 's/^\(AppPublisher=\).*/\1The Git Fundamentals Team at GitHub/' $b/installer/install.iss + - name: Let the installer configure Visual Studio to use the installed Git + shell: bash + run: | + set -x + + b=/usr/src/build-extra && + + sed -i -e '/^ *InstallAutoUpdater();$/a\ + CustomPostInstall();' \ + -e '/^ *UninstallAutoUpdater();$/a\ + CustomPostUninstall();' \ + $b/installer/install.iss && + + cat >>$b/installer/helpers.inc.iss <<\EOF + + procedure CustomPostInstall(); + begin + if not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\15.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or + not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\16.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or + not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\17.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or + not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\18.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or + not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\19.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) or + not RegWriteStringValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\20.0\TeamFoundation\GitSourceControl','GitPath',ExpandConstant('{app}')) then + LogError('Could not register TeamFoundation\GitSourceControl'); + end; + + procedure CustomPostUninstall(); + begin + if not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\15.0\TeamFoundation\GitSourceControl','GitPath') or + not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\16.0\TeamFoundation\GitSourceControl','GitPath') or + not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\17.0\TeamFoundation\GitSourceControl','GitPath') or + not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\18.0\TeamFoundation\GitSourceControl','GitPath') or + not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\19.0\TeamFoundation\GitSourceControl','GitPath') or + not RegDeleteValue(HKEY_CURRENT_USER,'Software\Microsoft\VSCommon\20.0\TeamFoundation\GitSourceControl','GitPath') then + LogError('Could not register TeamFoundation\GitSourceControl'); + end; + EOF + - name: Enable Scalar/C and the auto-updater in the installer by default + shell: bash + run: | + set -x + + b=/usr/src/build-extra && + + sed -i -e "/ChosenOptions:=''/a\\ + if (ExpandConstant('{param:components|/}')='/') then begin\n\ + WizardSelectComponents('autoupdate');\n\ + #ifdef WITH_SCALAR\n\ + WizardSelectComponents('scalar');\n\ + #endif\n\ + end;" $b/installer/install.iss + - name: Build 64-bit ${{matrix.artifact.name}} + shell: bash + run: | + set -x + + # Copy the PDB archive to the directory where `--include-pdbs` expects it + b=/usr/src/build-extra && + mkdir -p $b/cached-source-packages && + cp pkg-x86_64/*-pdb* $b/cached-source-packages/ && + + # Build the installer, embedding PDBs + eval $b/please.sh make_installers_from_mingw_w64_git --include-pdbs \ + --version=${{ needs.prereqs.outputs.tag_version }} \ + -o artifacts --${{matrix.artifact.name}} \ + --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 && + + 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}}-*.exe | sed "s/.* //" >artifacts/sha-256.txt + - name: Verify that .exe files are code-signed + if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != '' + shell: bash + run: | + PATH=$PATH:"/c/Program Files (x86)/Windows Kits/10/App Certification Kit/" \ + signtool verify //pa artifacts/${{matrix.artifact.fileprefix}}-*.exe + - name: Publish ${{matrix.artifact.name}}-x86_64 + uses: actions/upload-artifact@v4 + with: + name: win-${{matrix.artifact.name}}-x86_64 + path: artifacts + # End build Windows installers + + # Build and sign Mac OSX installers & upload artifacts + create-macos-artifacts: + strategy: + matrix: + arch: + - name: arm64 + runner: macos-latest-xl-arm64 + runs-on: ${{ matrix.arch.runner }} + needs: prereqs + env: + VERSION: "${{ needs.prereqs.outputs.tag_version }}" + environment: release + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + path: 'git' + + - name: Install Git dependencies + run: | + set -ex + + # Install x86_64 packages + arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + arch -x86_64 /usr/local/bin/brew install gettext + + # Install arm64 packages + brew install automake asciidoc xmlto docbook + brew link --force gettext + + # Make universal gettext library + lipo -create -output libintl.a /usr/local/opt/gettext/lib/libintl.a /opt/homebrew/opt/gettext/lib/libintl.a + + - name: Set up signing/notarization infrastructure + env: + A1: ${{ secrets.APPLICATION_CERTIFICATE_BASE64 }} + A2: ${{ secrets.APPLICATION_CERTIFICATE_PASSWORD }} + I1: ${{ secrets.INSTALLER_CERTIFICATE_BASE64 }} + I2: ${{ secrets.INSTALLER_CERTIFICATE_PASSWORD }} + N1: ${{ secrets.APPLE_TEAM_ID }} + N2: ${{ secrets.APPLE_DEVELOPER_ID }} + N3: ${{ secrets.APPLE_DEVELOPER_PASSWORD }} + N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }} + run: | + echo "Setting up signing certificates" + security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain + security default-keychain -s $RUNNER_TEMP/buildagent.keychain + security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain + # Prevent re-locking + security set-keychain-settings $RUNNER_TEMP/buildagent.keychain + + echo "$A1" | base64 -D > $RUNNER_TEMP/cert.p12 + security import $RUNNER_TEMP/cert.p12 \ + -k $RUNNER_TEMP/buildagent.keychain \ + -P "$A2" \ + -T /usr/bin/codesign + security set-key-partition-list \ + -S apple-tool:,apple:,codesign: \ + -s -k pwd \ + $RUNNER_TEMP/buildagent.keychain + + echo "$I1" | base64 -D > $RUNNER_TEMP/cert.p12 + security import $RUNNER_TEMP/cert.p12 \ + -k $RUNNER_TEMP/buildagent.keychain \ + -P "$I2" \ + -T /usr/bin/pkgbuild + security set-key-partition-list \ + -S apple-tool:,apple:,pkgbuild: \ + -s -k pwd \ + $RUNNER_TEMP/buildagent.keychain + + echo "Setting up notarytool" + xcrun notarytool store-credentials \ + --team-id "$N1" \ + --apple-id "$N2" \ + --password "$N3" \ + "$N4" + + - name: Build, sign, and notarize artifacts + env: + A3: ${{ secrets.APPLE_APPLICATION_SIGNING_IDENTITY }} + I3: ${{ secrets.APPLE_INSTALLER_SIGNING_IDENTITY }} + N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }} + run: | + die () { + echo "$*" >&2 + exit 1 + } + + # Trace execution, stop on error + set -ex + + # Write to "version" file to force match with trigger payload version + echo "${{ needs.prereqs.outputs.tag_version }}" >>git/version + + # Configure universal build + cat >git/config.mak <>git/config.mak <>git/config.mak <>git/config.mak + + # To make use of the catalogs... + export XML_CATALOG_FILES=$homebrew_prefix/etc/xml/catalog + + make -C git -j$(sysctl -n hw.physicalcpu) GIT-VERSION-FILE dist dist-doc + + export GIT_BUILT_FROM_COMMIT=$(gunzip -c git/git-$VERSION.tar.gz | git get-tar-commit-id) || + die "Could not determine commit for build" + + # Extract tarballs + mkdir payload manpages + tar -xvf git/git-$VERSION.tar.gz -C payload + tar -xvf git/git-manpages-$VERSION.tar.gz -C manpages + + # Lay out payload + cp git/config.mak payload/git-$VERSION/config.mak + make -C git/.github/macos-installer V=1 payload + + # Codesign payload + cp -R stage/git-universal-$VERSION/ \ + git/.github/macos-installer/build-artifacts + make -C git/.github/macos-installer V=1 codesign \ + APPLE_APP_IDENTITY="$A3" || die "Creating signed payload failed" + + # Build and sign pkg + make -C git/.github/macos-installer V=1 pkg \ + APPLE_INSTALLER_IDENTITY="$I3" \ + || die "Creating signed pkg failed" + + # Notarize pkg + make -C git/.github/macos-installer V=1 notarize \ + APPLE_INSTALLER_IDENTITY="$I3" APPLE_KEYCHAIN_PROFILE="$N4" \ + || die "Creating signed and notarized pkg failed" + + # Create DMG + make -C git/.github/macos-installer V=1 image || die "Creating DMG failed" + + # Move all artifacts into top-level directory + mv git/.github/macos-installer/disk-image/*.pkg git/.github/macos-installer/ + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-artifacts + path: | + git/.github/macos-installer/*.dmg + git/.github/macos-installer/*.pkg + # End build and sign Mac OSX installers + + # Build and sign Debian package + create-linux-artifacts: + runs-on: ubuntu-latest + needs: prereqs + environment: release + steps: + - name: Install git dependencies + run: | + set -ex + sudo apt-get update -q + sudo apt-get install -y -q --no-install-recommends gettext libcurl4-gnutls-dev libpcre3-dev asciidoc xmlto + + - name: Clone git + uses: actions/checkout@v4 + with: + path: git + + - name: Build and create Debian package + run: | + set -ex + + die () { + echo "$*" >&2 + exit 1 + } + + echo "${{ needs.prereqs.outputs.tag_version }}" >>git/version + make -C git GIT-VERSION-FILE + + VERSION="${{ needs.prereqs.outputs.tag_version }}" + + ARCH="$(dpkg-architecture -q DEB_HOST_ARCH)" + if test -z "$ARCH"; then + die "Could not determine host architecture!" + fi + + PKGNAME="microsoft-git_$VERSION" + PKGDIR="$(dirname $(pwd))/$PKGNAME" + + rm -rf "$PKGDIR" + mkdir -p "$PKGDIR" + + DESTDIR="$PKGDIR" make -C git -j5 V=1 DEVELOPER=1 \ + USE_LIBPCRE=1 \ + NO_CROSS_DIRECTORY_HARDLINKS=1 \ + ASCIIDOC8=1 ASCIIDOC_NO_ROFF=1 \ + ASCIIDOC='TZ=UTC asciidoc' \ + prefix=/usr/local \ + gitexecdir=/usr/local/lib/git-core \ + libexecdir=/usr/local/lib/git-core \ + htmldir=/usr/local/share/doc/git/html \ + install install-doc install-html + + cd .. + mkdir "$PKGNAME/DEBIAN" + + # Based on https://packages.ubuntu.com/xenial/vcs/git + cat >"$PKGNAME/DEBIAN/control" < + Description: Git client built from the https://github.com/microsoft/git repository, + specialized in supporting monorepo scenarios. Includes the Scalar CLI. + EOF + + dpkg-deb -Zxz --build "$PKGNAME" + # Move Debian package for later artifact upload + mv "$PKGNAME.deb" "$GITHUB_WORKSPACE" + + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Prepare for GPG signing + env: + AZURE_VAULT: ${{ secrets.AZURE_VAULT }} + GPG_KEY_SECRET_NAME: ${{ secrets.GPG_KEY_SECRET_NAME }} + GPG_PASSPHRASE_SECRET_NAME: ${{ secrets.GPG_PASSPHRASE_SECRET_NAME }} + GPG_KEYGRIP_SECRET_NAME: ${{ secrets.GPG_KEYGRIP_SECRET_NAME }} + run: | + # Install debsigs + sudo apt install debsigs + + # Download GPG key, passphrase, and keygrip from Azure Key Vault + key=$(az keyvault secret show --name $GPG_KEY_SECRET_NAME --vault-name $AZURE_VAULT --query "value") + passphrase=$(az keyvault secret show --name $GPG_PASSPHRASE_SECRET_NAME --vault-name $AZURE_VAULT --query "value") + keygrip=$(az keyvault secret show --name $GPG_KEYGRIP_SECRET_NAME --vault-name $AZURE_VAULT --query "value") + + # Remove quotes from downloaded values + key=$(sed -e 's/^"//' -e 's/"$//' <<<"$key") + passphrase=$(sed -e 's/^"//' -e 's/"$//' <<<"$passphrase") + keygrip=$(sed -e 's/^"//' -e 's/"$//' <<<"$keygrip") + + # Import GPG key + echo "$key" | base64 -d | gpg --import --no-tty --batch --yes + + # Configure GPG + echo "allow-preset-passphrase" > ~/.gnupg/gpg-agent.conf + gpg-connect-agent RELOADAGENT /bye + /usr/lib/gnupg2/gpg-preset-passphrase --preset "$keygrip" <<<"$passphrase" + + - name: Sign Debian package + run: | + # Sign Debian package + version="${{ needs.prereqs.outputs.tag_version }}" + debsigs --sign=origin --verify --check microsoft-git_"$version".deb + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-artifacts + path: | + *.deb + # End build and sign Debian package + + # Validate installers + validate-installers: + name: Validate installers + strategy: + matrix: + component: + - os: ubuntu-latest + artifact: linux-artifacts + command: git + - os: macos-latest-xl-arm64 + artifact: macos-artifacts + command: git + - os: macos-latest + artifact: macos-artifacts + command: git + - os: windows-latest + artifact: win-installer-x86_64 + command: $PROGRAMFILES\Git\cmd\git.exe + runs-on: ${{ matrix.component.os }} + needs: [prereqs, windows_artifacts, create-macos-artifacts, create-linux-artifacts] + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ matrix.component.artifact }} + + - name: Install Windows + if: contains(matrix.component.os, 'windows') + shell: pwsh + run: | + $exePath = Get-ChildItem -Path ./*.exe | %{$_.FullName} + Start-Process -Wait -FilePath "$exePath" -ArgumentList "/SILENT /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /ALLOWDOWNGRADE=1" + + - name: Install Linux + if: contains(matrix.component.os, 'ubuntu') + run: | + debpath=$(find ./*.deb) + sudo apt install $debpath + + - name: Install macOS + if: contains(matrix.component.os, 'macos') + run: | + # avoid letting Homebrew's `git` in `/opt/homebrew/bin` override `/usr/local/bin/git` + arch="$(uname -m)" + test arm64 != "$arch" || + brew uninstall git + + pkgpath=$(find ./*universal*.pkg) + sudo installer -pkg $pkgpath -target / + + - name: Validate + shell: bash + run: | + "${{ matrix.component.command }}" --version | sed 's/git version //' >actual + echo ${{ needs.prereqs.outputs.tag_version }} >expect + cmp expect actual || exit 1 + + - name: Validate universal binary CPU architecture + if: contains(matrix.component.os, 'macos') + shell: bash + run: | + set -ex + git version --build-options >actual + cat actual + grep "cpu: $(uname -m)" actual + # End validate installers + + create-github-release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write # required for Azure login via OIDC + needs: + - validate-installers + - create-linux-artifacts + - create-macos-artifacts + - windows_artifacts + - prereqs + env: + AZURE_VAULT: ${{ secrets.AZURE_VAULT }} + GPG_PUBLIC_KEY_SECRET_NAME: ${{ secrets.GPG_PUBLIC_KEY_SECRET_NAME }} + environment: release + if: | + success() || + (needs.create-linux-artifacts.result == 'skipped' && + needs.create-macos-artifacts.result == 'success' && + needs.windows_artifacts.result == 'success') + steps: + - name: Download Windows portable installer + uses: actions/download-artifact@v4 + with: + name: win-portable-x86_64 + path: win-portable-x86_64 + + - name: Download Windows x86_64 installer + uses: actions/download-artifact@v4 + with: + name: win-installer-x86_64 + path: win-installer-x86_64 + + - name: Download macOS artifacts + uses: actions/download-artifact@v4 + with: + name: macos-artifacts + path: macos-artifacts + + - name: Download Debian package + uses: actions/download-artifact@v4 + with: + name: linux-artifacts + path: deb-package + + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Download GPG public key signature file + run: | + az keyvault secret show --name "$GPG_PUBLIC_KEY_SECRET_NAME" \ + --vault-name "$AZURE_VAULT" --query "value" \ + | sed -e 's/^"//' -e 's/"$//' | base64 -d >msft-git-public.asc + mv msft-git-public.asc deb-package + + - uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + var releaseMetadata = { + owner: context.repo.owner, + repo: context.repo.repo + }; + + // Create the release + var tagName = "${{ needs.prereqs.outputs.tag_name }}"; + var createdRelease = await github.rest.repos.createRelease({ + ...releaseMetadata, + draft: true, + tag_name: tagName, + name: tagName + }); + releaseMetadata.release_id = createdRelease.data.id; + + // Uploads contents of directory to the release created above + async function uploadDirectoryToRelease(directory, includeExtensions=[]) { + return fs.promises.readdir(directory) + .then(async(files) => Promise.all( + files.filter(file => { + return includeExtensions.length==0 || includeExtensions.includes(path.extname(file).toLowerCase()); + }) + .map(async (file) => { + var filePath = path.join(directory, file); + github.rest.repos.uploadReleaseAsset({ + ...releaseMetadata, + name: file, + headers: { + "content-length": (await fs.promises.stat(filePath)).size + }, + data: fs.createReadStream(filePath) + }); + })) + ); + } + + await Promise.all([ + // Upload Windows artifacts + uploadDirectoryToRelease('win-installer-x86_64', ['.exe']), + uploadDirectoryToRelease('win-portable-x86_64', ['.exe']), + + // Upload Mac artifacts + uploadDirectoryToRelease('macos-artifacts'), + + // Upload Ubuntu artifacts + uploadDirectoryToRelease('deb-package') + ]); diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82d45f4f42c351..ae50f34bfd65bc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -164,7 +164,7 @@ jobs: vs-build: name: win+VS build needs: ci-config - if: github.event.repository.owner.login == 'git-for-windows' && needs.ci-config.outputs.enabled == 'yes' + if: github.event.repository.owner.login == 'microsoft' && needs.ci-config.outputs.enabled == 'yes' env: NO_PERL: 1 GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'" @@ -342,8 +342,8 @@ jobs: image: alpine distro: alpine-latest - jobname: linux32 - image: daald/ubuntu32:xenial - distro: ubuntu32-16.04 + image: i386/ubuntu:focal + distro: ubuntu32-20.04 - jobname: pedantic image: fedora distro: fedora-latest @@ -353,17 +353,17 @@ jobs: runs-on: ubuntu-latest container: ${{matrix.vector.image}} steps: - - uses: actions/checkout@v4 - if: matrix.vector.jobname != 'linux32' - - uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container + - name: prepare libc6 for actions if: matrix.vector.jobname == 'linux32' + run: apt -q update && apt -q -y install libc6-amd64 lib64stdc++6 + - uses: actions/checkout@v4 - run: ci/install-dependencies.sh - run: ci/run-build-and-tests.sh - name: print test failures if: failure() && env.FAILED_TEST_ARTIFACTS != '' run: ci/print-test-failures.sh - name: Upload failed tests' directories - if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname != 'linux32' + if: failure() && env.FAILED_TEST_ARTIFACTS != '' uses: actions/upload-artifact@v4 with: name: failed-tests-${{matrix.vector.jobname}} diff --git a/.github/workflows/monitor-components.yml b/.github/workflows/monitor-components.yml deleted file mode 100644 index fedc6add69a9e0..00000000000000 --- a/.github/workflows/monitor-components.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Monitor component updates - -# Git for Windows is a slightly modified subset of MSYS2. Some of its -# components are maintained by Git for Windows, others by MSYS2. To help -# keeping the former up to date, this workflow monitors the Atom/RSS feeds -# and opens new tickets for each new component version. - -on: - schedule: - - cron: "23 8,11,14,17 * * *" - workflow_dispatch: - -env: - CHARACTER_LIMIT: 5000 - MAX_AGE: 7d - -jobs: - job: - # Only run this in Git for Windows' fork - if: github.event.repository.owner.login == 'git-for-windows' - runs-on: ubuntu-latest - permissions: - issues: write - strategy: - matrix: - component: - - label: git - feed: https://github.com/git/git/tags.atom - - label: git-lfs - feed: https://github.com/git-lfs/git-lfs/tags.atom - - label: git-credential-manager - feed: https://github.com/git-ecosystem/git-credential-manager/tags.atom - - label: tig - feed: https://github.com/jonas/tig/tags.atom - - label: cygwin - feed: https://github.com/cygwin/cygwin/releases.atom - title-pattern: ^(?!.*newlib) - - label: msys2-runtime-package - feed: https://github.com/msys2/MSYS2-packages/commits/master/msys2-runtime.atom - - label: msys2-runtime - feed: https://github.com/msys2/msys2-runtime/commits/HEAD.atom - aggregate: true - - label: openssh - feed: https://github.com/openssh/openssh-portable/tags.atom - - label: libfido2 - feed: https://github.com/Yubico/libfido2/tags.atom - - label: libcbor - feed: https://github.com/PJK/libcbor/tags.atom - - label: openssl - feed: https://github.com/openssl/openssl/tags.atom - title-pattern: ^(?!.*alpha) - - label: gnutls - feed: https://gnutls.org/news.atom - - label: heimdal - feed: https://github.com/heimdal/heimdal/tags.atom - - label: git-sizer - feed: https://github.com/github/git-sizer/tags.atom - - label: gitflow - feed: https://github.com/petervanderdoes/gitflow-avh/tags.atom - - label: curl - feed: https://github.com/curl/curl/tags.atom - - label: libgpg-error - feed: https://github.com/gpg/libgpg-error/releases.atom - title-pattern: ^libgpg-error-[0-9\.]*$ - - label: libgcrypt - feed: https://github.com/gpg/libgcrypt/releases.atom - title-pattern: ^libgcrypt-[0-9\.]*$ - - label: gpg - feed: https://github.com/gpg/gnupg/releases.atom - - label: mintty - feed: https://github.com/mintty/mintty/releases.atom - - label: 7-zip - feed: https://sourceforge.net/projects/sevenzip/rss?path=/7-Zip - aggregate: true - - label: bash - feed: https://git.savannah.gnu.org/cgit/bash.git/atom/?h=master - aggregate: true - - label: perl - feed: https://github.com/Perl/perl5/tags.atom - title-pattern: ^(?!.*(5\.[0-9]+[13579]|RC)) - - label: pcre2 - feed: https://github.com/PCRE2Project/pcre2/tags.atom - - label: mingw-w64-llvm - feed: https://github.com/msys2/MINGW-packages/commits/master/mingw-w64-llvm.atom - - label: innosetup - feed: https://github.com/jrsoftware/issrc/tags.atom - fail-fast: false - steps: - - uses: git-for-windows/rss-to-issues@v0 - with: - feed: ${{matrix.component.feed}} - prefix: "[New ${{matrix.component.label}} version]" - labels: component-update - github-token: ${{ secrets.GITHUB_TOKEN }} - character-limit: ${{ env.CHARACTER_LIMIT }} - max-age: ${{ env.MAX_AGE }} - aggregate: ${{matrix.component.aggregate}} - title-pattern: ${{matrix.component.title-pattern}} diff --git a/.github/workflows/release-homebrew.yml b/.github/workflows/release-homebrew.yml new file mode 100644 index 00000000000000..e2a2634ff60c97 --- /dev/null +++ b/.github/workflows/release-homebrew.yml @@ -0,0 +1,31 @@ +name: Update Homebrew Tap +on: + release: + types: [released] + +jobs: + release: + runs-on: ubuntu-latest + environment: release + steps: + - id: version + name: Compute version number + run: | + echo "result=$(echo $GITHUB_REF | sed -e "s/^refs\/tags\/v//")" >>$GITHUB_OUTPUT + - id: hash + name: Compute release asset hash + uses: mjcheetham/asset-hash@v1.1 + with: + asset: /git-(.*)\.pkg/ + hash: sha256 + token: ${{ secrets.GITHUB_TOKEN }} + - name: Update scalar Cask + uses: mjcheetham/update-homebrew@v1.3 + with: + token: ${{ secrets.HOMEBREW_TOKEN }} + tap: microsoft/git + name: microsoft-git + type: cask + version: ${{ steps.version.outputs.result }} + sha256: ${{ steps.hash.outputs.result }} + alwaysUsePullRequest: false diff --git a/.github/workflows/release-winget.yml b/.github/workflows/release-winget.yml new file mode 100644 index 00000000000000..61010a5ce65abb --- /dev/null +++ b/.github/workflows/release-winget.yml @@ -0,0 +1,41 @@ +name: "release-winget" +on: + release: + types: [released] + + workflow_dispatch: + inputs: + release: + description: 'Release Id' + required: true + default: 'latest' + +jobs: + release: + runs-on: windows-latest + environment: release + steps: + - name: Publish manifest with winget-create + run: | + # Get correct release asset + $github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json + $asset = $github.release.assets | Where-Object -Property name -match '64-bit.exe$' + + # Remove 'v' and 'vfs' from the version + $github.release.tag_name -match '\d.*' + $version = $Matches[0] -replace ".vfs","" + + # Download wingetcreate and create manifests + Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe + .\wingetcreate.exe update Microsoft.Git -u $asset.browser_download_url -v $version -o manifests + + # Manually substitute the name of the default branch in the License + # and Copyright URLs since the tooling cannot do that for us. + $shortenedVersion = $version -replace ".{4}$" + $manifestPath = dir -Path ./manifests -Filter Microsoft.Git.locale.en-US.yaml -Recurse | %{$_.FullName} + sed -i "s/vfs-[.0-9]*/vfs-$shortenedVersion/g" "$manifestPath" + + # Submit manifests + $manifestDirectory = Split-Path "$manifestPath" + .\wingetcreate.exe submit -t "${{ secrets.WINGET_TOKEN }}" $manifestDirectory + shell: powershell diff --git a/.github/workflows/scalar-functional-tests.yml b/.github/workflows/scalar-functional-tests.yml new file mode 100644 index 00000000000000..da9f3a0d14bb2f --- /dev/null +++ b/.github/workflows/scalar-functional-tests.yml @@ -0,0 +1,220 @@ +name: Scalar Functional Tests + +env: + SCALAR_REPOSITORY: microsoft/scalar + SCALAR_REF: main + DEBUG_WITH_TMATE: false + SCALAR_TEST_SKIP_VSTS_INFO: true + +on: + push: + branches: [ vfs-*, tentative/vfs-* ] + pull_request: + branches: [ vfs-*, features/* ] + +jobs: + scalar: + name: "Scalar Functional Tests" + + strategy: + fail-fast: false + matrix: + # Order by runtime (in descending order) + os: [windows-2019, macos-13, ubuntu-20.04, ubuntu-22.04] + # Scalar.NET used to be tested using `features: [false, experimental]` + # But currently, Scalar/C ignores `feature.scalar` altogether, so let's + # save some electrons and run only one of them... + features: [ignored] + exclude: + # The built-in FSMonitor is not (yet) supported on Linux + - os: ubuntu-20.04 + features: experimental + - os: ubuntu-22.04 + features: experimental + runs-on: ${{ matrix.os }} + + env: + BUILD_FRAGMENT: bin/Release/netcoreapp3.1 + GIT_FORCE_UNTRACKED_CACHE: 1 + + steps: + - name: Check out Git's source code + uses: actions/checkout@v3 + + - name: Setup build tools on Windows + if: runner.os == 'Windows' + uses: git-for-windows/setup-git-for-windows-sdk@v1 + + - name: Provide a minimal `install` on Windows + if: runner.os == 'Windows' + shell: bash + run: | + test -x /usr/bin/install || + tr % '\t' >/usr/bin/install <<-\EOF + #!/bin/sh + + cmd=cp + while test $# != 0 + do + %case "$1" in + %-d) cmd="mkdir -p";; + %-m) shift;; # ignore mode + %*) break;; + %esac + %shift + done + + exec $cmd "$@" + EOF + + - name: Install build dependencies for Git (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev gettext + + - name: Build and install Git + shell: bash + env: + NO_TCLTK: Yup + run: | + # We do require a VFS version + def_ver="$(sed -n 's/DEF_VER=\(.*vfs.*\)/\1/p' GIT-VERSION-GEN)" + test -n "$def_ver" + + # Ensure that `git version` reflects DEF_VER + case "$(git describe --match "v[0-9]*vfs*" HEAD)" in + ${def_ver%%.vfs.*}.vfs.*) ;; # okay, we can use this + *) git -c user.name=ci -c user.email=ci@github tag -m for-testing ${def_ver}.NNN.g$(git rev-parse --short HEAD);; + esac + + SUDO= + extra= + case "${{ runner.os }}" in + Windows) + extra=DESTDIR=/c/Progra~1/Git + cygpath -aw "/c/Program Files/Git/cmd" >>$GITHUB_PATH + ;; + Linux) + SUDO=sudo + extra=prefix=/usr + ;; + macOS) + SUDO=sudo + extra=prefix=/usr/local + ;; + esac + + $SUDO make -j5 $extra install + + - name: Ensure that we use the built Git and Scalar + shell: bash + run: | + type -p git + git version + case "$(git version)" in *.vfs.*) echo Good;; *) exit 1;; esac + type -p scalar + scalar version + case "$(scalar version 2>&1)" in *.vfs.*) echo Good;; *) exit 1;; esac + + - name: Check out Scalar's source code + uses: actions/checkout@v3 + with: + fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works. + path: scalar + repository: ${{ env.SCALAR_REPOSITORY }} + ref: ${{ env.SCALAR_REF }} + + - name: Setup .NET Core + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '3.1.x' + + - name: Install dependencies + run: dotnet restore + working-directory: scalar + env: + DOTNET_NOLOGO: 1 + + - name: Build + working-directory: scalar + run: dotnet build --configuration Release --no-restore -p:UseAppHost=true # Force generation of executable on macOS. + + - name: Setup platform (Linux) + if: runner.os == 'Linux' + run: | + echo "BUILD_PLATFORM=${{ runner.os }}" >>$GITHUB_ENV + echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$GITHUB_ENV + + - name: Setup platform (Mac) + if: runner.os == 'macOS' + run: | + echo 'BUILD_PLATFORM=Mac' >>$GITHUB_ENV + echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$GITHUB_ENV + + - name: Setup platform (Windows) + if: runner.os == 'Windows' + run: | + echo "BUILD_PLATFORM=${{ runner.os }}" >>$env:GITHUB_ENV + echo 'BUILD_FILE_EXT=.exe' >>$env:GITHUB_ENV + echo "TRACE2_BASENAME=Trace2.${{ github.run_id }}__${{ github.run_number }}__${{ matrix.os }}__${{ matrix.features }}" >>$env:GITHUB_ENV + + - name: Configure feature.scalar + run: git config --global feature.scalar ${{ matrix.features }} + + - id: functional_test + name: Functional test + timeout-minutes: 60 + working-directory: scalar + shell: bash + run: | + export GIT_TRACE2_EVENT="$PWD/$TRACE2_BASENAME/Event" + export GIT_TRACE2_PERF="$PWD/$TRACE2_BASENAME/Perf" + export GIT_TRACE2_EVENT_BRIEF=true + export GIT_TRACE2_PERF_BRIEF=true + mkdir -p "$TRACE2_BASENAME" + mkdir -p "$TRACE2_BASENAME/Event" + mkdir -p "$TRACE2_BASENAME/Perf" + git version --build-options + cd ../out + Scalar.FunctionalTests/$BUILD_FRAGMENT/Scalar.FunctionalTests$BUILD_FILE_EXT --test-scalar-on-path --test-git-on-path --timeout=300000 --full-suite + + - name: Force-stop FSMonitor daemons and Git processes (Windows) + if: runner.os == 'Windows' && (success() || failure()) + shell: bash + run: | + set -x + wmic process get CommandLine,ExecutablePath,HandleCount,Name,ParentProcessID,ProcessID + wmic process where "CommandLine Like '%fsmonitor--daemon %run'" delete + wmic process where "ExecutablePath Like '%git.exe'" delete + + - id: trace2_zip_unix + if: runner.os != 'Windows' && ( success() || failure() ) && ( steps.functional_test.conclusion == 'success' || steps.functional_test.conclusion == 'failure' ) + name: Zip Trace2 Logs (Unix) + shell: bash + working-directory: scalar + run: zip -q -r $TRACE2_BASENAME.zip $TRACE2_BASENAME/ + + - id: trace2_zip_windows + if: runner.os == 'Windows' && ( success() || failure() ) && ( steps.functional_test.conclusion == 'success' || steps.functional_test.conclusion == 'failure' ) + name: Zip Trace2 Logs (Windows) + working-directory: scalar + run: Compress-Archive -DestinationPath ${{ env.TRACE2_BASENAME }}.zip -Path ${{ env.TRACE2_BASENAME }} + + - name: Archive Trace2 Logs + if: ( success() || failure() ) && ( steps.trace2_zip_unix.conclusion == 'success' || steps.trace2_zip_windows.conclusion == 'success' ) + uses: actions/upload-artifact@v3 + with: + name: ${{ env.TRACE2_BASENAME }}.zip + path: scalar/${{ env.TRACE2_BASENAME }}.zip + retention-days: 3 + + # The GitHub Action `action-tmate` allows developers to connect to the running agent + # using SSH (it will be a `tmux` session; on Windows agents it will be inside the MSYS2 + # environment in `C:\msys64`, therefore it can be slightly tricky to interact with + # Git for Windows, which runs a slightly incompatible MSYS2 runtime). + - name: action-tmate + if: env.DEBUG_WITH_TMATE == 'true' && failure() + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true diff --git a/.gitignore b/.gitignore index bf97276163b19b..e1b01493765301 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /fuzz_corpora /GIT-BUILD-DIR /GIT-BUILD-OPTIONS +/GIT-BUILT-FROM-COMMIT /GIT-CFLAGS /GIT-LDFLAGS /GIT-PREFIX @@ -73,6 +74,7 @@ /git-gc /git-get-tar-commit-id /git-grep +/git-gvfs-helper /git-hash-object /git-help /git-hook @@ -164,6 +166,7 @@ /git-submodule /git-submodule--helper /git-subtree +/git-survey /git-svn /git-switch /git-symbolic-ref @@ -171,6 +174,7 @@ /git-unpack-file /git-unpack-objects /git-update-index +/git-update-microsoft-git /git-update-ref /git-update-server-info /git-upload-archive diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37b991e08079fc..c4c45dad2f956f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,9 @@ test:linux: fi parallel: matrix: + - jobname: linux-old + image: ubuntu:16.04 + CC: gcc - jobname: linux-sha256 image: ubuntu:latest CC: clang diff --git a/BRANCHES.md b/BRANCHES.md new file mode 100644 index 00000000000000..364158375e7d55 --- /dev/null +++ b/BRANCHES.md @@ -0,0 +1,59 @@ +Branches used in this repo +========================== + +The document explains the branching structure that we are using in the VFSForGit repository as well as the forking strategy that we have adopted for contributing. + +Repo Branches +------------- + +1. `vfs-#` + + These branches are used to track the specific version that match Git for Windows with the VFSForGit specific patches on top. When a new version of Git for Windows is released, the VFSForGit patches will be rebased on that windows version and a new gvfs-# branch created to create pull requests against. + + #### Examples + + ``` + vfs-2.27.0 + vfs-2.30.0 + ``` + + The versions of git for VFSForGit are based on the Git for Windows versions. v2.20.0.vfs.1 will correspond with the v2.20.0.windows.1 with the VFSForGit specific patches applied to the windows version. + +2. `vfs-#-exp` + + These branches are for releasing experimental features to early adopters. They + should contain everything within the corresponding `vfs-#` branch; if the base + branch updates, then merge into the `vfs-#-exp` branch as well. + +Tags +---- + +We are using annotated tags to build the version number for git. The build will look back through the commit history to find the first tag matching `v[0-9]*vfs*` and build the git version number using that tag. + +Full releases are of the form `v2.XX.Y.vfs.Z.W` where `v2.XX.Y` comes from the +upstream version and `Z.W` are custom updates within our fork. Specifically, +the `.Z` value represents the "compatibility level" with VFS for Git. Only +increase this version when making a breaking change with a released version +of VFS for Git. The `.W` version is used for minor updates between major +versions. + +Experimental releases are of the form `v2.XX.Y.vfs.Z.W.exp`. The `.exp` +suffix indicates that experimental features are available. The rest of the +version string comes from the full release tag. These versions will only +be made available as pre-releases on the releases page, never a full release. + +Forking +------- + +A personal fork of this repository and a branch in that repository should be used for development. + +These branches should be based on the latest vfs-# branch. If there are work in progress pull requests that you have based on a previous version branch when a new version branch is created, you will need to move your patches to the new branch to get them in that latest version. + +#### Example + +``` +git clone +git remote add ms https://github.com/Microsoft/git.git +git checkout -b my-changes ms/vfs-2.20.0 --no-track +git push -fu origin HEAD +``` diff --git a/Documentation/RelNotes/2.46.2.txt b/Documentation/RelNotes/2.46.2.txt new file mode 100644 index 00000000000000..613386878d44c9 --- /dev/null +++ b/Documentation/RelNotes/2.46.2.txt @@ -0,0 +1,23 @@ +Git 2.46.2 Release Notes +======================== + +This release is primarily to merge changes to unbreak the 32-bit +GitHub actions jobs we use for CI testing, so that we can release +real fixes for the 2.46.x track after they pass CI. + +It also reverts the "git patch-id" change that went into 2.46.1, +as it seems to have got a regression reported (I haven't verified, +but it is better to keep a known breakage than adding an unintended +regression). + +Other than that, a handful of minor bugfixes are included. + + * In a few corner cases "git diff --exit-code" failed to report + "changes" (e.g., renamed without any content change), which has + been corrected. + + * Cygwin does have /dev/tty support that is needed by things like + single-key input mode. + + * The interpret-trailers command failed to recognise the end of the + message when the commit log ends in an incomplete line. diff --git a/Documentation/config.txt b/Documentation/config.txt index fedfaf30cd0d8b..b87cb7a593b368 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -448,6 +448,8 @@ include::config/gui.txt[] include::config/guitool.txt[] +include::config/gvfs.txt[] + include::config/help.txt[] include::config/http.txt[] @@ -536,6 +538,8 @@ include::config/status.txt[] include::config/submodule.txt[] +include::config/survey.txt[] + include::config/tag.txt[] include::config/tar.txt[] diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index 458a3f4f7f5d80..2ac35ae97d24d3 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -111,6 +111,14 @@ Version 2 uses an opaque string so that the monitor can return something that can be used to determine what files have changed without race conditions. +core.virtualFilesystem:: + If set, the value of this variable is used as a command which + will identify all files and directories that are present in + the working directory. Git will only track and update files + listed in the virtual file system. Using the virtual file system + will supersede the sparse-checkout settings which will be ignored. + See the "virtual file system" section of linkgit:githooks[5]. + core.trustctime:: If false, the ctime differences between the index and the working tree are ignored; useful when the inode change time @@ -743,6 +751,55 @@ core.multiPackIndex:: single index. See linkgit:git-multi-pack-index[1] for more information. Defaults to true. +core.gvfs:: + Enable the features needed for GVFS. This value can be set to true + to indicate all features should be turned on or the bit values listed + below can be used to turn on specific features. ++ +-- + GVFS_SKIP_SHA_ON_INDEX:: + Bit value 1 + Disables the calculation of the sha when writing the index + GVFS_MISSING_OK:: + Bit value 4 + Normally git write-tree ensures that the objects referenced by the + directory exist in the object database. This option disables this check. + GVFS_NO_DELETE_OUTSIDE_SPARSECHECKOUT:: + Bit value 8 + When marking entries to remove from the index and the working + directory this option will take into account what the + skip-worktree bit was set to so that if the entry has the + skip-worktree bit set it will not be removed from the working + directory. This will allow virtualized working directories to + detect the change to HEAD and use the new commit tree to show + the files that are in the working directory. + GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK:: + Bit value 16 + While performing a fetch with a virtual file system we know + that there will be missing objects and we don't want to download + them just because of the reachability of the commits. We also + don't want to download a pack file with commits, trees, and blobs + since these will be downloaded on demand. This flag will skip the + checks on the reachability of objects during a fetch as well as + the upload pack so that extraneous objects don't get downloaded. + GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS:: + Bit value 64 + With a virtual file system we only know the file size before any + CRLF or smudge/clean filters processing is done on the client. + To prevent file corruption due to truncation or expansion with + garbage at the end, these filters must not run when the file + is first accessed and brought down to the client. Git.exe can't + currently tell the first access vs subsequent accesses so this + flag just blocks them from occurring at all. + GVFS_PREFETCH_DURING_FETCH:: + Bit value 128 + While performing a `git fetch` command, use the gvfs-helper to + perform a "prefetch" of commits and trees. +-- + +core.useGvfsHelper:: + TODO + core.sparseCheckout:: Enable "sparse checkout" feature. See linkgit:git-sparse-checkout[1] for more information. @@ -777,3 +834,12 @@ core.WSLCompat:: The default value is false. When set to true, Git will set the mode bits of the file in the way of wsl, so that the executable flag of files can be set or read correctly. + +core.configWriteLockTimeoutMS:: + When processes try to write to the config concurrently, it is likely + that one process "wins" and the other process(es) fail to lock the + config file. By configuring a timeout larger than zero, Git can be + told to try to lock the config again a couple times within the + specified timeout. If the timeout is configure to zero (which is the + default), Git will fail immediately when the config is already + locked. diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt index 0221c3e620da89..470482ff4c2a38 100644 --- a/Documentation/config/credential.txt +++ b/Documentation/config/credential.txt @@ -9,6 +9,14 @@ credential.helper:: Note that multiple helpers may be defined. See linkgit:gitcredentials[7] for details and examples. +credential.interactive:: + By default, Git and any configured credential helpers will ask for + user input when new credentials are required. Many of these helpers + will succeed based on stored credentials if those credentials are + still valid. To avoid the possibility of user interactivity from + Git, set `credential.interactive=false`. Some credential helpers + respect this option as well. + credential.useHttpPath:: When acquiring credentials, consider the "path" component of an http or https URL to be important. Defaults to false. See diff --git a/Documentation/config/gvfs.txt b/Documentation/config/gvfs.txt new file mode 100644 index 00000000000000..7224939ac0b270 --- /dev/null +++ b/Documentation/config/gvfs.txt @@ -0,0 +1,10 @@ +gvfs.cache-server:: + TODO + +gvfs.sharedcache:: + TODO + +gvfs.fallback:: + If set to `false`, then never fallback to the origin server when the cache + server fails to connect. This will alert users to failures with the cache + server, but avoid causing throttling on the origin server. diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt index 3eff42036033ea..0d6d05b70ce03d 100644 --- a/Documentation/config/index.txt +++ b/Documentation/config/index.txt @@ -1,3 +1,9 @@ +index.deleteSparseDirectories:: + When enabled, the cone mode sparse-checkout feature will delete + directories that are outside of the sparse-checkout cone, unless + such a directory contains an untracked, non-ignored file. Defaults + to true. + index.recordEndOfIndexEntries:: Specifies whether the index file should include an "End Of Index Entry" section. This reduces index load time on multiprocessor diff --git a/Documentation/config/remote.txt b/Documentation/config/remote.txt index 8efc53e836d20b..36e771556c67aa 100644 --- a/Documentation/config/remote.txt +++ b/Documentation/config/remote.txt @@ -42,14 +42,15 @@ remote..mirror:: as if the `--mirror` option was given on the command line. remote..skipDefaultUpdate:: - If true, this remote will be skipped by default when updating - using linkgit:git-fetch[1] or the `update` subcommand of - linkgit:git-remote[1]. + A deprecated synonym to `remote..skipFetchAll` (if + both are set in the configuration files with different + values, the value of the last occurrence will be used). remote..skipFetchAll:: - If true, this remote will be skipped by default when updating - using linkgit:git-fetch[1] or the `update` subcommand of - linkgit:git-remote[1]. + If true, this remote will be skipped when updating + using linkgit:git-fetch[1], the `update` subcommand of + linkgit:git-remote[1], and ignored by the prefetch task + of `git maitenance`. remote..receivepack:: The default program to execute on the remote side when pushing. See diff --git a/Documentation/config/status.txt b/Documentation/config/status.txt index 8caf90f51c19a3..4d863fdaaec2eb 100644 --- a/Documentation/config/status.txt +++ b/Documentation/config/status.txt @@ -77,3 +77,25 @@ status.submoduleSummary:: the --ignore-submodules=dirty command-line option or the 'git submodule summary' command, which shows a similar output but does not honor these settings. + +status.deserializePath:: + EXPERIMENTAL, Pathname to a file containing cached status results + generated by `--serialize`. This will be overridden by + `--deserialize=` on the command line. If the cache file is + invalid or stale, git will fall-back and compute status normally. + +status.deserializeWait:: + EXPERIMENTAL, Specifies what `git status --deserialize` should do + if the serialization cache file is stale and whether it should + fall-back and compute status normally. This will be overridden by + `--deserialize-wait=` on the command line. ++ +-- +* `fail` - cause git to exit with an error when the status cache file +is stale; this is intended for testing and debugging. +* `block` - cause git to spin and periodically retry the cache file +every 100 ms; this is intended to help coordinate with another git +instance concurrently computing the cache file. +* `no` - to immediately fall-back if cache file is stale. This is the default. +* `` - time (in tenths of a second) to spin and retry. +-- diff --git a/Documentation/config/survey.txt b/Documentation/config/survey.txt new file mode 100644 index 00000000000000..857c1c3fff2d6a --- /dev/null +++ b/Documentation/config/survey.txt @@ -0,0 +1,41 @@ +survey.namerev:: + Boolean to show/hide `git name-rev` information for + each reported commit and the containing commit of each + reported tree and blob. + +survey.progress:: + Boolean to show/hide progress information. Defaults to + true when interactive (stderr is bound to a TTY). + +survey.showBlobSizes:: + A non-negative integer value. Requests details on the + largest file blobs by size in bytes. Provides a default + value for `--blob-sizes=` in linkgit:git-survey[1]. + +survey.showCommitParents:: + A non-negative integer value. Requests details on the + commits with the most number of parents. Provides a default + value for `--commit-parents=` in linkgit:git-survey[1]. + +survey.showCommitSizes:: + A non-negative integer value. Requests details on the + largest commits by size in bytes. Generally, these are the + commits with the largest commit messages. Provides a default + value for `--commit-sizes=` in linkgit:git-survey[1]. + +survey.showTreeEntries:: + A non-negative integer value. Requests details on the + trees (directories) with the most number of entries (files + and subdirectories). Provides a default value for + `--tree-entries=` in linkgit:git-survey[1]. + +survey.showTreeSizes:: + A non-negative integer value. Requests details on the + largest trees (directories) by size in bytes. This will + set will usually be equal to the `survey.showTreeEntries` + set, but may be skewed by very long file or subdirectory + entry names. Provides a default value for + `--tree-sizes=` in linkgit:git-survey[1]. + +survey.verbose:: + Boolean to show/hide verbose output. Default to false. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index e22b217fba9e2c..80838fe37ef30e 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -1,6 +1,7 @@ --[no-]all:: - Fetch all remotes. This overrides the configuration variable - `fetch.all`. + Fetch all remotes, except for the ones that has the + `remote..skipFetchAll` configuration variable set. + This overrides the configuration variable fetch.all`. -a:: --append:: diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt index 51d0f7e94b6a01..9d968191331080 100644 --- a/Documentation/git-maintenance.txt +++ b/Documentation/git-maintenance.txt @@ -107,6 +107,9 @@ with the prefetch task, the objects necessary to complete a later real fetch would already be obtained, making the real fetch faster. In the ideal case, it will just become an update to a bunch of remote-tracking branches without any object transfer. ++ +The `remote..skipFetchAll` configuration can be used to +exclude a particular remote from getting prefetched. gc:: Clean up unnecessary files and optimize the local repository. "GC" diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index e32404c6aaee30..93861d9f85b3b1 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -15,7 +15,8 @@ SYNOPSIS [--revs [--unpacked | --all]] [--keep-pack=] [--cruft] [--cruft-expiration=