Rebase onto Git for Windows v2.55.0#943
Merged
Merged
Conversation
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Teach subprocess_start() to use a copy of the passed `cmd` string rather than borrowing the buffer from the caller. Some callers of subprocess_start() pass the value returned from find_hook() which points to a static buffer and therefore is only good until the next call to find_hook(). This could cause problems for the long-running background processes managed by sub-process.c where later calls to subprocess_find_entry() to get an existing process will fail. This could cause more than 1 long-running process to be created. TODO Need to confirm, but if only read_object_hook() uses TODO subprocess_start() in this manner, we could drop this TODO commit when we drop support for read_object_hook(). Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Add function to start a subprocess with an argv. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Create a function to add a new object to the loose object cache after the existing odb/xx/ directory was scanned. This will be used in a later commit to keep the loose object cache fresh after dynamically fetching an individual object and without requiring the odb/xx/ directory to be rescanned. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Prevent packfile parsing from accidentally dynamically fetching each individual object found in the packfile. When index-pack parses the input packfile, it does a lookup in the ODB to test for conflicts/collisions. This can accidentally cause the object to be individually fetched when gvfs-helper (or read-object-hook or partial-clone) is enabled. The call site was migrated to odb_has_object() as part of the upstream refactoring, but odb_has_object(odb, oid, HAS_OBJECT_FETCH_PROMISOR) sets only OBJECT_INFO_QUICK without OBJECT_INFO_SKIP_FETCH_OBJECT, which means it WILL trigger remote fetches via gvfs-helper. But we want to prevent index-pack from individually fetching every object it encounters during the collision check. Passing 0 instead gives us both OBJECT_INFO_QUICK and OBJECT_INFO_SKIP_FETCH_OBJECT, which is the correct equivalent of the original OBJECT_INFO_FOR_PREFETCH behavior. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Create gvfs-helper. This is a helper tool to use the GVFS Protocol REST API to fetch objects and configuration data from a GVFS cache-server or Git server. This tool uses libcurl to send object requests to either server. This tool creates loose objects and/or packfiles. Create gvfs-helper-client. This code resides within git proper and uses the sub-process API to manage gvfs-helper as a long-running background process. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
The config variable `gvfs.sharedCache` contains the pathname to an alternate <odb> that will be used by `gvfs-helper` to store dynamically-fetched missing objects. If this directory does not exist on disk, `prepare_alt_odb()` omits this directory from the in-memory list of alternates. This causes `git` commands (and `gvfs-helper` in particular) to fall-back to `.git/objects` for storage of these objects. This disables the shared-cache and leads to poorer performance. Teach `alt_obj_usable()` and `prepare_alt_odb()`, match up the directory named in `gvfs.sharedCache` with an entry in `.git/objects/info/alternates` and force-create the `<odb>` root directory (and the associated `<odb>/pack` directory) if necessary. If the value of `gvfs.sharedCache` refers to a directory that is NOT listed as an alternate, create an in-memory alternate entry in the odb-list. (This is similar to how GIT_ALTERNATE_OBJECT_DIRECTORIES works.) This work happens the first time that `prepare_alt_odb()` is called. Furthermore, teach the `--shared-cache=<odb>` command line option in `gvfs-helper` (which is runs after the first call to `prepare_alt_odb()`) to override the inherited shared-cache (and again, create the ODB directory if necessary). Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Add trace2 message for CURL and HTTP errors. Fix typo reporting network error code back to gvfs-helper-client. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Fix parsing of the "loose <odb>" response from `gvfs-helper` and use the actually parsed OID when updating the loose oid cache. Previously, an uninitialized "struct oid" was used to update the cache. This did not cause any corruption, but could cause extra fetches for objects visited multiple times. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Add robust-retry mechanism to automatically retry a request after network
errors. This includes retry after:
[] transient network problems reported by CURL.
[] http 429 throttling (with associated Retry-After)
[] http 503 server unavailable (with associated Retry-After)
Add voluntary throttling using Azure X-RateLimit-* hints to avoid being
soft-throttled (tarpitted) or hard-throttled (429) on later requests.
Add global (outside of a single request) azure-throttle data to track the
rate limit hints from the cache-server and main Git server independently.
Add exponential retry backoff. This is used for transient network problems
when we don't have a Retry-After hint.
Move the call to index-pack earlier in the response/error handling sequence
so that if we receive a 200 but yet the packfile is truncated/corrupted, we
can use the regular retry logic to get it again.
Refactor the way we create tempfiles for packfiles to use
<odb>/pack/tempPacks/ rather than working directly in the <odb>/pack/
directory.
Move the code to create a new tempfile to the start of a single request
attempt (initial and retry attempts), rather than at the overall start
of a request. This gives us a fresh tempfile for each network request
attempt. This simplifies the retry mechanism and isolates us from the file
ownership issues hidden within the tempfile class. And avoids the need to
truncate previous incomplete results. This was necessary because index-pack
was pulled into the retry loop.
Minor: Add support for logging X-VSS-E2EID to telemetry on network errors.
Minor: rename variable:
params.b_no_cache_server --> params.b_permit_cache_server_if_defined.
This variable is used to indicate whether we should try to use the
cache-server when it is defined. Got rid of double-negative logic.
Minor: rename variable:
params.label --> params.tr2_label
Clarify that this variable is only used with trace2 logging.
Minor: Move the code to automatically map cache-server 400 responses
to normal 401 response earlier in the response/error handling sequence
to simplify later retry logic.
Minor: Decorate trace2 messages with "(cs)" or "(main)" to identify the
server in log messages. Add params->server_type to simplify this.
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Expose the differences in the semantics of GET and POST for
the "gvfs/objects" API:
HTTP GET: fetches a single loose object over the network.
When a commit object is requested, it just returns
the single object.
HTTP POST: fetches a batch of objects over the network.
When the oid-set contains a commit object, all
referenced trees are also included in the response.
gvfs-helper is updated to take "get" and "post" command line options.
the gvfs-helper "server" mode is updated to take "objects.get" and
"objects.post" verbs.
For convenience, the "get" option and the "objects.get" verb
do allow more than one object to be requested. gvfs-helper will
automatically issue a series of (single object) HTTP GET requests
and creating a series of loose objects.
The "post" option and the "objects.post" verb will perform bulk
object fetching using the batch-size chunking. Individual HTTP
POST requests containing more than one object will be created
as a packfile. A HTTP POST for a single object will create a
loose object.
This commit also contains some refactoring to eliminate the
assumption that POST is always associated with packfiles.
In gvfs-helper-client.c, gh_client__get_immediate() now uses the
"objects.get" verb and ignores any currently queued objects.
In gvfs-helper-client.c, the OIDSET built by gh_client__queue_oid()
is only processed when gh_client__drain_queue() is called. The queue
is processed using the "object.post" verb.
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
During development, it was very helpful to see the gvfs-helper do its work to request a pack-file or download a loose object. When these messages appear during normal use, it leads to a very noisy terminal output. Remove all progress indicators when downloading loose objects. We know that these can be numbered in the thousands in certain kinds of history calls, and would litter the terminal output with noise. This happens during 'git fetch' or 'git pull' as well when the tip commits are checked for the new refs. Remove the "Requesting packfile with %ld objects" message, as this operation is very fast. We quickly follow up with the more valuable "Receiving packfile %ld%ld with %ld objects". When a large "git checkout" causes many pack-file downloads, it is good to know that Git is asking for data from the server. Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
If our POST request includes a commit ID, then the the remote will send a pack-file containing the commit and all trees reachable from its root tree. With the current implementation, this causes a failure since we call install_loose() when asking for one object. Modify the condition to check for install_pack() when the response type changes. Also, create a tempfile for the pack-file download or else we will have problems! Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Create t/helper/test-gvfs-protocol.c and t/t5799-gvfs-helper.sh to test gvfs-helper. Create t/helper/test-gvfs-protocol.c as a stand-alone web server that speaks the GVFS Protocol [1] and serves loose objects and packfiles to clients. It is borrows heavily from the code in daemon.c. It includes a "mayhem" mode to cause various network and HTTP errors to test the retry/recovery ability of gvfs-helper. Create t/t5799-gvfs-helper.sh to test gvfs-helper. [1] https://github.com/microsoft/VFSForGit/blob/master/Protocol.md Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
gvfs-helper prints a "loose <oid>" or "packfile <name>" messages after they are received to help invokers update their in-memory caches. Move the code to accumulate these messages in the result_list into the install_* functions rather than waiting until the end. POST requests containing 1 object may return a loose object or a packfile depending on whether the object is a commit or non-commit. Delaying the message generation just complicated the caller. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Earlier versions of the test always returned a packfile in response to a POST. Now we look at the number of objects in the POST request. If > 1, always send a packfile. If = 1 and it is a commit, send a packfile. Otherwise, send a loose object. This is to better model the behavior of the GVFS server/protocol which treats commits differently. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
It is possible that a loose object that is written from a GVFS protocol "get object" request does not match the expected hash. Error out in this case. 2021-10-30: The prototype for read_loose_object() changed in 31deb28 (fsck: don't hard die on invalid object types, 2021-10-01) and 96e41f5 (fsck: report invalid object type-path combinations, 2021-10-01). Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Update tracing around report_tracking() to use 'tracking' category rather than 'exp' category. Add ahead/behind results from stat_tracking_info(). Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Teach helper/test-gvfs-protocol to be able to send corrupted loose blobs. Add unit test for gvfs-helper to detect receipt of a corrupted loose blob. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Teach gvfs-helper to support "/gvfs/prefetch" REST API. This includes a new `gvfs-helper prefetch --since=<t>` command line option. And a new `objects.prefetch` verb in `gvfs-helper server` mode. If `since` argument is omitted, `gvfs-helper` will search the local shared-cache for the most recent prefetch packfile and start from there. The <t> is usually a seconds-since-epoch, but may also be a "friendly" date -- such as "midnight", "yesterday" and etc. using the existing date selection mechanism. Add `gh_client__prefetch()` API to allow `git.exe` to easily call prefetch (and using the same long-running process as immediate and queued object fetches). Expanded t5799 unit tests to include prefetch tests. Test setup now also builds some commits-and-trees packfiles for testing purposes with well-known timestamps. Expanded t/helper/test-gvfs-protocol.exe to support "/gvfs/prefetch" REST API. Massive refactor of existing packfile handling in gvfs-helper.c to reuse more code between "/gvfs/objects POST" and "/gvfs/prefetch". With this we now properly name packfiles with the checksum SHA1 rather than a date string. Refactor also addresses some of the confusing tempfile setup and install_<result> code processing (introduced to handle the ambiguity of how POST works with commit objects). Update 2023-05-22 (v2.41.0): add '--no-rev-index' to 'index-pack' to avoid writing the extra (unused) file. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
The gvfs-helper allows us to download prefetch packs using a simple subprocess call. The gvfs-helper-client.h method will automatically compute the timestamp if passing 0, and passing NULL for the number of downloaded packs is valid. Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Teach gvfs-helper to better support the concurrent fetching of the same packfile by multiple instances. If 2 instances of gvfs-helper did a POST and requested the same set of OIDs, they might receive the exact same packfile (same checksum SHA). Both processes would then race to install their copy of the .pack and .idx files into the ODB/pack directory. This is not a problem on Unix (because of filesystem semantics). On Windows, this can cause an EBUSY/EPERM problem for the loser while the winner is holding a handle to the target files. (The existing packfile code already handled simple the existence and/or replacement case.) The solution presented here is to silently let the loser claim victory IIF the .pack and .idx are already present in the ODB. (We can't check this in advance because we don't know the packfile SHA checksum until after we receive it and run index-pack.) We avoid using a per-packfile lockfile (or a single lockfile for the `vfs-` prefix) to avoid the usual issues with stale lockfiles. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
When using the GVFS protocol, we should _never_ call "git fetch-pack" to attempt downloading a pack-file via the regular Git protocol. It appears that the mechanism that prevented this in the VFS for Git world is due to the read-object hook populating the commits at the new ref tips in a different way than the gvfs-helper does. By acting as if the fetch-pack succeeds here in remote-curl, we prevent a failed fetch. Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Construct 2 new unit tests to explicitly verify the use of `--fallback` and `--no-fallback` arguments to `gvfs-helper`. When a cache-server is enabled, `gvfs-helper` will try to fetch objects from it rather than the origin server. If the cache-server fails (and all cache-server retry attempts have been exhausted), `gvfs-helper` can optionally "fallback" and try to fetch the objects from the origin server. (The retry logic is also applied to the origin server, if the origin server fails on the first request.) Add new unit tests to verify that `gvfs-helper` respects both the `--max-retries` and `--[no-]fallback` arguments. We use the "http_503" mayhem feature of the `test_gvfs_protocol` server to force a 503 response on all requests to the cache-server and the origin server end-points. We can then count the number of connection requests that `gvfs-helper` makes to the server and confirm both the per-server retries and whether fallback was attempted. Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Add a release-pipeline scaffold for microsoft/git on Azure Pipelines, structured around a prereqs stage, a per-platform build stage with placeholder jobs, and a release stage that downloads the build artifacts and publishes them to a draft GitHub release. The per-platform build, signing, and validation logic lands in subsequent commits. The pipeline targets Microsoft-internal 1ES-hosted images across Windows x64, Windows ARM64, macOS, Ubuntu x64, and Ubuntu ARM64. Windows and Linux matrix entries carry a `poolArch` dimension because the 1ES hosted pools select their image from `hostArchitecture`; an arm64 entry on a default x64 pool would silently grab the wrong image. macOS uses the same matrix-parameter shape as Windows and Linux, so future macOS variants drop in the same way an extra Windows toolchain would. The prereqs stage derives the Git version, tag name, and tag SHA via resolve-version.sh and exposes them as pipeline variables for downstream stages to pick up. For that walk to find tags at all, the prereqs checkout uses fetchDepth: 0 / fetchTags: true. setup-git-bash.cmd is the Windows-side prerequisite that prepends Git Bash to PATH, since the bare hosted image does not provide a bash for Bash@3 tasks to find. ESRP signing to be added later. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
## TL;DR Add a new `vfs-functional-tests.yml` workflow that builds Git from this repository and runs the VFS for Git functional tests against it, using VFSForGit's reusable workflow. ## Why? VFS for Git functional tests currently only run in the VFSForGit repository, against a tagged microsoft/git release. This means VFS-related regressions in Git are only caught *after* a release is tagged. By running the FTs here on every push and PR to `vfs-*` branches, we can catch regressions before they ship. This is the counterpart to microsoft/VFSForGit#1932, which extracted the functional tests into a reusable `workflow_call` workflow. ## How it works 1. **Build Git** — checks out this repo, builds with the Git for Windows SDK, and packages the result into a `MicrosoftGit` artifact with an `install.bat` that deploys via robocopy to `C:\Program Files\Git`. Both ARM64 and x64 are built and combined into a single artifact for the FTs to install and use. 2. **Find VFSForGit build** — locates the latest successful VFSForGit CI run on `master` to get the GVFS installer and FT executables. If the build was a 'skipped' build (because an existing run succeeded with that tree) then follow the annotation to the real run. 3. **Call reusable workflow** — invokes `microsoft/VFSForGit/.github/workflows/functional-tests.yaml@master`, which handles the full test matrix (2 configs × 2 architectures × 10 slices)
Refactor install_prefetch() to process prefetch packs in two distinct phases: Phase 1 (extraction): Read the multipack stream sequentially, copying each packfile to its own temp file and recording its checksum and timestamp in a prefetch_entry array. This must be sequential because the multipack is a single byte stream. Phase 2 (indexing): Run 'git index-pack' on each extracted temp file and finalize it into the ODB. Today this still runs sequentially, but the separation makes it straightforward to parallelize in a subsequent commit. The new extract_packfile_from_multipack() only does I/O against the multipack fd plus temp-file creation. The new index_and_finalize_packfile() only does the index-pack and rename work. Neither depends on the other's state, so they can operate on different entries concurrently once the extraction phase completes. No behavioral change; this is a pure refactor. Signed-off-by: Derrick Stolee <stolee@gmail.com>
Add blame-specific configuration options for rename detection: - blame.renames: disable rename following (defaults to true) - blame.renameThreshold: minimum similarity for rename detection - blame.renameLimit: limit on rename detection candidates These are independent of the diff.renames/renameThreshold/renameLimit settings, which blame does not read because its config callback chains to git_default_config rather than git_diff_basic_config. The threshold and limit options forward to git_diff_basic_config to set the same globals that repo_diff_setup() copies into diff_options, following the existing pattern used for diff.algorithm. Also move diff.renameThreshold from git_diff_ui_config to git_diff_basic_config, alongside the related diff.renameLimit, since it controls rename detection behavior rather than UI presentation. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Add diff.renameThreshold, merge.renameThreshold, and status.renameThreshold configuration options to control the minimum similarity threshold for rename detection without requiring command-line flags. The cascade follows the existing pattern for renameLimit and renames: - merge.renameThreshold overrides diff.renameThreshold for merges - status.renameThreshold overrides diff.renameThreshold for status - CLI flags (-M, --find-renames) override all config values The value accepts the same format as -M: a percentage (e.g. 50%) or a fraction (e.g. 0.5). If unset, the default remains 50%. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add a stub pipeline for releases using Azure Pipelines. The pipeline runs on Microsoft internal images/runners across: * Windows x64 * Windows ARM64 * macOS * Ubuntu x64 * Ubuntu ARM64 At the start of a run there is a prerequisite stage and pre-build validation. Today this does nothing, and should be updated to: * validate the current commit is tagged (annotated), and * capture the Git version, tag name and SHA. Artifacts are uploaded from the build stage, and downloaded into the release stage later for uploading to a draft GitHub release. ESRP signing to be added later.
Replace the sequential index-pack loop in install_prefetch() with run_processes_parallel(), spawning up to four concurrent 'git index-pack' workers. The packfiles are already ordered by timestamp (oldest first) in the multipack response. In the common fresh-clone scenario the oldest pack is by far the largest, so it starts indexing immediately on the first worker while the remaining workers cycle through the smaller daily and hourly packs. Note that this works for the GVFS prefetch endpoint as all prefetch packfiles are non-thin packs. The bundle URI feature uses thin bundles that must be unpacked sequentially. Worker count is min(np, PREFETCH_MAX_WORKERS) where PREFETCH_MAX_WORKERS is 4, so we never create more workers than there are packfiles. When there is only a single packfile the parallel infrastructure is skipped entirely and index-pack runs directly. The default grouped mode of run_processes_parallel() is used so that child-process completion is detected via poll() on stderr pipes rather than the ungroup mode's aggressive mark-all-slots-WAIT_CLEANUP approach, which can misfire on slots that never started a process. The run_processes_parallel() callbacks are always invoked from the main thread, so finalize_prefetch_packfile() (which renames files into the ODB) needs no locking. If any index-pack fails, the error is recorded and remaining tasks still complete so that successfully-indexed packs are not lost. I performed manual performance testing on Linux using an internal monorepo. I deleted a set of recent prefetch packfiles, leading to a download of a couple daily packfiles and several hourly packfiles. This led to an improvement from 85.2 seconds to 40.3 seconds. Signed-off-by: Derrick Stolee <stolee@gmail.com>
blame's config callback did not load diff.renameThreshold because it chained directly to git_default_config, skipping git_diff_basic_config (and git_diff_ui_config) where that setting is handled. As a result, repo_diff_setup() always saw a zero rename_score, and blame fell back to DEFAULT_RENAME_SCORE (50%) regardless of the configured threshold. This PR moves diff.renameThreshold from git_diff_ui_config to git_diff_basic_config, alongside the related diff.renameLimit setting, so that plumbing commands that use git_diff_basic_config can also pick it up - but instead of trying to import that for blame, this PR adds separate configuration for blame.renames, blame.renameThreshold, and blame.renameLimit Assisted-by: Claude Opus 4.6
Add diff.renameThreshold, merge.renameThreshold, and
status.renameThreshold configuration options to control the minimum
similarity threshold for rename detection without requiring command-line
flags.
The cascade follows the existing pattern for renameLimit and renames:
- merge.renameThreshold overrides diff.renameThreshold for merges
- status.renameThreshold overrides diff.renameThreshold for status
- CLI flags (-M, --find-renames) override all config values
The value accepts the same format as -M: a percentage (e.g. 50%) or a
fraction (e.g. 0.5). If unset, the default remains 50%.
This also gives git-blame users control over the rename threshold for
the first time, since blame has no -M threshold flag but inherits
diff.renameThreshold via repo_diff_setup().
Assisted-by: Claude Opus 4.6
__________________
Thanks for taking the time to contribute to Git!
This fork contains changes specific to monorepo scenarios. If you are an
external contributor, then please detail your reason for submitting to
this fork:
* [x] 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.
Introduce a new config option, gvfs.prefetchThreads, that controls the number of parallel index-pack processes used when installing prefetch packfiles. The default value is 1, which processes packfiles sequentially without any parallel infrastructure, preserving the existing behavior as a safety measure. When set to a value greater than 1, the prefetch installation phase uses run_processes_parallel() with up to that many concurrent index-pack workers. This can significantly speed up installation when multiple prefetch packs are downloaded. The sequential path (threads=1) loops through each extracted packfile one at a time using my_run_index_pack() + finalize, while the parallel path (threads>1) dispatches index-pack processes via the existing parallel process machinery. Add trace2 data points (prefetch/install_mode) to log which code path is taken, enabling tests to verify the correct mode is active. Add t5797-gvfs-helper-prefetch-threads.sh with tests that exercise both sequential and parallel modes by looping over threads=1 and threads=4. Each iteration runs four scenarios: fetch-all, fetch-since, up-to-date re-fetch, and corrupt-pack error handling. Trace2 assertions confirm the expected code path is taken in each mode. Signed-off-by: Derrick Stolee <stolee@gmail.com>
This ports the changes from #894 from the `vfs-2.53.0` release train to the `vfs-2.54.0` release train.
Bring ESRP code signing into the release pipeline, gated behind an `esrp` boolean parameter that defaults to false until the rest of the signing wiring catches up. Microsoft policy precludes shipping unsigned binaries from this pipeline, so every per-platform build job needs an obvious place to plug signing in. The Windows flow runs through a custom esrpsign.sh script rather than the EsrpCodeSigning ADO task. The custom script gives the later Windows installer commit a CLI-shaped seam it can register as Git for Windows' `git signtool` alias from build-extra's release pipeline, so every binary embedded inside the installer gets signed rather than only the outer .exe wrapper. The canned ADO task does not expose that integration point. The accompanying setup template uses AzureCLI@2 to bind to the WIF service connection by name rather than by GUID, and relies on addSpnToEnvironment to surface the service principal ID, tenant ID, and connection GUID at runtime via ENDPOINT_URL_* env vars. That way esrpsign.sh composes the auth JSON with no hardcoded identifiers leaking into the repository. EsrpClientTool@4 takes care of downloading and caching the ESRP client binary itself. macOS and Linux take the simpler path: the EsrpCodeSigning@6 task via a shared sign.yml template. macOS in particular requires an archive submission (useArchive: true), so centralising the copy/zip/sign/extract cycle in the template keeps each platform job from re-implementing it. The Linux hosted agents do not ship with .NET, which EsrpCodeSigning requires. UseDotNet@2 installs the .NET 8 SDK ahead of the signing template invocation so Linux signing works out of the box without per-platform plumbing. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Smoke tests and dry runs of the pipeline itself need to build microsoft/git from an untagged tip, where resolve-version.sh's tag walk would either fail or pick up an unrelated tag. Add a queue-time `versionOverride` parameter; when it is set to anything other than the empty string or the sentinel `-`, the prereqs stage emits the override verbatim as the build version, labels the tag name as `untagged`, and bypasses resolve-version.sh entirely. A build from an untagged commit must not race a real release upload, so a non-empty override also forces the GitHub publishing job off regardless of the `github` parameter, and the prereqs step logs a warning to make that consequence visible in the run summary. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform Linux job that takes microsoft/git from
source to a signed `microsoft-git_<version>_<arch>.deb` staged
under `$(Build.ArtifactStagingDirectory)/_final/`. The flow
ports `create-linux-unsigned-artifacts` and
`create-linux-signed-artifacts` from the GitHub workflow at
.github/workflows/build-git-installers.yml, keeping the Make
recipe and DEBIAN/control body byte-for-byte identical so a
diff against the workflow's output is empty modulo the
deliberate departures called out below.
The GitHub workflow runs everything inside an ubuntu:20.04 /
ubuntu:22.04 container, both to pin the resulting .deb's glibc
ABI floor and to give apt-get a root-owned filesystem. The 1ES
pool images we run on
(GitClientPME-1ESHostedPool-{intel,arm64}-pc) silently ignore a
job-level `container:` directive, so the build executes on the
bare Ubuntu host VM as the unprivileged agent user. apt-get
therefore runs via `sudo`, and the job logs the Ubuntu version,
kernel, and effective UID up front so an audit can read the
.deb's effective glibc floor back from the build output.
Re-introducing a real container later (whether via 1ES's
container option, a custom container job, or docker invoked
from a step) is a separate question. The workflow's
`DEBIAN_FRONTEND=noninteractive` and `TZ=Etc/UTC` env vars
exist only to keep `tzdata` quiet inside a fresh container; the
bare 1ES image already has tzdata configured, so they are
dropped. The Node.js workaround in the workflow similarly
exists only to satisfy GitHub Actions' Node-based shim and is
not needed under Azure Pipelines.
A few intentional content changes: parallelism switches from
the workflow's hard-coded `-j5` (a runner-specific holdover) to
`-j$(nproc)`, which adapts to whatever the 1ES pool gives us;
the shell prologue changes from `set -ex` to `set -euo
pipefail` so an unbound variable or a failing stage in a pipe
aborts the job rather than silently producing a broken .deb;
`$(git_version)` now comes from the prereqs stage, dropping the
workflow's runtime dpkg-architecture round-trip in favour of
the matrix's explicit `amd64` / `arm64` entries via
`$(deb_arch)`. The `s/-rc/.rc/g` substitution carries over
because Git's GIT-VERSION-GEN spells release-candidate tags
with a dot.
The build drops its output under
`$(Build.ArtifactStagingDirectory)/app/` so the existing ESRP
signing template's `**/*.deb` pattern picks it up. A focused
move of just the signed
`microsoft-git_<version>_<arch>.deb` into
`$(Build.ArtifactStagingDirectory)/_final/` then feeds the
existing `templateContext.outputs.pipelineArtifact` for the
`linux_x64` / `linux_arm64` artifact. Naming the file
precisely turns "ESRP signed something else" into a
missing-file error rather than a silent wrong-artifact upload.
Assisted-by: Claude Opus 4.7
Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform macOS job that takes microsoft/git from source to a signed-and-notarized `git-<version>-universal.pkg` plus the corresponding `.dmg`, both staged under `$(Build.ArtifactStagingDirectory)/_final/`. The flow ports `create-macos-artifacts` and `create-macos-signed-artifacts` from the GitHub workflow at .github/workflows/build-git-installers.yml and leans on .github/macos-installer/Makefile for the heavy lifting, but swaps the workflow's productsign + xcrun notarytool path for ESRP signing and ESRP MacAppNotarize. The native Homebrew on the macOS-15-arm64 pool image is arm64 and lives at /opt/homebrew. Producing a universal binary additionally requires the x86_64 build of gettext/libintl, so a separate x86_64 Homebrew gets installed under /usr/local via the upstream installer running under Rosetta and pulls gettext from there as well. The two arch-specific libintl.a copies are then combined with lipo into a universal archive at the workspace root, which the upcoming config.mak's `LDFLAGS = -L"$(pwd)"` resolves. libintl depends on iconv, but the system /usr/lib/libiconv.dylib is already universal and exports the `_iconv*` symbols Homebrew's gettext was built against; Homebrew's own libiconv exports `_libiconv*` and would not link, hence the explicit `USE_HOMEBREW_LIBICONV` / `ICONVDIR` overrides in config.mak. Spotlight indexing on the boot volume is disabled (`mdutil -i off /`) at the start of the job because leaving it on caused intermittent file-locking failures in subsequent steps. config.mak collects the Make flags that turn on the dual-arch compile and route around several macOS quirks: HOST_CPU=universal, dual-arch CFLAGS (the actual universal-binary driver), -DNO_OPENSSL for contrib Makefiles that do not see the main Makefile's NO_OPENSSL handling, the USE_HOMEBREW_LIBICONV / ICONVDIR overrides, gettext include dirs from both Homebrew prefixes, and CURL_LDFLAGS / CURL_CONFIG pinned against the OS-supplied libcurl rather than a Homebrew copy. SKIP_DASHED_BUILT_INS disables the dashed built-ins because on macOS the hard-link optimisation does not kick in for the staging tree and the resulting full copies would bloat the eventual .dmg. `make GIT-VERSION-FILE dist dist-doc` runs in the source tree; `git get-tar-commit-id` recovers the original commit OID from the resulting source tarball (this becomes GIT_BUILT_FROM_COMMIT, which the macos-installer Makefile bakes into `git version --build-options`); the source and manpage tarballs extract into payload/ and manpages/; a copy of config.mak is dropped inside the extracted source so the universal-build flags apply during the real compile; finally `make -C .github/macos-installer payload` produces the universal binary tree. `git get-tar-commit-id` reads only the leading pax header and then closes its stdin, which makes `gunzip -c` exit 141 (SIGPIPE) under the outer `set -o pipefail`; the pipeline is wrapped in a `set +o pipefail` subshell so the SIGPIPE does not abort the build. The macos-installer Makefile produces the install tree at stage/git-universal-<ver>/ but its `pkg` target packages from build-artifacts/, so the tree is copied across after `make payload` completes, mirroring the GitHub workflow. GITHUB_WORKSPACE=$(Build.SourcesDirectory) is exported because the Makefile derives BUILD_DIR from $(GITHUB_WORKSPACE), which is unset under Azure Pipelines. XML_CATALOG_FILES points at the catalogs from the Homebrew docbook installed in the dependencies step. (FUTURE: the duplication exists only because .github/macos-installer/Makefile hardcodes both DESTDIR=stage/... and ARTIFACTDIR=build-artifacts; overriding ARTIFACTDIR on the `make pkg` line below to point at stage/ would let us drop the cp entirely. Worth cleaning up alongside moving the macOS installer Makefiles out of .github/, where they live for historical reasons rather than because they are GitHub-specific.) Signing happens against the install tree at .github/macos-installer/build-artifacts/usr/local/git/, not the source tree under payload/git-<version>/, because the macos-installer Makefile's `pkg` target packages from build-artifacts/; signing the source tree would have no effect on the resulting .pkg. Following the pattern in git-credential-manager/.azure-pipelines/release.yml, the install tree is pre-filtered to just the Mach-O files (using `file --mime` matching `mach`, the same heuristic .github/scripts/codesign.sh uses), copied into a staging directory under $(Build.ArtifactStagingDirectory)/macos-tosign/ preserving relative paths, handed to the existing .azure-pipelines/esrp/sign.yml template (which zips, signs via EsrpCodeSigning@6 with KeyCode CP-401337-Apple + OperationCode MacAppDeveloperSign + Hardening enabled, and extracts back into the staging dir), and finally copied back into the install tree. The pre-filter is necessary because the existing template's CopyFiles@2 step uses minimatch globs and the only reliable way to pick out Mach-O files is by file content; signing the entire install tree would either fail on non-binary files or sign things that should not be signed (shell scripts, perl, manpages, templates, the uninstall.sh). UseDotNet@2 (8.x) installs the .NET SDK that EsrpCodeSigning@6 depends on, since the macOS-15-arm64 pool image does not provide it. The macos-installer Makefile's `pkg` target then produces an unsigned .github/macos-installer/disk-image/git-<version>-universal.pkg from the signed payload tree. APPLE_INSTALLER_IDENTITY is deliberately left undefined so pkgbuild does not try to sign; the next step submits the .pkg back through ESRP for signing (KeyCode CP-401337-Apple covers both Developer ID Application and Developer ID Installer certs in this account, so MacAppDeveloperSign on a .pkg is the productsign equivalent), and then through ESRP for Apple notarization (MacAppNotarize, BundleId com.git.pkg, matching the identifier pkgbuild bakes in via `--identifier com.git.pkg` from the Makefile). The ESRP MacAppNotarize operation handles both submission and ticket stapling, returning the notarized .pkg back into disk-image/ via the same zip-extract template path the previous sign step used; this is what replaces the `xcrun notarytool submit ... --wait` plus `xcrun stapler staple` flow from .github/scripts/notarize.sh. Finally the Makefile's `image` target builds .github/macos-installer/git-<version>-universal.dmg from the contents of disk-image/. The .dmg lands at the macos-installer root, while the signed-and-notarized .pkg lives somewhere under disk-image/: ESRP's MacAppNotarize op repacks its output zip to wrap the notarized .pkg in a UUID-named .zip.unzipped/ subdirectory, so depending on whether notarization ran, the .pkg ends up either directly under disk-image/ or at disk-image/<uuid>.zip.unzipped/git-...pkg. `find` locates it and moves it (along with the globbed .dmg) into $(Build.ArtifactStagingDirectory)/_final/, which the job's templateContext.outputs already publishes as the `macos_universal` pipeline artifact. `set -euo pipefail` means an empty `find` result, or a missing .dmg, fails the mv loudly rather than producing a silent half-empty upload, matching the same defensive choice the Linux stage step makes. Assisted-by: Claude Opus 4.7 Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the per-platform Windows job that takes microsoft/git from source to ESRP-signed `Git-<version>-<arch>.exe` and `PortableGit-<version>-<arch>.exe`, plus the matching sha-256.txt sidecar, all staged under $(Build.ArtifactStagingDirectory)/_final/. The flow ports `create-windows-artifacts` and `create-windows-signed-artifacts` from the GitHub workflow at .github/workflows/build-git-installers.yml, leaning on git-for-windows/build-extra (please.sh + installer/release.sh) for the build itself. GitHub Actions has the git-for-windows/setup-git-for-windows-sdk@v1 action that drops a full SDK onto the runner; Azure Pipelines has no equivalent task, so the Windows job has to bootstrap the SDK by hand before any of the bash-driven build steps can run. Bootstrap is driven by .azure-pipelines/scripts/windows/setup-git-sdk.sh, which a Bash@3 task invokes via `filePath:` so it runs under the agent's MinGit-provided bash. A `sdk_repo` field on each windows_matrix entry (git-for-windows/git-sdk-64 for x64, git-sdk-arm64 for ARM64) lets the script pick the right upstream; the script does a partial+bare clone of the SDK, clones git-for-windows/build-extra into a sibling directory, then runs `please.sh create-sdk-artifact --sdk=<bare> --out=<sdk> build-installers` to materialise the build-installers flavour of the SDK at the requested output path. Routing through `please.sh create-sdk-artifact` keeps the bytes flowing via plain GitHub HTTPS clones (which 1ES allows) rather than the raw and release-asset CDNs that an earlier download-the-snapshot approach hit. Once the SDK is in place, its own usr/bin (which ships cygpath) and the matching MinGW toolchain bin/ are exposed to subsequent tasks via ##vso[task.prependpath]. The arm64 Windows hosted agents do not have Azure CLI pre-installed, which the AzureCLI@2 task in the ESRP setup step needs. Install the x64 MSI (which runs under x86-64 emulation on arm64 Windows) and prepend it to PATH, gated on a poolArch condition. This is a workaround until the bug preventing us from baking Azure CLI into the hosted pool image is fixed, at which point this step can be dropped. A small helper, .azure-pipelines/scripts/windows/utils.sh, provides `to_windows_path` / `to_unix_path` for scripts running on Windows agents. Both prefer `cygpath` when it is on PATH and fall back to a small pure-shell parser otherwise. The fallback matters because some bash steps run before the SDK is bootstrapped and only have MinGit's bash available, which does not ship cygpath. Both setup-git-sdk.sh and the ESRP-sign script source utils.sh rather than duplicating the conversion logic. Git for Windows' build tooling (please.sh, signtool.sh, the installer .iss templates, and the MINGW-packages helpers) lives in git-for-windows/build-extra rather than in the SDK snapshot. The GitHub workflow's Windows job clones it into /usr/src/build-extra of the SDK before invoking please.sh; this job does the same. A partial clone (--filter=blob:none) plus --single-branch -b main is enough for everything please.sh needs and avoids pulling the full blob history, matching the workflow's invocation byte for byte. The mingw-w64-git package is built via please.sh build-mingw-w64-git from a Bash@3 task using the SDK's bash that the bootstrap put on PATH. Outputs land in $(Build.SourcesDirectory)/artifacts/ so the subsequent installer-build step can pass them to please.sh make_installers_from_mingw_w64_git via --pkg= flags. Three small adaptations from the GitHub workflow's source step are worth flagging. First, the /usr/bin/git trampoline that delegates to the matching MinGW-built git.exe is the same one the workflow writes by hand; makepkg-mingw shells out to plain `git`, and the SDK bash's git candidates would otherwise come from MinGit, not the toolchain we are building against. Second, the user.name / user.email / PACKAGER values are hardcoded to a build-bot identity since Azure Pipelines has no GitHub-actor equivalent. Third, please.sh's --only-<arch> flag takes the bare CPU name (x86_64 or aarch64), not the toolchain triple, so a `cpu_arch` matrix dimension surfaces the right value next to each toolchain entry. The build task detaches stdin via `exec </dev/null` before invoking makepkg-mingw, because pacman's git-extra post_install hook runs `for s in $(grep -l PAT $(find /mingw*/bin/ ...))` and falls back to reading stdin when /mingw*/bin/ is absent and find produces empty output. Bash@3 leaves the task's stdin pipe open with no writer (the GitHub Actions runner closes it for the same reason; see actions/runner ProcessInvoker.cs), so without the detach the build would hang indefinitely waiting for input. Two pieces of the workflow's pkg-build step are intentionally not ported and are noted in a comment for follow-up: the per-tarball GPG signing (replaceable with an ESRP PGP operation analogous to the Linux LinuxSign flow if needed downstream), and the MINGW-packages bundle creation, which depends on a PKGBUILD.<tag_name> snapshot that microsoft/git does not currently ship. A single bash task drives please.sh make_installers_from_mingw_w64_git for both installer and portable variants. The GitHub workflow runs these as separate matrix jobs (one per type/arch combination); keeping both builds in the same Azure Pipelines job means the .pkg.tar.* artifacts produced by the previous step are available without an inter-job artifact passing trip. The PDB archive copy into build-extra/cached-source-packages is the same prerequisite that --include-pdbs needs in the GitHub workflow. The --pkg= filter that strips signatures and the optional archimport / cvs / p4 / gitweb / doc-man pieces matches the workflow's sed exactly so the resulting .exe sizes are comparable. The same `exec </dev/null` stdin detach as the build mingw-w64-git step applies. To make a silent hang inside the nested pacman / makepkg / Inno Setup chain debuggable, BASH_ENV is pointed at a tiny `set -x` fragment so every nested non-interactive bash subshell auto-enables xtrace, and please.sh itself is invoked via `sh -x`. The trace volume is the cost we pay for being able to identify which step a future hang is stuck in; cheap compared to debugging a silent hang. The GitHub Actions workflow applies five `sed` transformations during the Windows build to turn upstream Git for Windows into the microsoft/git distribution. They are spread over five run: blocks and largely opaque without following each sed pattern by hand. Capture them as patches under .azure-pipelines/patches/ instead, grouped by the upstream tree they mutate (build-extra/* for installer customisation, git-sdk/* for the SDK's git-update-git-for-windows helper); the patches themselves carry the explanatory commit-style headers each transformation deserves. A small helper, .azure-pipelines/scripts/apply-patches.sh, applies every *.patch in a directory in lexicographic order via `patch -p1`. patch(1) is used rather than `git apply` because the latter is strict about context whitespace; CRLF/LF mismatches between the patch context (as authored) and the working tree (which may be CRLF on Windows checkouts) trip it up. patch is more forgiving by default, and matches the convention used by msys2/MINGW-packages PKGBUILDs and git-for-windows/build-extra's get-sources.sh. The patches apply against /usr/src/build-extra and against the SDK's /$(mingwprefix), between cloning build-extra and building the mingw-w64-git package, mirroring where the GitHub workflow's sed steps slot in. ESRP signing is wired through build-extra's `signtool` alias hook so that ESRP signs every binary that ends up inside the Windows installer and portable Git, not just the outer .exe wrapper. A naive post-build sign of just `Git-*.exe` and `PortableGit-*.exe` would leave every binary embedded inside the installer (DLLs, helper exes, the mingw-w64-git pkg payload) shipping unsigned. The mechanism, set up by build-extra: please.sh's build_mingw_w64_git checks `git config alias.signtool` and, if set, exports `SIGNTOOL="git ... signtool"` into makepkg-mingw so the PKGBUILD can sign individual binaries during the package build; build-extra's installer/release.sh checks the same alias and passes `//Ssigntool="git signtool $f" //DSIGNTOOL` to Inno Setup's ISCC.exe, which then signs every embedded file via the SignTool=signtool directive in install.iss. The portable Git .exe is a 7z self-extractor that bypasses the Inno Setup signtool path, so it is signed explicitly after `make_installers` returns. The ESRP setup template (which sets ESRP_TOOL and ESRP_AUTH) runs before the build steps; a Bash@3 task registers the signtool alias to invoke .azure-pipelines/esrp/windows/esrpsign.sh; the ESRP env vars are added to both build tasks so esrpsign.sh has ESRP_TOOL, ESRP_AUTH, and SYSTEM_ACCESSTOKEN available when invoked via the alias from inside please.sh, makepkg-mingw, or Inno Setup. MSYSTEM is exported in the build installer task's env block because installer/release.sh requires it to select the architecture branch, and Bash@3 does not source /etc/profile. esrpsign.sh's calling convention (`<file> [file ...]`, sign in place) already matches signtool.sh's, so no further script changes are needed. Stage `Git-*.exe` and `PortableGit-*.exe` alongside a SHA-256 sidecar into $(Build.ArtifactStagingDirectory)/_final/, which the job's templateContext.outputs.pipelineArtifact already publishes as the `windows_x64` / `windows_arm64` artifact. The SHA-256 sidecar is computed here, post-build, rather than in the build step because ESRP signing rewrites the .exe contents; a SHA-256 computed before signing would mismatch the bytes that ship. Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
With build, signing, notarization, validation, and draft-release publishing all in place, the Azure Pipeline is ready to take over from the GitHub Actions build-git-installers workflow. Switch `trigger: none` to a tag-only trigger matching the same `v[0-9]*vfs*` pattern the GitHub workflow used (and that resolve-version.sh validates against), and explicitly exclude all branches so the pipeline does not fire on every topic-branch push. Flip the `esrp` and `github` parameter defaults from false to true. The GitHub release job still uses `isDraft: true`, so a maintainer inspects and publishes the release manually; manual runs in the Azure DevOps UI can still uncheck either box for a dry run. Assisted-by: Claude Opus 4.7 Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The 1ES PT Windows build job did not run binskim against the binaries
we ship. By default the template would point binskim at the published
artifact (the Inno Setup installer and the 7z self-extracting portable
.exe), neither of which binskim can crack open to find the PE files
inside, so any findings on those wrappers are also unactionable: they
are produced by external tools we do not control.
Opt the job into binskim explicitly and aim it at the actual product
binaries instead. Stage only the first-party pacman packages that
`please.sh build-mingw-w64-git` emits --
mingw-w64-<toolchain>-{git,git-credential-wincred,git-pdb}-*.pkg.tar.xz
-- into _bin/<mingwprefix>/ and scope the analyzer to the .exe/.dll
files in that tree. By construction those packages carry only the
binaries this repo's Makefile builds (git.exe, the dashed subcommands,
scalar.exe, headless-git.exe, git-gvfs-helper.exe,
git-credential-wincred.exe, ...) plus their cv2pdb-generated .pdbs,
so a broad **/*.{exe,dll} glob is safe.
Excluding everything else keeps the full Git for Windows installer
payload out of the scan: MSYS2/MinGW runtime, Perl, Tcl/Tk,
libcurl/libssl/libssh2, Git Credential Manager, Git LFS, tig, and the
build-extra git-wrapper launcher shims are all third-party content we
cannot fix from this repo.
Since the baseline that is added automatically does not conform to Git's
whitespace rules, also add a `.config/.gitattributes` file to suppress
those checks.
Assisted-by: Claude Opus 4.7
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
BinSkim flags toolchain-rooted issues on every release build that we cannot fix from this repo: BA2008 (Control Flow Guard) and BA2012 (stack cookie not locatable) on every clangarm64 binary, and BA2025 (CET shadow stack) on every mingw64 binary. CFG and CET shadow stack are gated on linker support that lld's MinGW driver does not expose, and BinSkim's stack-cookie check uses an MSVC PE walker that does not find clang's emitted cookie. None are actionable from microsoft/git. Point the SDL templateContext at a per-arch suppression file at .azure-pipelines/sdl/$dim.id/gdnsuppress so Guardian skips these known-bad findings on each scan. Per-arch paths keep the entries isolated to the matching toolchain and let either arch grow new entries without touching the other. Seed windows_arm64/gdnsuppress with the 44 hydrated entries Guardian auto-published in the drop_build_windows_arm64_sdl_analysis artifact on the previous release run; the signatures are derived from (tool, ruleId, target URI) and remain stable across rebuilds, so the same file applies to future runs. windows_x64/gdnsuppress ships as a stub with no suppression entries. BA2025 is the only BinSkim finding on x64 and it is Warning-severity, so it does not break the build, and Guardian's pipeline-export only hydrates findings at or above Error severity, so no canonical entries were auto-generated to seed from. The stub keeps the per-arch path uniform without requiring a YAML conditional, and gives us a place to drop x64 entries later if we ever want to silence the warning. Assisted-by: Claude Opus 4.7 Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Originally added to vfs-2.53.0. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When using Scalar clones with microsoft/git against Azure DevOps and GVFS Cache Servers, `git fetch` will download potentially multiple precomputed prefetch packfiles. The current mechanism indexes these files sequentially. Let's make those `git index-pack` processes run somewhat in parallel. For now, I've chosen to have a maximum of four parallel processes to limit the potential load on the disk. However, this already has some significant gains. When testing an internal monorepo (that uses Codespaces, for easy Linux testing) and deleting a few days of recent prefetch packfiles, the end-to-end `git fetch` time improved as follows: | Command | Mean [s] | Min [s] | Max [s] | Relative | |:---|---:|---:|---:|---:| | `new` | 40.306 ± 1.598 | 37.564 | 42.383 | 1.00 | | `old` | 85.213 ± 2.389 | 82.402 | 89.207 | 2.11 ± 0.10 | When downloading fewer prefetch packfiles, the improvement is still relevant: | Command | Mean [s] | Min [s] | Max [s] | Relative | |:---|---:|---:|---:|---:| | `new` | 6.411 ± 0.800 | 5.559 | 7.553 | 1.00 | | `old` | 13.906 ± 2.848 | 10.941 | 17.697 | 2.17 ± 0.52 | I should mention that I _first_ tried streaming data directly from the curl download into a sequence of `git index-pack` processes, but that did not make any serious difference in the performance. Based on these numbers, we are clearly blocked on the CPU time spent computing deltas and evaluating object hashes and not blocked on the "download to disk, then index from disk" I/O. I think it would be worthwhile to do some performance testing on Windows, at minimum, before merging this change. I'd like to get some feedback on the concept before going through those actions. Another question to ask is whether it is worth making this behavior configurable: should it be possible to disable parallel indexing in favor of a sequential process if a certain config option is set? Should we allow increasing the parallelism via config?
When 'git checkout <tree> -- <pathspec>' updates the index with entries from the source tree, update_some() creates a new cache entry that unconditionally clears skip-worktree. In a virtual filesystem repo (core.virtualfilesystem is set), this causes checkout_entry() to attempt unlink() on files that exist only as virtual projections with no physical NTFS entry. The unlink fails with ENOENT, producing 'error: unable to unlink old' messages and exit code 255. Fix this in three places: 1. update_some(): Propagate CE_SKIP_WORKTREE from the existing index entry to the replacement entry when core_virtualfilesystem is set. 2. mark_ce_for_checkout_overlay(): Allow skip-worktree entries that have CE_UPDATE (set by update_some for tree entries) to still match the pathspec, so report_path_error() does not reject them. 3. checkout_worktree(): Skip checkout_entry() for entries that have both CE_MATCHED and CE_SKIP_WORKTREE, since the virtual filesystem provider will serve the correct content from the updated projection. The index is updated to the new tree entry's OID while the working tree write is skipped entirely. The virtual filesystem provider re-reads the updated index and serves the correct content on next access. Same approach as the CE_NEW_SKIP_WORKTREE propagation in deleted_entry() (unpack-trees.c, PR #865) which avoids unnecessary lstats on virtualized paths during branch switches. Assisted-by: Claude Opus 4.6 Signed-off-by: Tyrie Vella <tyrielv@gmail.com> (cherry picked from commit d40f13b) Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
…ns (#920) Port #913 and #914 from the `vfs-2.53.0` to the `vfs-2.54.0` branch. While at it, reorganize the changes into a more concise commit history, including two `amend!` commits that target the next rebase to a new Git version. At time of writing, `git diff azp vfs-2.53.0 -- .azure-pipelines` shows an empty diff, proving that it brings `vfs-2.54.0` up to date with the latest changes that made v2.53.0.vfs.0.11 possible.
…d-port of #915) (#921) Forward-port of #915 ("checkout: preserve skip-worktree for virtual filesystem paths", merged into `vfs-2.53.0` as ea3eb21) to `vfs-2.54.0`. Cherry-picked from the underlying PR-branch commit d40f13b (so the original well-crafted commit message and Tyrie Vella's authorship are preserved). One auto-merge in `builtin/checkout.c` resolved cleanly without conflicts.
When Git is built under MSYS2/MinGW with Rust support enabled, the
Makefile expects `cargo build` to drop a `target/release/libgitcore.a`
that is linkable by the same MinGW GCC used for every other object.
With Rust installed via `rustup` (the way it ships on the
GitHub-hosted `windows-2022` and `windows-11-arm` runners that build
microsoft/git), the default toolchain targets the MSVC ABI; cargo
then writes `target/release/gitcore.lib` instead, which the MinGW
`ld.exe` cannot consume:
LINK git-shell.exe
D:\git-sdk-64-minimal\mingw64\bin/ld.exe: cannot find target/release/libgitcore.a: No such file or directory
collect2.exe: error: ld returned 1 exit status
See https://github.com/microsoft/git/actions/runs/27341625000 for the
full log.
Let's define the correct target. Re-use (and fix) the existing
`HOST_CPU` variable for that purpose. Avoid relying on environment
variables that are simply not defined in Git for Windows' minimal SDK
that Git uses in its CI runs.
Assisted-by: Claude Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The Windows runners used by `vfs-functional-tests.yml` ship `rustup` plus a `*-pc-windows-msvc` default toolchain (see https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md and https://github.com/actions/partner-runner-images/blob/main/images/arm-windows-11-image.md), but no precompiled `std` for `*-pc-windows-gnu` or `*-pc-windows-gnullvm`. With the Makefile now picking a GCC-compatible target triple based on `$(MSYSTEM)`, the build step needs that precompiled `std` to be installed before invoking `make`, otherwise `cargo build --target <triple>` fails to find a usable `std` for the chosen target. Add a step between the SDK setup and the `make` invocation that selects the matching triple from `$MSYSTEM` (which `git-for-windows/setup-git-for-windows-sdk` exports for every subsequent step) and runs `rustup target add` for it. The mapping mirrors what `config.mak.uname` derives from `$(MSYSTEM)` and `$(HOST_CPU)`, just enumerated explicitly here since CI has direct knowledge of which MSYS2 subsystems the matrix actually exercises (`CLANGARM64` for the ARM64 runner, `MINGW64` for the x86_64 runner). For a `staticlib` crate-type `cargo build` does not invoke an external linker, so no further toolchain components (e.g. the `gnullvm` LLVM linker) need to be installed; `rustup target add` alone is sufficient. Assisted-by: Claude Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
derrickstolee
approved these changes
Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Range-diff relative to v2.55.0-rc2.vfs.0.0
9: 51f364f = 1: c8fc5a2 sparse-index.c: fix use of index hashes in expand_index
10: b4eda55 = 2: 05a95ad t5300: confirm failure of git index-pack when non-idx suffix requested
12: 1864b21 = 3: 4705e95 t: remove advice from some tests
13: 18e1649 = 4: 23959ce t1092: add test for untracked files and directories
14: 8f46f0e = 5: 51bf99d index-pack: disable rev-index if index file has non .idx suffix
16: b06398a = 6: 59be980 trace2: prefetch value of GIT_TRACE2_DST_DEBUG at startup
1: 3c500fd = 7: 3a96a80 survey: calculate more stats on refs
2: ac61da8 = 8: 70c4057 survey: show some commits/trees/blobs histograms
3: 1cd999d = 9: 7a0e7a5 survey: add vector of largest objects for various scaling dimensions
4: 79e7fc2 = 10: cce6031 survey: add pathname of blob or tree to large_item_vec
5: 9d54480 = 11: 6ae68e1 survey: add commit-oid to large_item detail
6: a658f40 = 12: c45f59a survey: add commit name-rev lookup to each large_item
7: 5948e47 = 13: fe472de survey: add --no-name-rev option
8: f87c738 = 14: 98073ca survey: started TODO list at bottom of source file
11: 586f044 = 15: d034930 survey: expanded TODO list at the bottom of the source file
15: 3ffa992 = 16: 60aae5c survey: expanded TODO with more notes
17: 2899c15 = 17: e7baa92 reset --stdin: trim carriage return from the paths
18: 3116fcb ! 18: 3f93442 Identify microsoft/git via a distinct version suffix
19: fa9d6b3 = 19: c0019ec gvfs: ensure that the version is based on a GVFS tag
20: 2d0ea5f = 20: 2db17f6 gvfs: add a GVFS-specific header file
21: be2d670 = 21: 2541c2b gvfs: add the core.gvfs config setting
22: 8d9d1a7 = 22: fea2984 gvfs: add the feature to skip writing the index' SHA-1
23: 0425fe1 = 23: 910f887 gvfs: add the feature that blobs may be missing
24: 6a0701c = 24: 4e6ade6 gvfs: prevent files to be deleted outside the sparse checkout
25: f1841a5 = 25: e8d4c0c gvfs: optionally skip reachability checks/upload pack during fetch
26: f3dce21 = 26: 4fa282a gvfs: ensure all filters and EOL conversions are blocked
27: 316e3b1 = 27: 178e0a5 gvfs: allow "virtualizing" objects
28: 9ff001a = 28: 7e65dde Hydrate missing loose objects in check_and_freshen()
29: a8af0f9 = 29: 4c7f91a sha1_file: when writing objects, skip the read_object_hook
30: 32c8a80 = 30: 39c1754 gvfs: add global command pre and post hook procs
31: eaf70a4 = 31: 96f43a6 t0400: verify that the hook is called correctly from a subdirectory
32: e6fe914 = 32: c4ebeb4 t0400: verify core.hooksPath is respected by pre-command
33: 0173a8b = 33: a025fa9 Pass PID of git process to hooks.
34: c4c06b4 = 34: ad4f004 sparse-checkout: make sure to update files with a modify/delete conflict
35: 6117f33 = 35: ca60cb7 worktree: allow in Scalar repositories
36: 827c571 = 36: 80b71d8 sparse-checkout: avoid writing entries with the skip-worktree bit
37: 70b9277 = 37: 43241a4 Do not remove files outside the sparse-checkout
38: 432fe10 = 38: db40f09 send-pack: do not check for sha1 file when GVFS_MISSING_OK set
39: 0e1cb5e = 39: a5b4adf gvfs: allow corrupt objects to be re-downloaded
40: 9b640d9 = 40: 85c49ed cache-tree: remove use of strbuf_addf in update_one
41: 23d604c = 41: c1c5198 gvfs: block unsupported commands when running in a GVFS repo
42: e3cd6d1 = 42: bf67e47 gvfs: allow overriding core.gvfs
43: b832822 = 43: 92f98d5 BRANCHES.md: Add explanation of branches and using forks
49: 28cac31 = 44: 5808f51 Add virtual file system settings and hook proc
50: 0527dbd = 45: e097438 virtualfilesystem: don't run the virtual file system hook if the index has been redirected
51: e8b7b9d = 46: 6e86501 virtualfilesystem: check if directory is included
52: 621e84e = 47: 5dd8286 backwards-compatibility: support the post-indexchanged hook
53: ded3c1e = 48: 86ab76d gvfs: verify that the built-in FSMonitor is disabled
44: d8035a8 = 49: ac119a2 git.c: add VFS enabled cmd blocking
54: e966010 = 50: 04d948c wt-status: add trace2 data for sparse-checkout percentage
45: a7ee7df = 51: 39b0448 git.c: permit repack cmd in Scalar repos
55: bfa268f = 52: 7d7f617 status: add status serialization mechanism
46: 5989cdb = 53: 7fa2269 git.c: permit fsck cmd in Scalar repos
56: 3a8b996 = 54: 0ce937b Teach ahead-behind and serialized status to play nicely together
47: 6d8fd92 = 55: 9068cca git.c: permit prune cmd in Scalar repos
57: 3a30016 = 56: ba35418 status: serialize to path
48: f1c678a = 57: 8d716e2 worktree: remove special case GVFS cmd blocking
58: 59e6fe8 = 58: 796495c status: reject deserialize in V2 and conflicts
70: 043a00d = 59: 4b6cf43 builtin/repack.c: emit warning when shared cache is present
59: db20abc = 60: 2c83191 serialize-status: serialize global and repo-local exclude file metadata
60: 62d538c = 61: e0d756e status: deserialization wait
61: f74bb5f = 62: 66103c5 status: deserialize with -uno does not print correct hint
62: 25795a0 = 63: c2a37c9 fsmonitor: check CE_FSMONITOR_VALID in ce_uptodate
63: 9a1a644 = 64: 202ed70 fsmonitor: add script for debugging and update script for tests
64: c28cc68 = 65: b3213ee status: disable deserialize when verbose output requested.
65: e1bec4b = 66: 0cc6e57 t7524: add test for verbose status deserialzation
66: a472e90 = 67: 3896778 deserialize-status: silently fallback if we cannot read cache file
67: b4b4e1e = 68: c18a1ca gvfs:trace2:data: add trace2 tracing around read_object_process
68: b9fa20e = 69: 2152b3c gvfs:trace2:data: status deserialization information
69: 65333d0 = 70: d69c714 gvfs:trace2:data: status serialization
71: aa3f9d0 = 71: e93d1cd gvfs:trace2:data: add vfs stats
72: cb90eae = 72: ded7f62 trace2: refactor setting process starting time
73: ca5fd25 = 73: 1ac54a1 trace2:gvfs:experiment: report_tracking
74: c5fec05 = 74: c7ef01e trace2:gvfs:experiment: read_cache: annotate thread usage in read-cache
75: 8f67d92 = 75: 9420488 trace2:gvfs:experiment: read-cache: time read/write of cache-tree extension
76: 99e9885 = 76: 360d4d9 trace2:gvfs:experiment: add region to apply_virtualfilesystem()
77: 5592e81 = 77: 059cd69 trace2:gvfs:experiment: add region around unpack_trees()
78: 1f9299c = 78: 272d18f trace2:gvfs:experiment: add region to cache_tree_fully_valid()
79: 218f463 = 79: 7cc631c trace2:gvfs:experiment: add unpack_entry() counter to unpack_trees() and report_tracking()
80: 2ce3876 = 80: 69190af trace2:gvfs:experiment: increase default event depth for unpack-tree data
81: f1a87b6 = 81: 134271c trace2:gvfs:experiment: add data for check_updates() in unpack_trees()
82: b44f6ac = 82: 283ad1a Trace2:gvfs:experiment: capture more 'tracking' details
83: 1d91434 = 83: c64111d credential: set trace2_child_class for credential manager children
84: d93bf8e = 84: 4404bff sub-process: do not borrow cmd pointer from caller
85: 3142803 = 85: cddbc3f sub-process: add subprocess_start_argv()
86: d97ce7b = 86: 2c6dd9e sha1-file: add function to update existing loose object cache
87: 4b144c7 = 87: 38c9857 index-pack: avoid immediate object fetch while parsing packfile
88: 465f901 = 88: 4ff7b00 gvfs-helper: create tool to fetch objects using the GVFS Protocol
89: 698b478 = 89: 88dd0e5 sha1-file: create shared-cache directory if it doesn't exist
90: 4eb3f3a = 90: ab715cb gvfs-helper: better handling of network errors
91: f911658 = 91: d58bf5e gvfs-helper-client: properly update loose cache with fetched OID
92: 92b1a15 = 92: e4e032d gvfs-helper: V2 robust retry and throttling
93: e71c8ea = 93: 83cbcb1 gvfs-helper: expose gvfs/objects GET and POST semantics
94: b961d73 = 94: 37c8d8f gvfs-helper: dramatically reduce progress noise
95: 182821c = 95: 97641e4 gvfs-helper: handle pack-file after single POST request
96: fb8591f = 96: be8762e test-gvfs-prococol, t5799: tests for gvfs-helper
97: 689216e = 97: 1cbab9e gvfs-helper: move result-list construction into install functions
98: 6ad7bdd = 98: 40ab73b t5799: add support for POST to return either a loose object or packfile
99: 7da9541 = 99: 77afaf2 t5799: cleanup wc-l and grep-c lines
100: a0a872f = 100: 4a1e85f gvfs-helper: verify loose objects after write
101: 2d27776 = 101: 59438b5 t7599: create corrupt blob test
102: 356d701 = 102: fe76797 gvfs-helper: add prefetch support
103: d3a866a = 103: 8e2b8a7 gvfs-helper: add prefetch .keep file for last packfile
104: ee468c6 = 104: 75280df gvfs-helper: do one read in my_copy_fd_len_tail()
105: 3cae7a0 = 105: 2fb6021 gvfs-helper: move content-type warning for prefetch packs
106: f9cc961 = 106: d72feaa fetch: use gvfs-helper prefetch under config
107: 2233960 = 107: f162507 gvfs-helper: better support for concurrent packfile fetches
108: 2127287 = 108: e274b65 remote-curl: do not call fetch-pack when using gvfs-helper
109: 2de9f7f = 109: 1e27016 fetch: reprepare packs before checking connectivity
110: 4c418a0 = 110: d8b6330 gvfs-helper: retry when creating temp files
111: 8cc6b71 = 111: a225fd0 sparse: avoid warnings about known cURL issues in gvfs-helper.c
112: a7c166c = 112: 4639c63 gvfs-helper: add --max-retries to prefetch verb
113: 581b65f = 113: 2961f9e t5799: add tests to detect corrupt pack/idx files in prefetch
114: 7ed5199 = 114: 22c6486 gvfs-helper: ignore .idx files in prefetch multi-part responses
116: 8ce092e = 115: c81f6fd t5799: explicitly test gvfs-helper --fallback and --no-fallback
118: 676d7f7 = 116: 4266eaa gvfs-helper: don't fallback with new config
120: e0343eb = 117: 3ef5217 test-gvfs-protocol: add cache_http_503 to mayhem
115: 9b132ed = 118: c246a5d maintenance: care about gvfs.sharedCache config
122: 335d6fc = 119: 5a9a265 t5799: add unit tests for new
gvfs.fallbackconfig setting117: 291a9a6 = 120: 5efced6 unpack-trees:virtualfilesystem: Improve efficiency of clear_ce_flags
119: 6c9a09b = 121: e0c5167 homebrew: add GitHub workflow to release Cask
121: 28a3dd3 = 122: 6560eff Adding winget workflows
123: a6ba11d = 123: e3417fa Disable the
monitor-componentsworkflow in msft-git124: e091ceb = 124: 7ae2bba .github: enable windows builds on microsoft fork
125: 4ee6f0b = 125: e044ae6 .github/actions/akv-secret: add action to get secrets
126: 6205d7b = 126: db9cc31 release: create initial Windows installer build workflow
127: c3a5335 = 127: 4e3e75d help: special-case HOST_CPU
universal128: b8a11cc = 128: d533a77 release: add Mac OSX installer build
129: ef30051 = 129: bf1e49d release: build unsigned Ubuntu .deb package
130: 2e21a93 = 130: cbbdfc8 release: add signing step for .deb package
132: d14c26c = 131: 22d7862 release: create draft GitHub release with packages & installers
134: 22509ac = 132: 7a03260 build-git-installers: publish gpg public key
136: 7797dd8 = 133: 0904361 release: continue pestering until user upgrades
131: b39d616 = 134: 691ddd0 update-microsoft-git: create barebones builtin
139: 32fce03 = 135: 810cf31 dist: archive HEAD instead of HEAD^{tree}
133: 17378eb = 136: dd99d23 update-microsoft-git: Windows implementation
142: a92e1cc = 137: ef3279a release: include GIT_BUILT_FROM_COMMIT in MacOS build
135: 9646fd0 = 138: e58416e update-microsoft-git: use brew on macOS
145: 0cc5b9f = 139: 9f61567 release: add installer validation
137: a40547f = 140: 43da41b git_config_set_multivar_in_file_gently(): add a lock timeout
140: 228d049 = 141: 12a8aa2 scalar: set the config write-lock timeout to 150ms
143: 7551502 = 142: efbd18c scalar: add docs from microsoft/scalar
138: 3b305bf = 143: 6ec1e1b .github: reinstate ISSUE_TEMPLATE.md for microsoft/git
146: 0c298a4 = 144: 48ef38e scalar (Windows): use forward slashes as directory separators
141: ebfeba5 = 145: 53b4908 .github: update PULL_REQUEST_TEMPLATE.md
147: 7065e24 = 146: 9411981 scalar: add retry logic to run_git()
144: 19491a7 = 147: 790e547 Adjust README.md for microsoft/git
148: c74bdf9 = 148: 3a242e4 scalar: support the
configcommand for backwards compatibility149: b4e4313 = 149: 9676f63 scalar: implement a minimal JSON parser
150: 8c20221 = 150: d680224 scalar clone: support GVFS-enabled remote repositories
151: 6422abc = 151: ff853b4 test-gvfs-protocol: also serve smart protocol
152: aee680d = 152: 4d11773 gvfs-helper: add the
endpointcommand153: 807f366 = 153: ba2c7c3 dir_inside_of(): handle directory separators correctly
154: e82e538 = 154: 2a13a39 scalar: disable authentication in unattended mode
155: 717ed79 = 155: 1b4d513 abspath: make strip_last_path_component() global
156: d0ac1ad = 156: 55226d1 scalar: do initialize
gvfs.sharedCache157: d27e489 = 157: 0cebd3d scalar diagnose: include shared cache info
158: 41fd12c = 158: 8bfa488 scalar: only try GVFS protocol on https:// URLs
159: 461aef7 = 159: 77fcdc7 scalar: verify that we can use a GVFS-enabled repository
160: 3888503 = 160: f34397a scalar: add the
cache-servercommand161: b3c328a = 161: abe682c scalar: add a test toggle to skip accessing the vsts/info endpoint
162: a6364ca = 162: 214803a scalar: adjust documentation to the microsoft/git fork
163: 70e2ec4 = 163: 0f5f72b scalar: enable untracked cache unconditionally
164: f084e7d = 164: 8cacb6b scalar: parse
clone --no-fetch-commits-and-treesfor backwards compatibility165: e490ca1 = 165: 5436ac8 scalar: make GVFS Protocol a forced choice
166: 902169f = 166: 182ec1c scalar: work around GVFS Protocol HTTP/2 failures
167: f1dc791 = 167: 4aa2b23 gvfs-helper-client: clean up server process(es)
169: ca97330 = 168: 236cdb2 scalar diagnose: accommodate Scalar's Functional Tests
171: 256a735 = 169: 80777af ci: run Scalar's Functional Tests
168: 3900fbe = 170: bda6599 add/rm: allow adding sparse entries when virtual
173: 6a9f26b = 171: 6ffbe4c scalar: upgrade to newest FSMonitor config setting
170: 9f83459 = 172: 723152f sparse-checkout: add config to disable deleting dirs
172: a04f0ee = 173: 1911816 diff: ignore sparse paths in diffstat
174: f4ae471 = 174: 685b656 repo-settings: enable sparse index by default
175: 353c643 = 175: 5d50249 TO-UPSTREAM: sequencer: avoid progress when stderr is redirected
176: 929d7c1 = 176: 8fd9544 TO-CHECK: t1092: use quiet mode for rebase tests
177: b289adf = 177: c9d1123 reset: fix mixed reset when using virtual filesystem
178: 7789928 = 178: ad67805 diff(sparse-index): verify with partially-sparse
179: f1cb9fd = 179: b25ab9a stash: expand testing for
git stash -u180: f8c09e3 = 180: 3c7d3e8 sparse-index: add ensure_full_index_with_reason()
181: ba880c4 = 181: 67ab008 treewide: add reasons for expanding index
182: 464e7e6 = 182: f0dcb27 treewide: custom reasons for expanding index
183: 104120c = 183: c94d991 sparse-index: add macro for unaudited expansions
184: 873bcf7 = 184: 3e02eaf Docs: update sparse index plan with logging
185: 569e244 = 185: f4654eb sparse-index: log failure to clear skip-worktree
186: 4e06c5c = 186: 52f1b9d stash: use -f in checkout-index child process
187: cfc7b63 = 187: ba03c72 sparse-index: do not copy hashtables during expansion
188: 6baaf5f = 188: a0366b9 TO-UPSTREAM: sub-process: avoid leaking
cmd189: 32b970b = 189: 14117b7 remote-curl: release filter options before re-setting them
190: b8a3ca5 = 190: c80c493 transport: release object filter options
191: b657a40 = 191: 416a515 push: don't reuse deltas with path walk
192: d69ad7e = 192: 3b37512 t7900-maintenance.sh: reset config between tests
193: 48b4e4b = 193: 7a5af41 maintenance: add cache-local-objects maintenance task
194: 9ce8142 = 194: 13e557f scalar.c: add cache-local-objects task
195: 9242922 = 195: 07b4d06 hooks: add custom post-command hook config
198: 88d266f = 196: 2be625e TO-UPSTREAM: Docs: fix asciidoc failures from short delimiters
201: 69ac713 = 197: 233cfcb hooks: make hook logic memory-leak free
204: 9cea9e7 = 198: 04afdcf t0401: test post-command for alias, version, typo
197: 4330937 = 199: 8454287 cat_one_file(): make it easy to see that the
sizevariable is initialized207: 48c607c = 200: 63dd7fa hooks: better handle config without gitdir
196: 8206832 = 201: 08c6fe8 revision: defensive programming
199: 0c20a75 = 202: 0ede4f0 get_parent(): defensive programming
202: 675e792 = 203: d1559b6 fetch-pack: defensive programming
208: 92073a8 = 204: 6b367b9 codeql: run static analysis as part of CI builds
205: caefd37 = 205: 6458e23 unparse_commit(): defensive programming
209: 4db5652 = 206: f7d940e codeql: publish the sarif file as build artifact
210: 15376eb = 207: 7396e03 verify_commit_graph(): defensive programming
211: 047038e = 208: 708febc codeql: disable a couple of non-critical queries for now
212: 642bf89 = 209: 59e1d5f stash: defensive programming
213: 0badfee = 210: 5242574 date: help CodeQL understand that there are no leap-year issues here
214: f318ed0 = 211: 1a1ed99 stash: defensive programming
200: 5a77ee7 = 212: 3dc3f08 fsck: avoid using an uninitialized variable
215: 743c1f1 = 213: 47a2e33 help: help CodeQL understand that consuming envvars is okay here
216: d51a383 = 214: 3b147ac push: defensive programming
203: ff6b700 = 215: dfd98c7 load_revindex_from_disk(): avoid accessing uninitialized data
217: ddd3abd = 216: cb2a751 ctype: help CodeQL understand that
sane_istest()does not access array past end218: ba26a1d = 217: b340977 test-tool repository: check return value of
lookup_commit()219: eb87f53 = 218: c49b40b fetch: defensive programming
206: 4bc301a = 219: 40d8927 load_pack_mtimes_file(): avoid accessing uninitialized data
220: ba4134d = 220: 629c2d7 ctype: accommodate for CodeQL misinterpreting the
zinmallocz()221: d93ead2 = 221: 989f755 shallow: handle missing shallow commits gracefully
222: 4e36459 = 222: 3c3c954 inherit_tracking(): defensive programming
223: 77e6ece = 223: c3ea9ed strbuf_read: help with CodeQL misunderstanding that
strbuf_read()does NUL-terminate correctly224: 3d67a46 = 224: a84a0a5 commit-graph: suppress warning about using a stale stack addresses
225: 96abc85 = 225: 2745bbf codeql: also check JavaScript code
226: a8df079 = 226: e9a6793 scalar: add run_git_argv
227: 494c601 = 227: 0613e00 scalar: add --ref-format option to scalar clone
228: 8d5beaa = 228: 6a0d2da gvfs-helper: skip collision check for loose objects
229: 520c0b2 = 229: 3dc9149 gvfs-helper: emit advice on transient errors
230: cb4b159 = 230: b4fd903 gvfs-helper: avoid collision check for packfiles
231: e061225 = 231: 75845f4 t5799: update cache-server methods for multiple instances
232: 49c1ccf = 232: fd6407c gvfs-helper: override cache server for prefetch
233: c2a49d2 = 233: b4668fc gvfs-helper: override cache server for get
234: a5d50f0 = 234: 97eae2e gvfs-helper: override cache server for post
235: decb193 = 235: 92a9687 t5799: add test for all verb-specific cache-servers together
236: 93c46fb = 236: 548af29 lib-gvfs-helper: create helper script for protocol tests
237: 9b11c37 = 237: 27de0de t579*: split t5799 into several parts
238: 39ca95c = 238: 0bff5ab scalar: add ---cache-server-url options
239: c52080b = 239: 06616a4 Restore previous errno after post command hook
240: 84c218a = 240: 1d9095a t9210: differentiate origin and cache servers
241: 178eb27 = 241: 6f693c2 unpack-trees: skip lstats for deleted VFS entries in checkout
242: b909f7b = 242: 8f83a69 worktree: conditionally allow worktree on VFS-enabled repos
243: 1b08bff = 243: 5b6b46c gvfs-helper: send X-Session-Id headers
244: 592355f = 244: 17848a9 gvfs-helper: create shared object cache if missing
245: b82adba = 245: 4c41212 gvfs: add gvfs.sessionKey config
246: fb228dc = 246: bed370f gvfs: clear DIE_IF_CORRUPT in streaming incore fallback
247: f9bdf8b = 247: 241f1c4 workflow: add release-vfsforgit to automate VFS for Git updates
248: de21bf3 = 248: 21ae6a1 worktree remove: use GVFS_SUPPORTS_WORKTREES for skip-clean-check gate
249: 3e13db5 = 249: a4a48d4 ci: add new VFS for Git functional tests workflow
250: 9d8d455 = 250: a771e02 azure-pipelines: add stub release pipeline for Azure
251: 89f9c1c = 251: e551e62 diff: add renameThreshold configuration option
252: 9d0b6a2 = 252: 4cade5c gvfs-helper: separate packfile extraction from indexing
253: d9fd43c = 253: 32936ce blame: add blame.renames, blame.renameThreshold, blame.renameLimit
254: 26b7c85 = 254: 5b7027b gvfs-helper: run prefetch index-pack in parallel
255: cbf5822 = 255: 355cd4a gvfs-helper: add gvfs.prefetchThreads config for parallel prefetch
256: 63f0ad8 = 256: 2e8febb azure-pipelines: add ESRP code signing
257: 289c6cd = 257: 41500d3 azure-pipelines: allow overriding Git version
258: 6a728b1 = 258: 08a9f59 azure-pipelines: build, sign and stage the Linux Debian package
259: 762cd88 = 259: 02e2d8e azure-pipelines: build, sign, notarize and stage the macOS installer
260: 5428ff5 = 260: c4ded8c azure-pipelines: build, sign and stage the Windows installer
261: abd35b5 = 261: 8a204a2 azure-pipelines: enable on tag push, default ESRP and GitHub release on
262: 0d0d352 = 262: 61ff841 release: binskim for Windows
263: 100c65e = 263: 3f15f8c release: suppress unfixable binskim findings
264: 5f056c5 = 264: 41d240c binskim: add baseline
265: 1a9b27a = 265: b7252dd checkout: preserve skip-worktree for virtual filesystem paths
266: 4cd72e5 = 266: e96535a rust: pick a GCC-compatible Cargo target under MSYS2/MinGW
267: 72e1420 = 267: de444c4 ci(vfs): install the GCC-compatible Rust target before building
268: 450e47e < -: ------------ DEBUG