Skip to content

Rebase onto Git for Windows v2.55.0#943

Merged
dscho merged 337 commits into
vfs-2.55.0from
tentative/vfs-2.55.0
Jun 30, 2026
Merged

Rebase onto Git for Windows v2.55.0#943
dscho merged 337 commits into
vfs-2.55.0from
tentative/vfs-2.55.0

Conversation

@dscho

@dscho dscho commented Jun 29, 2026

Copy link
Copy Markdown
Member
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

    @@ Commit message
      ## GIT-VERSION-GEN ##
     @@
      
    - DEF_VER=v2.55.0-rc2
    + DEF_VER=v2.55.0
      
     +# Identify microsoft/git via a distinct version suffix
     +DEF_VER=$DEF_VER.vfs.0.0
  • 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.fallback config setting

  • 117: 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-components workflow in msft-git

  • 124: 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 universal

  • 128: 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 config command for backwards compatibility

  • 149: 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 endpoint command

  • 153: 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.sharedCache

  • 157: 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-server command

  • 161: 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-trees for backwards compatibility

  • 165: 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 -u

  • 180: 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 cmd

  • 189: 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 size variable is initialized

  • 207: 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 end

  • 218: 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 z in mallocz()

  • 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 correctly

  • 224: 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

jeffhostetler and others added 30 commits June 29, 2026 19:10
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>
mjcheetham and others added 26 commits June 29, 2026 19:10
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>
@dscho dscho self-assigned this Jun 29, 2026
@dscho dscho merged commit de444c4 into vfs-2.55.0 Jun 30, 2026
222 of 224 checks passed
@dscho dscho deleted the tentative/vfs-2.55.0 branch June 30, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants