Skip to content

Conversation

@dscho
Copy link
Member

@dscho dscho commented Jan 20, 2026

This PR rebases Microsoft Git patches onto Git for Windows v2.53.0-rc0.windows.1.

Previous base: vfs-2.52.0

Range-diff vs vfs-2.52.0
  • 8: 1ffd4c8 = 1: a6f0631 sparse-index.c: fix use of index hashes in expand_index
  • 11: 92fdb68 = 2: b82fe7c t: remove advice from some tests
  • 1: 59e95ca = 3: b2ceffc survey: calculate more stats on refs
  • 2: 9d5f9e4 = 4: 240a579 survey: show some commits/trees/blobs histograms
  • 3: 4c586cb = 5: 063b6ea survey: add vector of largest objects for various scaling dimensions
  • 4: f35b3e7 = 6: ddb0a79 survey: add pathname of blob or tree to large_item_vec
  • 10: a2cc235 = 7: 0bae99d t5300: confirm failure of git index-pack when non-idx suffix requested
  • 5: 7d41254 = 8: 3c916c2 survey: add commit-oid to large_item detail
  • 12: 4ae9cbd = 9: e57b704 t1092: add test for untracked files and directories
  • 14: d930723 = 10: 72e4483 index-pack: disable rev-index if index file has non .idx suffix
  • 15: 9c89875 = 11: 5c62d56 trace2: prefetch value of GIT_TRACE2_DST_DEBUG at startup
  • 6: 338d9da = 12: 2740e01 survey: add commit name-rev lookup to each large_item
  • 7: e8a6751 = 13: 276c24e survey: add --no-name-rev option
  • 9: 81d5530 = 14: d3cc1bc survey: started TODO list at bottom of source file
  • 13: 43f0d81 = 15: 11d4ad5 survey: expanded TODO list at the bottom of the source file
  • 16: bee35c4 = 16: 4f85852 survey: expanded TODO with more notes
  • 17: 9039ffe = 17: 513e3a1 reset --stdin: trim carriage return from the paths
  • 18: 8eab8ac ! 18: 03fa729 Identify microsoft/git via a distinct version suffix
    @@ Commit message
      ## GIT-VERSION-GEN ##
     @@
      
    - DEF_VER=v2.52.0
    + DEF_VER=v2.53.0-rc0
      
     +# Identify microsoft/git via a distinct version suffix
     +DEF_VER=$DEF_VER.vfs.0.0
  • 19: c74ee39 = 19: 457da1e gvfs: ensure that the version is based on a GVFS tag
  • 20: 33a392a = 20: b8de5eb gvfs: add a GVFS-specific header file
  • 21: 500d070 = 21: 1f59c8f gvfs: add the core.gvfs config setting
  • 22: ff1fe6d = 22: c011756 gvfs: add the feature to skip writing the index' SHA-1
  • 23: adb520c = 23: 9f4ad2e gvfs: add the feature that blobs may be missing
  • 24: d673ccf = 24: 2439f17 gvfs: prevent files to be deleted outside the sparse checkout
  • 25: 2ffe857 = 25: 3fa6112 gvfs: optionally skip reachability checks/upload pack during fetch
  • 26: 90b7929 = 26: 9f7fe44 gvfs: ensure all filters and EOL conversions are blocked
  • 27: dace480 ! 27: 2130c0f gvfs: allow "virtualizing" objects
    @@ odb.c
      #include "lockfile.h"
      #include "loose.h"
     @@
    - #include "strvec.h"
      #include "submodule.h"
    + #include "tmp-objdir.h"
      #include "trace2.h"
     +#include "trace.h"
      #include "write-or-die.h"
    @@ odb.c: void disable_obj_read_lock(void)
      
      static int register_all_submodule_sources(struct object_database *odb)
     @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 	int rtype;
    + 	const struct cached_object *co;
      	const struct object_id *real = oid;
      	int already_retried = 0;
     +	int tried_hook = 0;
      
    - 
      	if (flags & OBJECT_INFO_LOOKUP_REPLACE)
    + 		real = lookup_replace_object(odb->repo, oid);
     @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 	if (!oi)
    - 		oi = &blank_oi;
    + 	if (is_null_oid(real))
    + 		return -1;
      
     +retry:
      	co = find_cached_object(odb, real);
      	if (co) {
    - 		if (oi->typep)
    + 		if (oi) {
     @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
      			odb_reprepare(odb->repo->objects);
    - 			if (find_pack_entry(odb->repo, real, &e))
    - 				break;
    + 			if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
    + 				return 0;
     +			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
     +				tried_hook = 1;
     +				if (!run_read_object_hook(odb->repo, oid))
  • 28: f5da48f ! 28: b53f484 Hydrate missing loose objects in check_and_freshen()
    @@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
     +	return ret;
      }
      
    - int has_loose_object(struct odb_source *source,
    + int odb_source_loose_has_object(struct odb_source *source,
     
      ## odb.c ##
     @@
    @@ odb.c
      #include "strvec.h"
     +#include "sub-process.h"
      #include "submodule.h"
    + #include "tmp-objdir.h"
      #include "trace2.h"
    - #include "trace.h"
     @@ odb.c: int odb_has_alternates(struct object_database *odb)
      	return !!odb->sources->next;
      }
    @@ odb.c: void disable_obj_read_lock(void)
      
      static int register_all_submodule_sources(struct object_database *odb)
     @@ odb.c: retry:
    - 				break;
    + 				return 0;
      			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
      				tried_hook = 1;
     -				if (!run_read_object_hook(odb->repo, oid))
    @@ odb.c: retry:
      		}
     
      ## odb.h ##
    -@@ odb.h: static inline int odb_write_object(struct object_database *odb,
    - 	return odb_write_object_ext(odb, buf, len, type, oid, NULL, 0);
    - }
    +@@ odb.h: int odb_write_object_stream(struct object_database *odb,
    + 			    struct odb_write_stream *stream, size_t len,
    + 			    struct object_id *oid);
      
     +int read_object_process(struct repository *r, const struct object_id *oid);
     +
  • 29: 1eb8c1b ! 29: 20dc547 sha1_file: when writing objects, skip the read_object_hook
    @@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
      		if (!read_object_process(source->odb->repo, oid))
      			goto retry;
     @@ object-file.c: retry:
    - int has_loose_object(struct odb_source *source,
    - 		     const struct object_id *oid)
    + int odb_source_loose_has_object(struct odb_source *source,
    + 				const struct object_id *oid)
      {
     -	return check_and_freshen_source(source, oid, 0);
     +	return check_and_freshen_source(source, oid, 0, 0);
    @@ object-file.c: retry:
     @@ object-file.c: static int write_loose_object(struct odb_source *source,
      }
      
    - static int freshen_loose_object(struct object_database *odb,
    --				const struct object_id *oid)
    -+				const struct object_id *oid,
    -+				int skip_virtualized_objects)
    + int odb_source_loose_freshen_object(struct odb_source *source,
    +-				    const struct object_id *oid)
    ++				    const struct object_id *oid,
    ++				    int skip_virtualized_objects)
      {
    - 	odb_prepare_alternates(odb);
    - 	for (struct odb_source *source = odb->sources; source; source = source->next)
    --		if (check_and_freshen_source(source, oid, 1))
    -+		if (check_and_freshen_source(source, oid, 1, skip_virtualized_objects))
    - 			return 1;
    - 	return 0;
    +-	return !!check_and_freshen_source(source, oid, 1);
    ++	return !!check_and_freshen_source(source, oid, 1, skip_virtualized_objects);
      }
    -@@ object-file.c: int stream_loose_object(struct odb_source *source,
    + 
    + int odb_source_loose_write_stream(struct odb_source *source,
    +@@ object-file.c: int odb_source_loose_write_stream(struct odb_source *source,
    + 		die(_("deflateEnd on stream object failed (%d)"), ret);
      	close_loose_object(source, fd, tmp_file.buf);
      
    - 	if (freshen_packed_object(source->odb, oid) ||
    --	    freshen_loose_object(source->odb, oid)) {
    -+	    freshen_loose_object(source->odb, oid, 1)) {
    +-	if (odb_freshen_object(source->odb, oid)) {
    ++	if (odb_freshen_object(source->odb, oid, 1)) {
      		unlink_or_warn(tmp_file.buf);
      		goto cleanup;
      	}
    -@@ object-file.c: int write_object_file(struct odb_source *source,
    +@@ object-file.c: int odb_source_loose_write_object(struct odb_source *source,
    + 	 * it out into .git/objects/??/?{38} file.
      	 */
      	write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
    - 	if (freshen_packed_object(source->odb, oid) ||
    --	    freshen_loose_object(source->odb, oid))
    -+	    freshen_loose_object(source->odb, oid, 1))
    +-	if (odb_freshen_object(source->odb, oid))
    ++	if (odb_freshen_object(source->odb, oid, 1))
      		return 0;
      	if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
      		return -1;
     
    + ## object-file.h ##
    +@@ object-file.h: int odb_source_loose_has_object(struct odb_source *source,
    + 				const struct object_id *oid);
    + 
    + int odb_source_loose_freshen_object(struct odb_source *source,
    +-				    const struct object_id *oid);
    ++				    const struct object_id *oid,
    ++				    int skip_virtualized_objects);
    + 
    + int odb_source_loose_write_object(struct odb_source *source,
    + 				  const void *buf, size_t len,
    +
    + ## odb.c ##
    +@@ odb.c: int odb_has_object(struct object_database *odb, const struct object_id *oid,
    + }
    + 
    + int odb_freshen_object(struct object_database *odb,
    +-		       const struct object_id *oid)
    ++		       const struct object_id *oid,
    ++		       int skip_virtualized_objects)
    + {
    + 	struct odb_source *source;
    + 
    +@@ odb.c: int odb_freshen_object(struct object_database *odb,
    + 
    + 	odb_prepare_alternates(odb);
    + 	for (source = odb->sources; source; source = source->next)
    +-		if (odb_source_loose_freshen_object(source, oid))
    ++		if (odb_source_loose_freshen_object(source, oid, skip_virtualized_objects))
    + 			return 1;
    + 
    + 	return 0;
    +
    + ## odb.h ##
    +@@ odb.h: int odb_has_object(struct object_database *odb,
    + 		   unsigned flags);
    + 
    + int odb_freshen_object(struct object_database *odb,
    +-		       const struct object_id *oid);
    ++		       const struct object_id *oid,
    ++		       int skip_virtualized_objects);
    + 
    + void odb_assert_oid_type(struct object_database *odb,
    + 			 const struct object_id *oid, enum object_type expect);
    +
      ## t/t0410/read-object ##
     @@ t/t0410/read-object: while (1) {
      		system ('git --git-dir="' . $DIR . '" cat-file blob ' . $sha1 . ' | git -c core.virtualizeobjects=false hash-object -w --stdin >/dev/null 2>&1');
  • 30: c852173 = 30: 7ceb2dc gvfs: add global command pre and post hook procs
  • 31: f9c2455 = 31: 82dc6dd t0400: verify that the hook is called correctly from a subdirectory
  • 32: a873609 = 32: 1ee3af2 t0400: verify core.hooksPath is respected by pre-command
  • 33: f393e0b = 33: 13cdc21 Pass PID of git process to hooks.
  • 34: 23b48a2 = 34: bd5893b sparse-checkout: make sure to update files with a modify/delete conflict
  • 35: 6cc5cdd = 35: b5a1a09 worktree: allow in Scalar repositories
  • 36: e03e55f = 36: d152d18 sparse-checkout: avoid writing entries with the skip-worktree bit
  • 37: d6b899a = 37: 03c58f0 Do not remove files outside the sparse-checkout
  • 38: 2599d83 = 38: 9b2b802 send-pack: do not check for sha1 file when GVFS_MISSING_OK set
  • 39: d3a9c83 = 39: d87c305 gvfs: allow corrupt objects to be re-downloaded
  • 40: 71445fe = 40: 6995c9a cache-tree: remove use of strbuf_addf in update_one
  • 41: fd65a3a = 41: ac57dde gvfs: block unsupported commands when running in a GVFS repo
  • 42: d926c79 = 42: 4445b46 gvfs: allow overriding core.gvfs
  • 43: ac21b75 = 43: 2a46a85 BRANCHES.md: Add explanation of branches and using forks
  • 44: 64cbd4e = 44: b4d416b git.c: add VFS enabled cmd blocking
  • 45: ed93bfd = 45: ec9bf55 git.c: permit repack cmd in Scalar repos
  • 46: 83a9855 = 46: c918576 git.c: permit fsck cmd in Scalar repos
  • 47: 109925d = 47: 9f10408 git.c: permit prune cmd in Scalar repos
  • 48: 82f3cf2 = 48: 1891beb worktree: remove special case GVFS cmd blocking
  • 50: eebb8e2 = 49: e249519 Add virtual file system settings and hook proc
  • 49: 4e55278 = 50: 3aa5279 builtin/repack.c: emit warning when shared cache is present
  • 51: cba0dcb = 51: da7c6aa virtualfilesystem: don't run the virtual file system hook if the index has been redirected
  • 52: b97a594 = 52: 3f575f4 virtualfilesystem: check if directory is included
  • 53: c63215d = 53: a85109e backwards-compatibility: support the post-indexchanged hook
  • 54: df8fddc = 54: 1c5c9de gvfs: verify that the built-in FSMonitor is disabled
  • 55: 0afb94e = 55: e29fe34 wt-status: add trace2 data for sparse-checkout percentage
  • 56: 6f4ff2d ! 56: 6d27bae status: add status serialization mechanism
    @@ Documentation/git-status.adoc: ignored, then the directory is not shown, but all
      	threshold.
      	See also linkgit:git-diff[1] `--find-renames`.
      
    -+--serialize[=<version>]::
    ++`--serialize[=<version>]`::
     +	(EXPERIMENTAL) Serialize raw status results to stdout in a
     +	format suitable for use by `--deserialize`.  Valid values for
     +	`<version>` are "1" and "v1".
     +
    -+--deserialize[=<path>]::
    ++`--deserialize[=<path>]`::
     +	(EXPERIMENTAL) Deserialize raw status results from a file or
     +	stdin rather than scanning the worktree.  If `<path>` is omitted
     +	and `status.deserializePath` is unset, input is read from stdin.
    -+--no-deserialize::
    ++`--no-deserialize`::
     +	(EXPERIMENTAL) Disable implicit deserialization of status results
     +	from the value of `status.deserializePath`.
     +
    - <pathspec>...::
    + `<pathspec>...`::
      	See the 'pathspec' entry in linkgit:gitglossary[7].
      
     @@ Documentation/git-status.adoc: quoted as explained for the configuration variable `core.quotePath`
  • 57: c30ac09 = 57: 4544b3a Teach ahead-behind and serialized status to play nicely together
  • 58: 559ff2b ! 58: 55248ed status: serialize to path
    @@ Documentation/git-status.adoc: ignored, then the directory is not shown, but all
      	threshold.
      	See also linkgit:git-diff[1] `--find-renames`.
      
    ----serialize[=<version>]::
    +-`--serialize[=<version>]`::
     -	(EXPERIMENTAL) Serialize raw status results to stdout in a
     -	format suitable for use by `--deserialize`.  Valid values for
     -	`<version>` are "1" and "v1".
    -+--serialize[=<path>]::
    ++`--serialize[=<path>]`::
     +	(EXPERIMENTAL) Serialize raw status results to a file or stdout
     +	in a format suitable for use by `--deserialize`.  If a path is
     +	given, serialize data will be written to that path *and* normal
     +	status output will be written to stdout.  If path is omitted,
     +	only binary serialization data will be written to stdout.
      
    - --deserialize[=<path>]::
    + `--deserialize[=<path>]`::
      	(EXPERIMENTAL) Deserialize raw status results from a file or
     
      ## builtin/commit.c ##
  • 59: f768ecf = 59: 8f94edb status: reject deserialize in V2 and conflicts
  • 60: 781af8f = 60: 1fce7d4 serialize-status: serialize global and repo-local exclude file metadata
  • 61: 0966f1d = 61: 6065b54 status: deserialization wait
  • 62: 6dc05f1 = 62: 28f26d7 status: deserialize with -uno does not print correct hint
  • 63: 08ae962 = 63: f04c3e9 fsmonitor: check CE_FSMONITOR_VALID in ce_uptodate
  • 64: e4668c7 = 64: 2eb4871 fsmonitor: add script for debugging and update script for tests
  • 65: 2df56fa = 65: d83b684 status: disable deserialize when verbose output requested.
  • 66: 72e22f9 = 66: d8c7ee4 t7524: add test for verbose status deserialzation
  • 67: 4185ec0 = 67: 810f9e7 deserialize-status: silently fallback if we cannot read cache file
  • 68: ed646e5 ! 68: 69ca216 gvfs:trace2:data: add trace2 tracing around read_object_process
    @@ Commit message
     
      ## odb.c ##
     @@
    - #include "submodule.h"
    + #include "tmp-objdir.h"
      #include "trace2.h"
      #include "trace.h"
     +#include "trace2.h"
  • 69: 9eae94a = 69: 5b00bd5 gvfs:trace2:data: status deserialization information
  • 70: 0aeef5f = 70: a6f3d71 gvfs:trace2:data: status serialization
  • 71: 7c4d0e2 = 71: d7b3b60 gvfs:trace2:data: add vfs stats
  • 72: 8f1130a = 72: d38d034 trace2: refactor setting process starting time
  • 73: 9138bf5 = 73: 0682fc7 trace2:gvfs:experiment: clear_ce_flags_1
  • 74: c4db737 ! 74: 7a24f9d trace2:gvfs:experiment: report_tracking
    @@ builtin/checkout.c: static void update_refs_for_switch(const struct checkout_opt
     +	}
      }
      
    - static int add_pending_uninteresting_ref(const char *refname, const char *referent UNUSED,
    + static int add_pending_uninteresting_ref(const struct reference *ref, void *cb_data)
  • 75: 58a06e1 = 75: 5708651 trace2:gvfs:experiment: read_cache: annotate thread usage in read-cache
  • 76: ca92d61 = 76: db6c6d5 trace2:gvfs:experiment: read-cache: time read/write of cache-tree extension
  • 77: f905905 = 77: 18a690b trace2:gvfs:experiment: add region to apply_virtualfilesystem()
  • 78: 16bf262 = 78: 1eafd8c trace2:gvfs:experiment: add region around unpack_trees()
  • 79: 70ad1b0 = 79: 5f7eb82 trace2:gvfs:experiment: add region to cache_tree_fully_valid()
  • 80: 0a6d3b1 = 80: 16d7ab4 trace2:gvfs:experiment: add unpack_entry() counter to unpack_trees() and report_tracking()
  • 81: f0bd9c1 = 81: 69a7416 trace2:gvfs:experiment: increase default event depth for unpack-tree data
  • 82: b65ac9b = 82: d064ab9 trace2:gvfs:experiment: add data for check_updates() in unpack_trees()
  • 83: cb7f57d = 83: 9869d50 Trace2:gvfs:experiment: capture more 'tracking' details
  • 84: d33770d = 84: 0f6e564 credential: set trace2_child_class for credential manager children
  • 85: 4d965ad = 85: 562f941 sub-process: do not borrow cmd pointer from caller
  • 86: b5cdcff = 86: d62bc21 sub-process: add subprocess_start_argv()
  • 87: be563b7 < -: ------------ sha1-file: add function to update existing loose object cache
  • 88: 4e743e6 < -: ------------ packfile: add install_packed_git_and_mru()
  • -: ------------ > 87: 32da4d2 sha1-file: add function to update existing loose object cache
  • 142: a4e5a11 = 88: 5e89994 git_config_set_multivar_in_file_gently(): add a lock timeout
  • 178: 4b24114 = 89: f9aff2d TO-UPSTREAM: sequencer: avoid progress when stderr is redirected
  • 207: 5012092 = 90: 3aba24d revision: defensive programming
  • 208: c04c3f4 = 91: e1fa61a get_parent(): defensive programming
  • 209: 3982ec6 = 92: d2637fc fetch-pack: defensive programming
  • 210: d5bd6e9 = 93: 05a18da unparse_commit(): defensive programming
  • 211: aa5d7db = 94: 017717c verify_commit_graph(): defensive programming
  • 201: 4124f85 = 95: 5cb79ba cat_one_file(): make it easy to see that the size variable is initialized
  • 212: 75bc9c4 = 96: 560552a stash: defensive programming
  • 202: 445a948 = 97: 5b06913 fsck: avoid using an uninitialized variable
  • 214: 621340b = 98: f0feb2a stash: defensive programming
  • 203: 03bba98 = 99: d03508b load_revindex_from_disk(): avoid accessing uninitialized data
  • 216: bb6ed4b = 100: a433b41 push: defensive programming
  • 205: 55f5d1a = 101: 329d7d2 load_pack_mtimes_file(): avoid accessing uninitialized data
  • 219: 52ac586 = 102: 8a12656 fetch: defensive programming
  • 213: 3d81d04 = 103: db7f56e fetch: silence a CodeQL alert about a local variable's address' use after release
  • 222: 473b681 = 104: 63f1a8b inherit_tracking(): defensive programming
  • 218: 94c76f9 = 105: d7d1227 codeql: run static analysis as part of CI builds
  • 221: 5cf407a = 106: 47886ea codeql: publish the sarif file as build artifact
  • 224: 9e41ca1 = 107: d156136 codeql: disable a couple of non-critical queries for now
  • 225: aad1990 = 108: cc0984f date: help CodeQL understand that there are no leap-year issues here
  • 226: 6771403 = 109: c032af3 help: help CodeQL understand that consuming envvars is okay here
  • 217: f207589 = 110: 5234177 test-tool repository: check return value of lookup_commit()
  • 227: 1328dc4 = 111: 8c8f04c ctype: help CodeQL understand that sane_istest() does not access array past end
  • 220: 9afcb88 = 112: 1f4d572 shallow: handle missing shallow commits gracefully
  • 228: 09ebb2c = 113: 05b6a4b ctype: accommodate for CodeQL misinterpreting the z in mallocz()
  • 223: 7b976ae = 114: c733c46 commit-graph: suppress warning about using a stale stack addresses
  • 229: f731d22 = 115: 0fd71f3 strbuf_read: help with CodeQL misunderstanding that strbuf_read() does NUL-terminate correctly
  • 230: f54bb27 = 116: 16c807a codeql: also check JavaScript code
  • 89: 53e0ae4 = 117: 7dc70b4 index-pack: avoid immediate object fetch while parsing packfile
  • 90: b46ad0a ! 118: d96249f gvfs-helper: create tool to fetch objects using the GVFS Protocol
    @@ gvfs-helper-client.c (new)
     +	if (!skip_prefix(line, "loose ", &v1_oid))
     +		BUG("update_loose_cache: invalid line '%s'", line);
     +
    -+	odb_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
    -+}
    -+
    -+/*
    -+ * Update the packed-git list to include the newly created packfile.
    -+ */
    -+static void gh_client__update_packed_git(const char *line)
    -+{
    -+	struct strbuf path = STRBUF_INIT;
    -+	const char *v1_filename;
    -+	struct packed_git *p;
    -+	int is_local;
    -+
    -+	if (!skip_prefix(line, "packfile ", &v1_filename))
    -+		BUG("update_packed_git: invalid line '%s'", line);
    -+
    -+	/*
    -+	 * ODB[0] is the local .git/objects.  All others are alternates.
    -+	 */
    -+	is_local = (gh_client__chosen_odb == the_repository->objects->sources);
    -+
    -+	strbuf_addf(&path, "%s/pack/%s",
    -+		    gh_client__chosen_odb->path, v1_filename);
    -+	strbuf_strip_suffix(&path, ".pack");
    -+	strbuf_addstr(&path, ".idx");
    -+
    -+	p = add_packed_git(the_repository, path.buf, path.len, is_local);
    -+	if (p)
    -+		packfile_store_add_pack_also_to_mru(the_repository, p);
    -+	strbuf_release(&path);
    ++	odb_source_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
     +}
     +
     +/*
    @@ gvfs-helper-client.c (new)
     +		}
     +
     +		else if (starts_with(line, "packfile")) {
    -+			gh_client__update_packed_git(line);
     +			ghc |= GHC__CREATED__PACKFILE;
     +			*p_nr_packfile += 1;
     +		}
    @@ gvfs-helper-client.c (new)
     +		}
     +	}
     +
    ++	if (ghc & GHC__CREATED__PACKFILE)
    ++		packfile_store_reprepare(the_repository->objects->packfiles);
    ++
     +	*p_ghc = ghc;
     +
     +	return err;
    @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
      	const struct object_id *real = oid;
      	int already_retried = 0;
      	int tried_hook = 0;
    --
     +	int tried_gvfs_helper = 0;
      
      	if (flags & OBJECT_INFO_LOOKUP_REPLACE)
      		real = lookup_replace_object(odb->repo, oid);
     @@ odb.c: retry:
    - 	}
    + 	odb_prepare_alternates(odb);
      
      	while (1) {
     +		extern int core_use_gvfs_helper;
    -+
    - 		if (find_pack_entry(odb->repo, real, &e))
    - 			break;
    + 		struct odb_source *source;
      
    + 		if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
     @@ odb.c: retry:
    - 		if (!loose_object_info(odb->repo, real, oi, flags))
    - 			return 0;
    + 			if (!odb_source_loose_read_object_info(source, real, oi, flags))
    + 				return 0;
      
     +		if (core_use_gvfs_helper && !tried_gvfs_helper) {
     +			enum gh_client__created ghc;
    @@ odb.c: retry:
      		/* Not a loose object; someone else may have just packed it. */
      		if (!(flags & OBJECT_INFO_QUICK)) {
      			odb_reprepare(odb->repo->objects);
    - 			if (find_pack_entry(odb->repo, real, &e))
    - 				break;
    + 			if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
    + 				return 0;
      			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
     +				// TODO Assert or at least trace2 if gvfs-helper
     +				// TODO was tried and failed and then read-object-hook
  • 91: 763e9b9 ! 119: 2f5fca9 sha1-file: create shared-cache directory if it doesn't exist
    @@ odb.c: int odb_mkstemp(struct object_database *odb,
      /*
       * Return non-zero iff the path is usable as an alternate object database.
       */
    -@@ odb.c: static int alt_odb_usable(struct object_database *o,
    - 			  struct strbuf *path,
    - 			  const char *normalized_objdir, khiter_t *pos)
    + static bool odb_is_source_usable(struct object_database *o, const char *path)
      {
     +	extern struct strbuf gvfs_shared_cache_pathname;
      	int r;
    + 	struct strbuf normalized_objdir = STRBUF_INIT;
    + 	bool usable = false;
      
    -+	if (!strbuf_cmp(path, &gvfs_shared_cache_pathname)) {
    + 	strbuf_realpath(&normalized_objdir, o->sources->path, 1);
    + 
    ++	if (!strcmp(path, gvfs_shared_cache_pathname.buf)) {
     +		/*
     +		 * `gvfs.sharedCache` is the preferred alternate that we
     +		 * will use with `gvfs-helper.exe` to dynamically fetch
    @@ odb.c: static int alt_odb_usable(struct object_database *o,
     +		 * as a leading directory of something deeper (which it
     +		 * won't create).
     +		 */
    -+		strbuf_addf(&buf_pack_foo, "%s/pack/foo", path->buf);
    ++		strbuf_addf(&buf_pack_foo, "%s/pack/foo", path);
     +
     +		scld = safe_create_leading_directories(o->repo, buf_pack_foo.buf);
     +		if (scld != SCLD_OK && scld != SCLD_EXISTS) {
    @@ odb.c: static int alt_odb_usable(struct object_database *o,
     +	}
     +
      	/* Detect cases where alternate disappeared */
    - 	if (!is_directory(path->buf)) {
    + 	if (!is_directory(path)) {
      		error(_("object directory %s does not exist; "
     @@ odb.c: int odb_for_each_alternate(struct object_database *odb,
      
      void odb_prepare_alternates(struct object_database *odb)
      {
     +	extern struct strbuf gvfs_shared_cache_pathname;
    - 	if (odb->loaded_alternates)
    - 		return;
    + 	struct strvec sources = STRVEC_INIT;
      
    - 	link_alt_odb_entries(odb, odb->alternate_db, PATH_SEP, NULL, 0);
    + 	if (odb->loaded_alternates)
    +@@ odb.c: void odb_prepare_alternates(struct object_database *odb)
    + 	for (size_t i = 0; i < sources.nr; i++)
    + 		odb_add_alternate_recursively(odb, sources.v[i], 0);
      
    - 	read_info_alternates(odb, odb->sources->path, 0);
    -+
     +	if (gvfs_shared_cache_pathname.len &&
     +	    !gvfs_matched_shared_cache_to_alternate) {
     +		/*
    @@ odb.c: int odb_for_each_alternate(struct object_database *odb,
     +		 * for us to update .git/objects/info/alternates instead.
     +		 * The code here is considered a backstop.
     +		 */
    -+		link_alt_odb_entries(odb, gvfs_shared_cache_pathname.buf,
    -+				     '\n', NULL, 0);
    ++		parse_alternates(gvfs_shared_cache_pathname.buf, '\n', NULL, &sources);
    ++		odb_source_read_alternates(odb->sources, &sources);
    ++		for (size_t i = 0; i < sources.nr; i++)
    ++			odb_add_alternate_recursively(odb, sources.v[i], 0);
    ++
     +	}
     +
      	odb->loaded_alternates = 1;
    - }
      
    + 	strvec_clear(&sources);
  • 92: 614c85e = 120: f0efd14 gvfs-helper: better handling of network errors
  • 93: e672c0e ! 121: 286d80c gvfs-helper-client: properly update loose cache with fetched OID
    @@ gvfs-helper-client.c: static void gh_client__update_loose_cache(const char *line
     +	if (get_oid_hex(v1_oid, &oid))
     +		BUG("update_loose_cache: invalid line '%s'", line);
     +
    - 	odb_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
    + 	odb_source_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
      }
      
  • 94: 6ea75c5 = 122: 635cad9 gvfs-helper: V2 robust retry and throttling
  • 95: 3ce279b ! 123: e0769b8 gvfs-helper: expose gvfs/objects GET and POST semantics
    @@ gvfs-helper-client.c: static int gh_client__get__send_command(struct child_proce
      /*
       * Update the loose object cache to include the newly created
       * object.
    -@@ gvfs-helper-client.c: static void gh_client__update_packed_git(const char *line)
    +@@ gvfs-helper-client.c: static void gh_client__update_loose_cache(const char *line)
      }
      
      /*
    @@ gvfs-helper-client.c: static void gh_client__update_packed_git(const char *line)
       *
       *    <odb>
       *    <data>*
    -@@ gvfs-helper-client.c: static void gh_client__update_packed_git(const char *line)
    +@@ gvfs-helper-client.c: static void gh_client__update_loose_cache(const char *line)
       * grouped with a queued request for a blob.  The tree-walk *might* be
       * able to continue and let the 404 blob be handled later.
       */
  • 96: 77504cf = 124: 375e49c gvfs-helper: dramatically reduce progress noise
  • 97: 8340ab7 = 125: e231f1c gvfs-helper: handle pack-file after single POST request
  • 98: de4f7af ! 126: 14aea24 test-gvfs-prococol, t5799: tests for gvfs-helper
    @@ t/t5799-gvfs-helper.sh (new)
     +		>OUT.output 2>OUT.stderr
     +'
     +
    ++trace_has_queue_oid () {
    ++	oid=$1
    ++	grep "gh_client__queue_oid: $oid"
    ++}
    ++
    ++trace_has_immediate_oid () {
    ++	oid=$1
    ++	grep "gh_client__get_immediate: $oid"
    ++}
    ++
     +test_expect_success 'integration: fully implicit: diff 2 commits' '
     +	test_when_finished "per_test_cleanup" &&
     +	start_gvfs_protocol_server &&
     +
     +	# Implicitly demand-load everything without any pre-seeding.
     +	#
    ++	GIT_TRACE2_EVENT="$(pwd)/diff-trace.txt" \
     +	git -C "$REPO_T1" -c core.useGVFSHelper=true \
     +		diff $(cat m1.branch)..$(cat m3.branch) \
    -+		>OUT.output 2>OUT.stderr
    ++		>OUT.output 2>OUT.stderr &&
    ++
    ++	oid=$(git -C "$REPO_SRC" rev-parse main:file9.txt.t) &&
    ++	trace_has_queue_oid $oid <diff-trace.txt &&
    ++	! trace_has_immediate_oid $oid <diff-trace.txt
     +'
     +
     +test_done
  • 99: b71d6ae = 127: 51dd99f gvfs-helper: move result-list construction into install functions
  • 100: 1d727d5 = 128: c8194fc t5799: add support for POST to return either a loose object or packfile
  • 101: a97f685 = 129: 27fab01 t5799: cleanup wc-l and grep-c lines
  • 102: 16c08a7 = 130: b83c808 gvfs-helper: verify loose objects after write
  • 103: e4b9b44 ! 131: ffd9b50 t7599: create corrupt blob test
    @@ t/helper/test-gvfs-protocol.c: static enum worker_result send_loose_object(const
     
      ## t/t5799-gvfs-helper.sh ##
     @@ t/t5799-gvfs-helper.sh: test_expect_success 'integration: fully implicit: diff 2 commits' '
    - 		>OUT.output 2>OUT.stderr
    + 	! trace_has_immediate_oid $oid <diff-trace.txt
      '
      
     +#################################################################
  • 104: b8f8754 ! 132: 12f8572 gvfs-helper: add prefetch support
    @@ gvfs-helper-client.c: static int gh_client__send__objects_get(struct child_proce
      /*
       * Update the loose object cache to include the newly created
       * object.
    -@@ gvfs-helper-client.c: static void gh_client__update_packed_git(const char *line)
    +@@ gvfs-helper-client.c: static void gh_client__update_loose_cache(const char *line)
      }
      
      /*
    @@ gvfs-helper-client.c: static int gh_client__objects__receive_response(
      
      	while (1) {
     @@ gvfs-helper-client.c: static int gh_client__objects__receive_response(
    + 
      		else if (starts_with(line, "packfile")) {
    - 			gh_client__update_packed_git(line);
      			ghc |= GHC__CREATED__PACKFILE;
     -			*p_nr_packfile += 1;
     +			nr_packfile++;
    @@ gvfs-helper-client.c: static int gh_client__objects__receive_response(
      
      		else if (starts_with(line, "ok"))
     @@ gvfs-helper-client.c: static int gh_client__objects__receive_response(
    - 	}
    + 		packfile_store_reprepare(the_repository->objects->packfiles);
      
      	*p_ghc = ghc;
     +	*p_nr_loose = nr_loose;
  • 105: 66c53ad = 133: c52f9e0 gvfs-helper: add prefetch .keep file for last packfile
  • 106: 7fa304e = 134: e70c5ae gvfs-helper: do one read in my_copy_fd_len_tail()
  • 107: 4a51839 = 135: 1e3eb72 gvfs-helper: move content-type warning for prefetch packs
  • 108: d5d13e4 = 136: 530c386 fetch: use gvfs-helper prefetch under config
  • 109: 3737e02 ! 137: accdcb2 gvfs-helper: better support for concurrent packfile fetches
    @@ t/t5799-gvfs-helper.sh: verify_prefetch_keeps () {
      	stop_gvfs_protocol_server
      
     @@ t/t5799-gvfs-helper.sh: test_expect_success 'integration: fully implicit: diff 2 commits' '
    - 		>OUT.output 2>OUT.stderr
    + 	! trace_has_immediate_oid $oid <diff-trace.txt
      '
      
     +#################################################################
  • 110: 0df839f = 138: b754725 remote-curl: do not call fetch-pack when using gvfs-helper
  • 111: 7c0292f = 139: 9cecfa7 fetch: reprepare packs before checking connectivity
  • 112: 61faefe = 140: 0a97b10 gvfs-helper: retry when creating temp files
  • 113: 3ba1db1 = 141: 76d67ef sparse: avoid warnings about known cURL issues in gvfs-helper.c
  • 114: b4d5fee = 142: bba8392 gvfs-helper: add --max-retries to prefetch verb
  • 115: 58484f0 = 143: 80d8687 t5799: add tests to detect corrupt pack/idx files in prefetch
  • 116: d0c6df2 = 144: 87a0a59 gvfs-helper: ignore .idx files in prefetch multi-part responses
  • 117: f48c9c9 = 145: 6f7a6a1 t5799: explicitly test gvfs-helper --fallback and --no-fallback
  • 118: afe38f4 = 146: 4c7a09f gvfs-helper: don't fallback with new config
  • 132: a957878 = 147: e37bc01 test-gvfs-protocol: add cache_http_503 to mayhem
  • 119: e8eff45 = 148: 61cf282 maintenance: care about gvfs.sharedCache config
  • 120: 8e94c5e = 149: f223d52 unpack-trees:virtualfilesystem: Improve efficiency of clear_ce_flags
  • 121: 56471ce = 150: ef1db25 homebrew: add GitHub workflow to release Cask
  • 122: 0f989a2 ! 151: 1384c9d Adding winget workflows
    @@ .github/workflows/release-winget.yml (new)
     +          $env:TAG_NAME -match 'v(.*?)vfs\.(.*)'
     +          $version = $Matches[1] + $Matches[2]
     +
    ++          # Download the token from Azure Key Vault and mask it in the logs
    ++          $env:WINGET_CREATE_GITHUB_TOKEN = az keyvault secret show --name ${{ secrets.WINGET_TOKEN_SECRET_NAME }} --vault-name ${{ secrets.AZURE_VAULT }} --query "value" -o tsv
    ++          Write-Host -NoNewLine "::add-mask::$env:WINGET_CREATE_GITHUB_TOKEN"
    ++
     +          # Download wingetcreate and create manifests
     +          Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
     +          .\wingetcreate.exe update Microsoft.Git `
    @@ .github/workflows/release-winget.yml (new)
     +                 "$($asset_arm64_url)|arm64|machine" `
     +                 "$($asset_arm64_url)|arm64|user"
     +
    -+          # Download the token from Azure Key Vault and mask it in the logs
    -+          az keyvault secret download --name ${{ secrets.WINGET_TOKEN_SECRET_NAME }} --vault-name ${{ secrets.AZURE_VAULT }} --file token.txt
    -+          Write-Host -NoNewLine "::add-mask::$(Get-Content token.txt)"
    -+
     +          # Submit the manifest to the winget-pkgs repository
     +          $manifestDirectory = "$PWD\manifests\m\Microsoft\Git\$version"
    -+          $output = & .\wingetcreate.exe submit -t "$(Get-Content token.txt)" $manifestDirectory
    ++          $output = & .\wingetcreate.exe submit $manifestDirectory
     +          Write-Host $output
    -+          $url = ($output | Select-String -Pattern 'https://\S+' | ForEach-Object { $_.Matches.Value })[0]
    ++          $url = ($output | Select-String -Pattern 'https://github\.com/microsoft/winget-pkgs/pull/\S+' | ForEach-Object { $_.Matches.Value })[0]
     +          Write-Host "::notice::Submitted ${env:TAG_NAME} to winget as $url"
     +        shell: powershell
  • 123: 3e5ab76 ! 152: 35199a8 Disable the monitor-components workflow in msft-git
    @@ .github/workflows/monitor-components.yml (deleted)
     -            feed: https://github.com/jrsoftware/issrc/tags.atom
     -          - label: mimalloc
     -            feed: https://github.com/microsoft/mimalloc/tags.atom
    --            title-pattern: ^(?!v1\.)
    +-            title-pattern: ^(?!v1\.|v3\.[01]\.)
     -      fail-fast: false
     -    steps:
     -      - uses: git-for-windows/rss-to-issues@v0
  • 124: cfeb5b4 = 153: f274bc2 .github: enable windows builds on microsoft fork
  • 134: dbbfb0d ! 154: 9575ae5 t5799: add unit tests for new gvfs.fallback config setting
    @@ t/t5799-gvfs-helper.sh: test_expect_success 'setup repos' '
      	# Create some test data sets.
      	#
     @@ t/t5799-gvfs-helper.sh: test_expect_success 'integration: fully implicit: diff 2 commits' '
    - 		>OUT.output 2>OUT.stderr
    + 	! trace_has_immediate_oid $oid <diff-trace.txt
      '
      
     +# T1 should be considered contaminated at this point.
  • 125: b5b8beb = 155: 2629aed .github/actions/akv-secret: add action to get secrets
  • 126: 52ac0d1 ! 156: 2426125 release: create initial Windows installer build workflow
    @@ .github/workflows/build-git-installers.yml (new)
     +          eval $b/please.sh make_installers_from_mingw_w64_git --include-pdbs \
     +              --version=${{ needs.prereqs.outputs.tag_version }} \
     +              -o artifacts --${{matrix.type.name}} \
    -+              --pkg=${{matrix.arch.artifact}}/mingw-w64-${{matrix.arch.toolchain}}-git-[0-9]*.tar.xz \
    -+              --pkg=${{matrix.arch.artifact}}/mingw-w64-${{matrix.arch.toolchain}}-git-doc-html-[0-9]*.tar.xz &&
    -+
    ++              $(ls ${{matrix.arch.artifact}}/mingw-w64-${{matrix.arch.toolchain}}-*.tar.* |
    ++                sed '/\.sig$/d;/archimport/d;/cvs/d;/p4/d;/gitweb/d;/doc-man/d;s/^/--pkg=/' |
    ++                tr '\n' ' ') &&
     +          if test portable = '${{matrix.type.name}}' && test -n "$(git config alias.signtool)"
     +          then
     +            git signtool artifacts/PortableGit-*.exe
  • 127: 6efdee7 = 157: 1e6be52 release: create initial Windows installer build workflow
  • 128: f14ea8a = 158: 893690f help: special-case HOST_CPU universal
  • 129: f25ca1e = 159: 930fb70 release: add Mac OSX installer build
  • 130: 7c62bfa ! 160: 57cee90 release: build unsigned Ubuntu .deb package
    @@ Commit message
         - build & upload unsigned .deb package
     
         Co-authored-by: Lessley Dennington <ldennington@github.com>
    +    Co-authored-by: Sverre Johansen <sverre.johansen@gmail.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .github/workflows/build-git-installers.yml ##
     @@ .github/workflows/build-git-installers.yml: jobs:
    @@ .github/workflows/build-git-installers.yml: jobs:
     +
     +  # Build unsigned Ubuntu package
     +  create-linux-unsigned-artifacts:
    -+    runs-on: ubuntu-latest
    ++    runs-on: ${{ matrix.arch.runner }}
    ++    strategy:
    ++      matrix:
    ++        arch:
    ++          - name: amd64
    ++            runner: ubuntu-latest
    ++            # EOL 04/2025: https://endoflife.date/ubuntu
    ++            container_image: ubuntu:20.04
    ++            # Use unofficial Node.js builds with glibc-217 for older Ubuntu
    ++            node_url: https://unofficial-builds.nodejs.org/download/release/v20.18.1/node-v20.18.1-linux-x64-glibc-217.tar.gz
    ++          - name: arm64
    ++            runner: ubuntu-24.04-arm
    ++            # EOL 04/2027: https://endoflife.date/ubuntu
    ++            container_image: ubuntu:22.04
    ++            # Use official Node.js builds for ARM64 (requires glibc 2.28+, Ubuntu 22.04 has 2.35)
    ++            node_url: https://nodejs.org/dist/v20.18.1/node-v20.18.1-linux-arm64.tar.gz
     +    container:
    -+      image: ubuntu:20.04 # security support until 04/02/2025, according to https://endoflife.date/ubuntu
    ++      image: ${{ matrix.arch.container_image }}
     +      volumes:
     +        # override /__e/node20 because GitHub Actions uses a version that requires too-recent glibc, see "Install dependencies" below
     +        - /tmp:/__e/node20
    @@ .github/workflows/build-git-installers.yml: jobs:
     +            libcurl4-gnutls-dev libpcre2-dev zlib1g-dev libexpat-dev \
     +            curl ca-certificates
     +
    -+          # Install a Node.js version that works in older Ubuntu containers (read: does not require very recent glibc)
    -+          NODE_VERSION=v20.18.1 &&
    -+          NODE_URL=https://unofficial-builds.nodejs.org/download/release/$NODE_VERSION/node-$NODE_VERSION-linux-x64-glibc-217.tar.gz &&
    -+          curl -Lo /tmp/node.tar.gz $NODE_URL &&
    ++          # Install Node.js for GitHub Actions compatibility
    ++          curl -Lo /tmp/node.tar.gz "${{ matrix.arch.node_url }}" &&
     +          tar -C /__e/node20 -x --strip-components=1 -f /tmp/node.tar.gz
     +
     +      - name: Clone git
    @@ .github/workflows/build-git-installers.yml: jobs:
     +            die "Could not determine host architecture!"
     +          fi
     +
    -+          PKGNAME="microsoft-git_$VERSION"
    ++          PKGNAME="microsoft-git_${VERSION}_${ARCH}"
     +          PKGDIR="$(dirname $(pwd))/$PKGNAME"
     +
     +          rm -rf "$PKGDIR"
  • 131: 2675196 ! 161: c000de0 release: add signing step for .deb package
    @@ Commit message
         - job skipped if credentials for accessing certificate aren't present
     
         Co-authored-by: Lessley Dennington <ldennington@github.com>
    +    Co-authored-by: Sverre Johansen <sverre.johansen@gmail.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .github/workflows/build-git-installers.yml ##
     @@ .github/workflows/build-git-installers.yml: on:
    @@ .github/workflows/build-git-installers.yml: jobs:
     -  # Build unsigned Ubuntu package
     +  # Build and sign Debian package
        create-linux-unsigned-artifacts:
    -     runs-on: ubuntu-latest
    -     container:
    +     runs-on: ${{ matrix.arch.runner }}
    +     strategy:
     @@ .github/workflows/build-git-installers.yml: jobs:
    -           # Move Debian package for later artifact upload
    -           mv "$PKGNAME.deb" "$GITHUB_WORKSPACE"
    - 
    -+      - name: Upload artifacts
    -+        uses: actions/upload-artifact@v4
    -+        with:
    -+          name: linux-unsigned-artifacts
    +       - name: Upload artifacts
    +         uses: actions/upload-artifact@v4
    +         with:
    +-          name: linux-artifacts
    ++          name: linux-unsigned-${{ matrix.arch.name }}
     +          path: |
     +            *.deb
     +
     +  create-linux-artifacts:
     +    runs-on: ubuntu-latest
     +    needs: [prereqs, create-linux-unsigned-artifacts]
    ++    strategy:
    ++      matrix:
    ++        arch: [amd64, arm64]
     +    environment: release
     +    steps:
     +      - name: Log into Azure
    @@ .github/workflows/build-git-installers.yml: jobs:
     +      - name: Download artifacts
     +        uses: actions/download-artifact@v4
     +        with:
    -+          name: linux-unsigned-artifacts
    ++          name: linux-unsigned-${{ matrix.arch }}
     +
     +      - name: Sign Debian package
     +        run: |
     +          # Sign Debian package
     +          version="${{ needs.prereqs.outputs.tag_version }}"
    -+          debsigs --sign=origin --verify --check microsoft-git_"$version".deb
    ++          debsigs --sign=origin --verify --check microsoft-git_"$version"_${{ matrix.arch }}.deb
     +
    -       - name: Upload artifacts
    -         uses: actions/upload-artifact@v4
    -         with:
    -           name: linux-artifacts
    ++      - name: Upload artifacts
    ++        uses: actions/upload-artifact@v4
    ++        with:
    ++          name: linux-${{ matrix.arch }}
                path: |
                  *.deb
     -  # End build unsigned Debian package
  • 133: 44958f7 ! 162: c7cd57b release: create draft GitHub release with packages & installers
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          name: macos-artifacts
     +          path: macos-artifacts
     +
    -+      - name: Download Debian package
    ++      - name: Download Debian package (amd64)
     +        uses: actions/download-artifact@v4
     +        with:
    -+          name: linux-artifacts
    ++          name: linux-amd64
    ++          path: deb-package
    ++
    ++      - name: Download Debian package (arm64)
    ++        uses: actions/download-artifact@v4
    ++        with:
    ++          name: linux-arm64
     +          path: deb-package
     +
     +      - uses: actions/github-script@v6
  • 137: 81d1da4 ! 163: 6780511 build-git-installers: publish gpg public key
    @@ .github/workflows/build-git-installers.yml: jobs:
            success() ||
              (needs.create-linux-artifacts.result == 'skipped' &&
     @@ .github/workflows/build-git-installers.yml: jobs:
    -           name: linux-artifacts
    +           name: linux-arm64
                path: deb-package
      
     +      - name: Log into Azure
  • 139: 1552b43 = 164: 0545c0f release: continue pestering until user upgrades
  • 141: 3505413 = 165: db7c2b8 dist: archive HEAD instead of HEAD^{tree}
  • 135: 4562f38 = 166: 9c3884b update-microsoft-git: create barebones builtin
  • 136: 3f77287 = 167: 6dac9dc update-microsoft-git: Windows implementation
  • 138: 6a1129a = 168: 6fa0b40 update-microsoft-git: use brew on macOS
  • 140: 9cf6c98 = 169: ceac7d2 .github: reinstate ISSUE_TEMPLATE.md for microsoft/git
  • 143: b87c26a = 170: bd4929d .github: update PULL_REQUEST_TEMPLATE.md
  • 144: f589ada = 171: 22d40cd release: include GIT_BUILT_FROM_COMMIT in MacOS build
  • 146: 70329c1 = 172: 34ee69b Adjust README.md for microsoft/git
  • 147: 45c713e ! 173: 209ecf4 release: add installer validation
    @@ .github/workflows/build-git-installers.yml: jobs:
     +      matrix:
     +        component:
     +          - os: ubuntu-latest
    -+            artifact: linux-artifacts
    ++            artifact: linux-amd64
    ++            command: git
    ++          - os: ubuntu-24.04-arm
    ++            artifact: linux-arm64
     +            command: git
     +          - os: macos-latest
     +            artifact: macos-artifacts
  • 145: b3ebac2 ! 174: 30b2fb9 scalar: set the config write-lock timeout to 150ms
    @@ Commit message
     
         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
    + ## Documentation/scalar.adoc ##
    +@@ Documentation/scalar.adoc: status.aheadBehind=false::
    + 	message that can be disabled by disabling the `advice.statusAheadBehind`
    + 	config.
    + 
    ++core.configWriteLockTimeoutMS::
    ++    Sets a timeout to work gracefully around Git config write contention.
    ++
    + The following settings are different based on which platform is in use:
    + 
    + core.untrackedCache=(true|false)::
    +
      ## scalar.c ##
     @@ scalar.c: static int set_recommended_config(int reconfigure)
    - 		{ "core.safeCRLF", "false" },
    - 		{ "fetch.showForcedUpdates", "false" },
    - 		{ "pack.usePathWalk", "true" },
    + 		{ "commitGraph.changedPaths", "true" },
    + 		{ "commitGraph.generationVersion", "1" },
    + 		{ "core.autoCRLF", "false" },
     +		{ "core.configWriteLockTimeoutMS", "150" },
    - 		{ NULL, NULL },
    - 	};
    - 	int i;
    + 		{ "core.logAllRefUpdates", "true" },
    + 		{ "core.safeCRLF", "false" },
    + 		{ "credential.https://dev.azure.com.useHttpPath", "true" },
     @@ scalar.c: static int set_recommended_config(int reconfigure)
       */
      static int toggle_maintenance(int enable)
  • 148: ed317ca = 175: 11dc4b9 scalar: add docs from microsoft/scalar
  • 149: 31963eb = 176: aaa827c scalar (Windows): use forward slashes as directory separators
  • 150: d274abe = 177: 0b03800 scalar: add retry logic to run_git()
  • 151: de53ca4 = 178: 6681895 scalar: support the config command for backwards compatibility
  • 152: 9871237 = 179: 15f6a71 scalar: implement a minimal JSON parser
  • 153: 8bfb239 ! 180: b55cd61 scalar clone: support GVFS-enabled remote repositories
    @@ diagnose.c: diagnose_cleanup:
     
      ## scalar.c ##
     @@
    - #include "help.h"
      #include "setup.h"
      #include "trace2.h"
    + #include "path.h"
     +#include "json-parser.h"
      
      static void setup_enlistment_directory(int argc, const char **argv,
  • 154: 03cabd8 = 181: 30c8d21 test-gvfs-protocol: also serve smart protocol
  • 155: e9eee40 = 182: 282ff8a gvfs-helper: add the endpoint command
  • 156: a944e34 = 183: 90e1e22 dir_inside_of(): handle directory separators correctly
  • 157: eef1989 = 184: 0fa6132 scalar: disable authentication in unattended mode
  • 158: a5f7a70 = 185: 86e4bf4 abspath: make strip_last_path_component() global
  • 159: 35e9646 ! 186: de6450b scalar: do initialize gvfs.sharedCache
    @@ scalar.c
      #include "setup.h"
     +#include "wrapper.h"
      #include "trace2.h"
    + #include "path.h"
      #include "json-parser.h"
     +#include "path.h"
     +
  • 160: 7bbc473 = 187: cffd005 scalar diagnose: include shared cache info
  • 161: 9afae7e = 188: f0c9d30 scalar: only try GVFS protocol on https:// URLs
  • 162: eecc2fe = 189: 91e8111 scalar: verify that we can use a GVFS-enabled repository
  • 163: aa93378 ! 190: 7822385 scalar: add the cache-server command
    @@ Documentation/scalar.adoc: delete <enlistment>::
     +configured credential helper is employed (see linkgit:git-credential[1]
     +for details).
     +
    - SEE ALSO
    - --------
    - linkgit:git-clone[1], linkgit:git-maintenance[1].
    + RECOMMENDED CONFIG VALUES
    + -------------------------
    + 
     
      ## scalar.c ##
     @@
    - #include "wrapper.h"
      #include "trace2.h"
    + #include "path.h"
      #include "json-parser.h"
     +#include "remote.h"
      #include "path.h"
  • 164: c4fd085 = 191: 5315f69 scalar: add a test toggle to skip accessing the vsts/info endpoint
  • 165: 6834689 = 192: d15da79 scalar: adjust documentation to the microsoft/git fork
  • 166: 339f964 < -: ------------ scalar: enable untracked cache unconditionally
  • -: ------------ > 193: 0c58fd6 scalar: enable untracked cache unconditionally
  • 167: 0b8b3b1 = 194: bff06a0 scalar: parse clone --no-fetch-commits-and-trees for backwards compatibility
  • 168: 4ab8a41 = 195: e251832 scalar: make GVFS Protocol a forced choice
  • 169: d684bf1 = 196: 8ef5aad scalar: work around GVFS Protocol HTTP/2 failures
  • 170: 4f35628 = 197: 582eab3 gvfs-helper-client: clean up server process(es)
  • 171: d87b397 = 198: ad2a57c scalar diagnose: accommodate Scalar's Functional Tests
  • 174: c678112 = 199: 4e7f3c5 ci: run Scalar's Functional Tests
  • 176: 6a04a00 ! 200: 0cb1fb9 scalar: upgrade to newest FSMonitor config setting
    @@ scalar.c: static int set_recommended_config(int reconfigure)
     +	}
     +
      	for (i = 0; config[i].key; i++) {
    - 		if (set_scalar_config(config + i, reconfigure))
    + 		if (set_config_if_missing(config + i, reconfigure))
      			return error(_("could not configure %s=%s"),
  • 172: 5f5c2e1 = 201: 636d32c add/rm: allow adding sparse entries when virtual
  • 173: 08c6f16 = 202: e67a1f3 sparse-checkout: add config to disable deleting dirs
  • 175: b92840f = 203: 3597a1e diff: ignore sparse paths in diffstat
  • 177: 0fbcabc = 204: 7e21d2b repo-settings: enable sparse index by default
  • 179: c9520df = 205: 0b56c88 TO-CHECK: t1092: use quiet mode for rebase tests
  • 180: 33d632a = 206: e76650e reset: fix mixed reset when using virtual filesystem
  • 181: b1ccdad = 207: 62bf2eb diff(sparse-index): verify with partially-sparse
  • 182: 276ff94 = 208: 1d9e329 stash: expand testing for git stash -u
  • 183: 21e6e77 = 209: af8dfd8 sparse-index: add ensure_full_index_with_reason()
  • 184: 44554ac = 210: 1190d47 treewide: add reasons for expanding index
  • 185: 5f15774 = 211: 7365756 treewide: custom reasons for expanding index
  • 186: 8abb5bb = 212: 0d5da1d sparse-index: add macro for unaudited expansions
  • 187: a3b970b = 213: 5815e20 Docs: update sparse index plan with logging
  • 188: d14448e = 214: 9f71809 sparse-index: log failure to clear skip-worktree
  • 189: 3787fdf = 215: 7d17d6e stash: use -f in checkout-index child process
  • 190: e7ab470 = 216: 490e6d6 sparse-index: do not copy hashtables during expansion
  • 191: 0779173 = 217: 57c1419 TO-UPSTREAM: sub-process: avoid leaking cmd
  • 192: a935153 = 218: 5643e2d remote-curl: release filter options before re-setting them
  • 193: f2cc70c = 219: eac13b8 transport: release object filter options
  • 194: 0cffb2b = 220: db10001 push: don't reuse deltas with path walk
  • 195: e789961 = 221: b682077 t7900-maintenance.sh: reset config between tests
  • 196: 58f2ac1 = 222: 941a9cb maintenance: add cache-local-objects maintenance task
  • 197: e05caee = 223: 43de449 scalar.c: add cache-local-objects task
  • 198: 0449ce4 = 224: e392446 hooks: add custom post-command hook config
  • 199: be736e3 ! 225: d9297be TO-UPSTREAM: Docs: fix asciidoc failures from short delimiters
    @@ Documentation/config/protocol.adoc: protocol.version::
     +----
     
      ## Documentation/config/push.adoc ##
    -@@ Documentation/config/push.adoc: push.default::
    +@@
      	(i.e. the fetch source is equal to the push destination),
      	`upstream` is probably what you want.  Possible values are:
      +
     ---
     +----
    - 
    - * `nothing` - do not push anything (error out) unless a refspec is
    -   given. This is primarily meant for people who want to
    + `nothing`;;
    + do not push anything (error out) unless a refspec is
    + given. This is primarily meant for people who want to
     @@ Documentation/config/push.adoc: branches outside your control.
      This used to be the default, but not since Git 2.0 (`simple` is the
      new default).
    @@ Documentation/config/push.adoc: branches outside your control.
     ---
     +----
      
    - push.followTags::
    + `push.followTags`::
      	If set to true, enable `--follow-tags` option by default.  You
     
      ## Documentation/config/sendemail.adoc ##
  • 200: 08ee1dd = 226: 27ba3c3 hooks: make hook logic memory-leak free
  • 204: 1cd37c0 = 227: c42e07c t0401: test post-command for alias, version, typo
  • 206: 9cb759f = 228: 32db8ee hooks: better handle config without gitdir
  • 215: 629d433 < -: ------------ submodule: check return value of submodule_from_path()
  • 231: 3eb3cf1 < -: ------------ fixup! gvfs-helper: create tool to fetch objects using the GVFS Protocol
  • 232: 13b8048 < -: ------------ fixup! packfile: add install_packed_git_and_mru()
  • 233: d50fc42 < -: ------------ fixup! fixup! gvfs-helper: create tool to fetch objects using the GVFS Protocol
  • 234: 65222a5 < -: ------------ fixup! gvfs-helper: create tool to fetch objects using the GVFS Protocol
  • 235: 4e6a770 < -: ------------ fixup! release: create initial Windows installer build workflow
  • 236: ac863dd = 229: 724630f scalar: add run_git_argv
  • 237: 33c414d = 230: 7e78c87 scalar: add --ref-format option to scalar clone
  • 238: 99b6e73 < -: ------------ mimalloc: prevent crashes in the post-command hook handling
  • 239: 98e06c8 (upstream: 56d388e) < -: ------------ diff: avoid segfault with freed entries
  • 240: 247b0ea = 231: dbf92c7 gvfs-helper: skip collision check for loose objects
  • 241: 4b90264 = 232: 400e944 gvfs-helper: emit advice on transient errors
  • 242: e123e69 = 233: e5f698d gvfs-helper: avoid collision check for packfiles
  • 243: 2a7ba47 < -: ------------ fixup! Adding winget workflows
  • 244: b7d2bbe < -: ------------ fixup! Adding winget workflows
  • 245: 20f24e2 < -: ------------ fixup! Adding winget workflows
  • 246: fbe30b0 < -: ------------ ci: add ARM64 (aarch64) Linux release builds

Ben Peart and others added 30 commits January 20, 2026 10:35
This adds hard-coded call to GVFS.hooks.exe before and after each Git
command runs.

To make sure that this is only called on repositories cloned with GVFS, we
test for the tell-tale .gvfs.

2021-10-30: Recent movement of find_hook() to hook.c required moving these
changes out of run-command.c to hook.c.

2025-11-06: The `warn_on_auto_comment_char` hack is so ugly that it
forces us to pile similarly ugly code on top because that hack _expects_
that the config has not been read when `cmd_commit()`, `cmd_revert()`,
`cmd_cherry_pick()`, `cmd_merge()`, or `cmd_rebase()` set that flag. But
with the `pre_command()` hook already run, that assumption is incorrect.

Signed-off-by: Ben Peart <Ben.Peart@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Suggested by Ben Peart.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Verify that the core.hooksPath configuration is repsected by the
pre-command hook. Original regression test was written by
Alejandro Pauly.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Alejandro Pauly <alpauly@microsoft.com>
When using the sparse-checkout feature, the file might not be on disk
because the skip-worktree bit is on. This used to be a bug in the
(hence deleted) `recursive` strategy. Let's ensure that this bug does
not resurface.

Signed-off-by: Kevin Willford <kewillf@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The 'git worktree' command was marked as BLOCK_ON_GVFS_REPO because it
does not interact well with the virtual filesystem of VFS for Git. When
a Scalar clone uses the GVFS protocol, it enables the
GVFS_BLOCK_COMMANDS flag, since commands like 'git gc' do not work well
with the GVFS protocol.

However, 'git worktree' works just fine with the GVFS protocol since it
isn't doing anything special. It copies the sparse-checkout from the
current worktree, so it does not have performance issues.

This is a highly requested option.

The solution is to stop using the BLOCK_ON_GVFS_REPO option and instead
add a special-case check in cmd_worktree() specifically for a particular
bit of the 'core_gvfs' global variable (loaded by very early config
reading) that corresponds to the virtual filesystem. The bit that most
closely resembled this behavior was non-obviously named, but does
provide a signal that we are in a Scalar clone and not a VFS for Git
clone. The error message is copied from git.c, so it will have the same
output as before if a user runs this in a VFS for Git clone.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
When using the sparse-checkout feature git should not write to the working
directory for files with the skip-worktree bit on.  With the skip-worktree
bit on the file may or may not be in the working directory and if it is
not we don't want or need to create it by calling checkout_entry.

There are two callers of checkout_target.  Both of which check that the
file does not exist before calling checkout_target.  load_current which
make a call to lstat right before calling checkout_target and
check_preimage which will only run checkout_taret it stat_ret is less than
zero.  It sets stat_ret to zero and only if !stat->cached will it lstat
the file and set stat_ret to something other than zero.

This patch checks if skip-worktree bit is on in checkout_target and just
returns so that the entry doesn't not end up in the working directory.
This is so that apply will not create a file in the working directory,
then update the index but not keep the working directory up to date with
the changes that happened in the index.

Signed-off-by: Kevin Willford <kewillf@microsoft.com>
Signed-off-by: Kevin Willford <kewillf@microsoft.com>
As of 9e59b38 (object-file: emit corruption errors when detected,
2022-12-14), Git will loudly complain about corrupt objects.

That is fine, as long as the idea isn't to re-download locally-corrupted
objects. But that's exactly what we want to do in VFS for Git via the
`read-object` hook, as per the `GitCorruptObjectTests` code
added in microsoft/VFSForGit@2db0c030eb25 (New
features: [...] -  GVFS can now recover from corrupted git object files
[...] , 2018-02-16).

So let's support precisely that, and add a regression test that ensures
that re-downloading corrupt objects via the `read-object` hook works.

While at it, avoid the XOR operator to flip the bits, when we actually
want to make sure that they are turned off: Use the AND-NOT operator for
that purpose.

Helped-by: Matthew John Cheetham <mjcheetham@outlook.com>
Helped-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Add the ability to block built-in commands based on if the `core.gvfs`
setting has the `GVFS_USE_VIRTUAL_FILESYSTEM` bit set. This allows us
to selectively block commands that use the GVFS protocol, but don't use
VFS for Git (for example repos cloned via `scalar clone` against Azure
DevOps).

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Loosen the blocking of the `repack` command from all "GVFS repos" (those
that have `core.gvfs` set) to only those that actually use the virtual
file system (VFS for Git only). This allows for `repack` to be used in
Scalar clones.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
String formatting can be a performance issue when there are
hundreds of thousands of trees.

Change to stop using the strbuf_addf and just add the strings
or characters individually.

There are a limited number of modes so added a switch for the
known ones and a default case if something comes through that
are not a known one for git.

In one scenario regarding a huge worktree, this reduces the
time required for a `git checkout <branch>` from 44 seconds
to 38 seconds, i.e. it is a non-negligible performance
improvement.

Signed-off-by: Kevin Willford <kewillf@microsoft.com>
Loosen the blocking of the `fsck` command from all "GVFS repos" (those
that have `core.gvfs` set) to only those that actually use the virtual
file system (VFS for Git only). This allows for `fsck` to be used in
Scalar clones.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
The following commands and options are not currently supported when working
in a GVFS repo.  Add code to detect and block these commands from executing.

1) fsck
2) gc
4) prune
5) repack
6) submodule
8) update-index --split-index
9) update-index --index-version (other than 4)
10) update-index --[no-]skip-worktree
11) worktree

Signed-off-by: Ben Peart <benpeart@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Loosen the blocking of the `prune` command from all "GVFS repos" (those
that have `core.gvfs` set) to only those that actually use the virtual
file system (VFS for Git only). This allows for `prune` to be used in
Scalar clones.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
In earlier versions of `microsoft/git`, we found a user who had set
`core.gvfs = false` in their global config. This should not have been
necessary, but it also should not have caused a problem. However, it
did.

The reason was that `gvfs_load_config_value()` was called from
`config.c` when reading config key/value pairs from all the config
files. The local config should override the global config, and this is
done by `config.c` reading the global config first then reading the
local config. However, our logic only allowed writing the `core_gvfs`
variable once.

In v2.51.0, we had to adapt to upstream changes that changed way the
`core.gvfs` config value is read, and the special handling is no longer
necessary, yet we still want the test case that ensures that this bug
does not experience a regression.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Replace the special casing of the `worktree` command being blocked on
VFS-enabled repos with the new `BLOCK_ON_VFS_ENABLED` flag.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
On index load, clear/set the skip worktree bits based on the virtual
file system data. Use virtual file system data to update skip-worktree
bit in unpack-trees. Use virtual file system data to exclude files and
folders not explicitly requested.

Update 2022-04-05: disable the "present-despite-SKIP_WORKTREE" file removal
behavior when 'core.virtualfilesystem' is enabled.

Signed-off-by: Ben Peart <benpeart@microsoft.com>
Emit a warning message when the `gvfs.sharedCache` option is set that
the `repack` command will not perform repacking on the shared cache.

In the future we can teach `repack` to operate on the shared cache, at
which point we can drop this commit.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
…x has been redirected

Fixes #13

Some git commands spawn helpers and redirect the index to a different
location.  These include "difftool -d" and the sequencer
(i.e. `git rebase -i`, `git cherry-pick` and `git revert`) and others.
In those instances we don't want to update their temporary index with
our virtualization data.

Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Ben Peart <Ben.Peart@microsoft.com>
Add check to see if a directory is included in the virtualfilesystem
before checking the directory hashmap.  This allows a directory entry
like foo/ to find all untracked files in subdirectories.
When our patches to support that hook were upstreamed, the hook's name
was eliciting some reviewer suggestions, and it was renamed to
`post-index-change`. These patches (with the new name) made it into
v2.22.0.

However, VFSforGit users may very well have checkouts with that hook
installed under the original name.

To support this, let's just introduce a hack where we look a bit more
closely when we just failed to find the `post-index-change` hook, and
allow any `post-indexchanged` hook to run instead (if it exists).
When using a virtual file system layer, the FSMonitor does not make
sense.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When sparse-checkout is enabled, add the sparse-checkout percentage to
the Trace2 data stream.  This number was already computed and printed
on the console in the "You are in a sparse checkout..." message.  It
would be helpful to log it too for performance monitoring.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Teach STATUS to optionally serialize the results of a
status computation to a file.

Teach STATUS to optionally read an existing serialization
file and simply print the results, rather than actually
scanning.

This is intended for immediate status results on extremely
large repos and assumes the use of a service/daemon to
maintain a fresh current status snapshot.

2021-10-30: packet_read() changed its prototype in ec9a37d (pkt-line.[ch]:
remove unused packet_read_line_buf(), 2021-10-14).

2021-10-30: sscanf() now does an extra check that "%d" goes into an "int"
and complains about "uint32_t". Replacing with "%u" fixes the compile-time
error.

2021-10-30: string_list_init() was removed by abf897b (string-list.[ch]:
remove string_list_init() compatibility function, 2021-09-28), so we need to
initialize manually.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Teach status serialization to take an optional pathname on
the command line to direct that cache data be written there
rather than to stdout.  When used this way, normal status
results will still be written to stdout.

When no path is given, only binary serialization data is
written to stdout.

Usage:
    git status --serialize[=<path>]

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Teach status deserialize code to reject status cache
when printing in porcelain V2 and there are unresolved
conflicts in the cache file.  A follow-on task might
extend the cache format to include this additiona data.

See code for longer explanation.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
dscho and others added 26 commits January 20, 2026 12:57
…-and-fix-built-in-fsmonitor

Fix the built-in FSMonitor, and run Scalar's Functional Tests as part of the automated builds
This is random stuff that probably all got upstream in the meantime.
Under certain circumstances, the `cmd` attribute is set to an
`strdup()`ed value. This value needs to be released in the end!

These circumstances can be observed easily in the Microsoft Git fork,
where the `read-object` hook triggers that code path.

Since other users assign a non-`strdup()`ed value, be careful to add
_another_ attribute (called `to_free`) that can hold a reference to such
a string that needs to be released once the sub process is done.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This fixes a leak that is not detected by Git's test suite (but by
microsoft/git's).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This fixes a leak that is not detected by Git's own test suite (but by
microsoft/git's, in the t9210-scalar.sh test).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Tests in t7900 assume the state of the `maintenance.strategy`
config setting; set/unset by previous tests. Correct this by
explictly unsetting and re-setting the config at the start of the
tests.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
The --path-walk option in `git pack-objects` is implied by the
pack.usePathWalk=true config value. This is intended to help the
packfile generation within `git push` specifically.

While this config does enable the path-walk feature, it does not lead to
the expected levels of compression in the cases it was designed to
handle. This is due to the default implication of the --reuse-delta
option as well as auto-GC.

In the performance tests used to evaluate the --path-walk option, such
as those in p5313, the --no-reuse-delta option is used to ensure that
deltas are recomputed according to the new object walk. However, it was
assumed (I assumed this) that when the objects were loose from
client-side operations that better deltas would be computed during this
operation. This wasn't confirmed because the test process used data that
was fetched from real repositories and thus existed in packed form only.

I was able to confirm that this does not reproduce when the objects to
push are loose. Careful use of making the pushed commit unreachable and
loosening the objects via `git repack -Ad` helps to confirm my
suspicions here. Independent of this change, I'm pushing for these
pipeline agents to set `gc.auto=0` before creating their Git objects. In
the current setup, the repo is adding objects and then incrementally
repacking them and ending up with bad cross-path deltas. This approach
can help scenarios where that makes sense, but will not cover all of our
users without them choosing to opt-in to background maintenance (and
even then, an incremental repack could cost them efficiency).

In order to make sure we are getting the intended compression in `git
push`, this change enforces the spawned `git pack-objects` process to
use `--no-reuse-delta`.

As far as I can tell, the main motivation for implying the --reuse-delta
option by default is two-fold:

 1. The code in send-pack.c that executes 'git pack-objects' is ignorant
    of whether the current process is a client pushing to a remote or a
    remote sending a fetch or clone to a client.

 2. For servers, it is critical that they trust the previously computed
    deltas whenever possible, or they could overload their CPU
    resources.

There's also the side that most servers use repacking logic that will
replace any bad deltas that are sent by clients (or at least, that's the
hope; we've seen that repacks can also pick bad deltas).

This commit also adds a test case that demonstrates that `git -c
pack.usePathWalk=true push` now avoids reusing deltas.

To do this, the test case constructs a pack with a horrendously
inefficient delta object, then verifies that the pack on the receiving
side of the `push` fails to have such an inefficient delta.

The test case would probably be a lot more readable if hex numbers were
used instead of octal numbers, but alas, `printf "\x<hex>"` is not
portable, only `printf "\<octal>"` is. For example, dash's built-in
`printf` function simply prints `\x` verbatim while bash's built-in
happily converts this construct to the corresponding byte.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Git v2.48.0 has become even more stringent about leaks.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Introduce a new maintenance task, `cache-local-objects`, that operates
on Scalar or VFS for Git repositories with a per-volume, shared object
cache (specified by `gvfs.sharedCache`) to migrate packfiles and loose
objects from the repository object directory to the shared cache.

Older versions of `microsoft/git` incorrectly placed packfiles in the
repository object directory instead of the shared cache; this task will
help clean up existing clones impacted by that issue.

Migration of packfiles involves the following steps for each pack:

1. Hardlink (or copy):
   a. the .pack file
   b. the .keep file
   c. the .rev file
2. Move (or copy + delete) the .idx file
3. Delete/unlink:
   a. the .pack file
   b. the .keep file
   c. the .rev file

Moving the index file after the others ensures the pack is not read
from the new cache directory until all associated files (rev, keep)
exist in the cache directory also.

Moving loose objects operates as a move, or copy + delete.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
The --path-walk option in 'git pack-objects' is implied by the
pack.usePathWalk=true config value. This is intended to help the
packfile generation within 'git push' specifically.

While this config does enable the path-walk feature, it does not lead
the expected levels of compression in the cases it was designed to
handle. This is due to the default implication of the --reuse-delta
option as well as auto-GC.

In the performance tests used to evaluate the --path-walk option, such
as those in p5313, the --no-reuse-delta option is used to ensure that
deltas are recomputed according to the new object walk. However, it was
assumed (I assumed this) that when the objects were loose from
client-side operations that better deltas would be computed during this
operation. This wasn't confirmed because the test process used data that
was fetched from real repositories and thus existed in packed form only.

I was able to confirm that this does not reproduce when the objects to
push are loose. Careful use of making the pushed commit unreachable and
loosening the objects via 'git repack -Ad' helps to confirm my
suspicions here. Independent of this change, I'm pushing for these
pipeline agents to set 'gc.auto=0' before creating their Git objects. In
the current setup, the repo is adding objects and then incrementally
repacking them and ending up with bad cross-path deltas. This approach
can help scenarios where that makes sense, but will not cover all of our
users without them choosing to opt-in to background maintenance (and
even then, an incremental repack could cost them efficiency).

In order to make sure we are getting the intended compression in 'git
push', this change makes the --path-walk option imply --no-reuse-delta
when the --reuse-delta option is not provided.

As far as I can tell, the main motivation for implying the --reuse-delta
option by default is two-fold:

1. The code in send-pack.c that executes 'git pack-objects' is ignorant
of whether the current process is a client pushing to a remote or a
remote sending a fetch or clone to a client.

2. For servers, it is critical that they trust the previously computed
deltas whenever possible, or they could overload their CPU resources.

There's also the side that most servers use repacking logic that will
replace any bad deltas that are sent by clients (or at least, that's the
hope; we've seen that repacks can also pick bad deltas).

The --path-walk option at the moment is not compatible with reachability
bitmaps, so is not planned to be used by Git servers. Thus, we can
reasonably assume (for now) that the --path-walk option is assuming a
client-side scenario, either a push or a repack. The repack option will
be explicit about the --reuse-delta option or not.

One thing to be careful about is background maintenance, which uses a
list of objects instead of refs, so we condition this on the case where
the --path-walk option will be effective by checking that the --revs
option was provided.

Alternative options considered included:

* Adding _another_ config ('pack.reuseDelta=false') to opt-in to this
choice. However, we already have pack.usePathWalk=true as an opt-in to
"do the right thing to make my data small" as far as our internal users
are concerned.

* Modify the chain between builtin/push.c, transport.c, and
builtin/send-pack.c to communicate that we are in "push" mode, not
within a fetch or clone. However, this seemed like overkill. It may be
beneficial in the future to pass through a mode like this, but it does
not meet the bar for the immediate need.

Reviewers, please see git-for-windows#5171 for the baseline
implementation of this feature within Git for Windows and thus
microsoft/git. This feature is still under review upstream.
The microsoft/git fork includes pre- and post-command hooks, with the
initial intention of using these for VFS for Git. In that environment,
these are important hooks to avoid concurrent issues when the
virtualization is incomplete.

However, in the Office monorepo the post-command hook is used in a
different way. A custom hook is used to update the sparse-checkout, if
necessary. To avoid this hook from being incredibly slow on every Git
command, this hook checks for the existence of a "sentinel file" that is
written by a custom post-index-change hook and no-ops if that file does
not exist.

However, even this "no-op" is 200ms due to the use of two scripts (one
simple script in .git/hooks/ does some environment checking and then
calls a script from the working directory which actually contains the
logic).

Add a new config option, 'postCommand.strategy', that will allow for
multiple possible strategies in the future. For now, the one we are
adding is 'worktree-change' which states that we should write a
sentinel file instead of running the 'post-index-change' hook and then
skip the 'post-command' hook if the proper sentinel file doesn't exist.
If it does exist, then delete it and run the hook. This behavior is
_only_ triggered, however, if a part of the index changes that is within
the sparse checkout; If only parts of the index change that are not even
checked out on disk, the hook is still skipped.

I originally planned to put this into the repo-settings, but this caused
the repo settings to load in more cases than they did previously. When
there is an invalid boolean config option, this causes failure in new
places. This was caught by t3007.

This behavior is tested in t0401-post-command-hook.sh.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
This helps t0401 pass while under SANITIZE=leak.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
When the top-level Git process is an alias, it doesn't load much config
and thus the postCommand.strategy config setting is not loaded properly.
This leads to the post-command hook being run more frequently than we want,
including an alias for 'git add' running the hook even when the worktree
did not change.

Similar state is not loaded by 'git version' or 'git typo'.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Add the `cache-local-objects` maintenance task to the list of tasks run
by the `scalar run` command. It's often easier for users to run the
shorter `scalar run` command than the equivalent `git maintenance`
command.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
When the post-command hook runs, we could be in several custom states:

1. The command is a regular builtin, but the repo setup is incomplete.
   (Example: "git version", "git rev-parse HEAD")

2. The command is a dashed command, but the top level process uses a space
   in its call. (Example: "git http-request")

3. The command is an alias that goes to a builtin.

4. The command has no arguments or is only helptext.

Each of these cases leads to a place where we previously had not loaded the
postCommand.strategy config and would execute the post-command hook without
looking for a sentinel file.

There are two fixes here:

First, we use early config parsing so we can get config details without
fully setting up the repository. This is how core.hooksPath works in these
situations.

Second, we need to make sure handle_hook_replacement() is called even when
the repo's gitdir is NULL. This requires a small amount of care to say that
a sentinel file cannot exist if the gitdir isn't set, as we would not have
written one without initializing the gitdir.

This gets all of the desired behaviors we want for this setting without
doing anything extreme with how pre- and post-command hooks run otherwise.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
…windows#720)

Introduce a new maintenance task, `cache-local-objects`, that operates
on Scalar or VFS for Git repositories with a per-volume, shared object
cache (specified by `gvfs.sharedCache`) to migrate packfiles and loose
objects from the repository object directory to the shared cache.

Older versions of `microsoft/git` incorrectly placed packfiles in the
repository object directory instead of the shared cache; this task will
help clean up existing clones impacted by that issue.

Fixes git-for-windows#716
Add ability to run Git commands for Scalar by passing a struct strvec
rather than having to use varargs.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
The microsoft/git fork includes pre- and post-command hooks, with the
initial intention of using these for VFS for Git. In that environment,
these are important hooks to avoid concurrent issues when the
virtualization is incomplete.

However, in the Office monorepo the post-command hook is used in a
different way. A custom hook is used to update the sparse-checkout, if
necessary. To avoid this hook from being incredibly slow on every Git
command, this hook checks for the existence of a "sentinel file" that is
written by a custom post-index-change hook and no-ops if that file does
not exist.

However, even this "no-op" is 200ms due to the use of two scripts (one
simple script in .git/hooks/ does some environment checking and then
calls a script from the working directory which actually contains the
logic).

Add a new config option, 'postCommand.strategy', that will allow for
multiple possible strategies in the future. For now, the one we are
adding is 'post-index-change' which states that we should write a
sentinel file instead of running the 'post-index-change' hook and then
skip the 'post-command' hook if the proper sentinel file doesn't exist.
(If it does exist, then delete it and run the hook.)

--- 

This fork contains changes specific to monorepo scenarios. If you are an
external contributor, then please detail your reason for submitting to
this fork:

* [ ] This is an early version of work already under review upstream.
* [ ] This change only applies to interactions with Azure DevOps and the
      GVFS Protocol.
* [ ] This change only applies to the virtualization hook and VFS for
Git.
* [x] This change only applies to custom bits in the microsoft/git fork.
Add the `--ref-format` option to the `scalar clone` command. This will
allow users to opt-in to creating a Scalar repository using alternative
ref storage backends, such as reftable. Example:

  scalar clone --ref-format reftable $URL

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
This patch series has been long in the making, ever since Johannes
Nicolai and myself spiked this in November/December 2020.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When we are installing a loose object, finalize_object_file() first
checks to see if the contents match what already exists in a loose
object file of the target name. However, this doesn't check if the
target is valid, it assumes the target is valid.

However, in the case of a power outage or something like that, the file
may be corrupt (for example: all NUL bytes). That is a common occurrence
when we are needing to install a loose object _again_: we don't think we
have it already so any copy that exists is probably bogus.

Use the flagged version with FOF_SKIP_COLLISION_CHECK to avoid these
types of errors, as seen in GitHub issue
microsoft#837.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Users sometimes see transient network errors, but they are actually due
to some other problem within the installation of a packfile. Observed
resolutions include freeing up space on a full disk or deleting the
shared object cache because something was broken due to a file
corruption or power outage.

This change only provides the advice to suggest those workarounds to
help users help themselves.

This is our first advice custom to the microsoft/git fork, so I have
partitioned the key away from the others to avoid adjacent change
conflicts (at least until upstream adds a new change at the end of the
alphabetical list).

We could consider providing a tool that does a more robust check of the
shared object cache, but since 'git fsck' isn't safe to run as it may
download missing objects, we do not have that ability at the moment.

The good news is that it is safe to delete and rebuild the shared object
cache as long as all local branches are pushed. The branches must be
pushed because the local .git/objects/ directory is moved to the shared
object cache in the 'cache-local-objects' maintenance task.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Similar to a recent change to avoid the collision check for loose
objects, do the same for prefetch packfiles. This should be more rare,
but the same prefetch packfile could be downloaded from the same cache
server so this isn't out of the range of possibility.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
…-for-windows#832)

Add the `--ref-format` option to the `scalar clone` command. This will allow users to opt-in to creating a Scalar repository using alternative ref storage backends, such as reftable. Example:

```shell
scalar clone --ref-format reftable $URL
```
…ed object cache (git-for-windows#840)

There have been a number of customer-reported problems with errors of
the form

```
error: inflate: data stream error (unknown compression method)
error: unable to unpack a163b1302d4729ebdb0a12d3876ca5bca4e1a8c3 header
error: files 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/pack/tempPacks/t-20260106-014520-049919-0001.temp' and 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/a1/63b1302d4729ebdb0a12d3876ca5bca4e1a8c3' differ in contents
error: gvfs-helper error: 'could not install loose object 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/a1/63b1302d4729ebdb0a12d3876ca5bca4e1a8c3': from GET a163b1302d4729ebdb0a12d3876ca5bca4e1a8c3'
```

or 

```
Receiving packfile 1/1 with 1 objects (bytes received): 17367934, done.
Receiving packfile 1/1 with 1 objects [retry 1/6] (bytes received): 17367934, done.
Waiting to retry after network error (sec): 100% (8/8), done.
Receiving packfile 1/1 with 1 objects [retry 2/6] (bytes received): 17367934, done.
Waiting to retry after network error (sec): 100% (16/16), done.
Receiving packfile 1/1 with 1 objects [retry 3/6] (bytes received): 17367934, done.
```

These are not actually due to network issues, but they look like it
based on the stack that is doing retries. Instead, these actually have
problems when installing the loose object or packfile into the shared
object cache.

The loose objects are hitting issues when installing and the target
loose object is corrupt in some way, such as all NUL bytes because the
disk wasn't flushed when the machine shut down. The error results
because we are doing a collision check without confirming that the
existing contents are valid.

The packfiles may be hitting similar comparison cases, but it is less
likely. We update these packfile installations to also skip the
collision check.

In both cases, if we have a transient network error, we add a new advice
message that helps users with the two most common workarounds:

1. Your disk may be full. Make room.
2. Your shared object cache may be corrupt. Push all branches, delete
it, and fetch to refill it.

I make special note of when the shared object cache doesn't exist and
point that it probably should so the whole repo is suspect at that
point.

* [X] This change only applies to interactions with Azure DevOps and the
      GVFS Protocol.

Resolves git-for-windows#837.
@dscho
Copy link
Member Author

dscho commented Jan 20, 2026

This comment is unfortunately a little bit long. I tried to make it more readable via details and summary tags, but unfortunately those cannot really be nested with quoted areas, so it doesn't work, sadness.

  • 29: 1eb8c1b ! 29: 20dc547 sha1_file: when writing objects, skip the read_object_hook
    @@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
      		if (!read_object_process(source->odb->repo, oid))
      			goto retry;
     @@ object-file.c: retry:
    - int has_loose_object(struct odb_source *source,
    - 		     const struct object_id *oid)
    + int odb_source_loose_has_object(struct odb_source *source,
    + 				const struct object_id *oid)
      {
     -	return check_and_freshen_source(source, oid, 0);
     +	return check_and_freshen_source(source, oid, 0, 0);
    @@ object-file.c: retry:
     @@ object-file.c: static int write_loose_object(struct odb_source *source,
      }
      
    - static int freshen_loose_object(struct object_database *odb,
    --				const struct object_id *oid)
    -+				const struct object_id *oid,
    -+				int skip_virtualized_objects)
    + int odb_source_loose_freshen_object(struct odb_source *source,
    +-				    const struct object_id *oid)
    ++				    const struct object_id *oid,
    ++				    int skip_virtualized_objects)
      {
    - 	odb_prepare_alternates(odb);
    - 	for (struct odb_source *source = odb->sources; source; source = source->next)
    --		if (check_and_freshen_source(source, oid, 1))
    -+		if (check_and_freshen_source(source, oid, 1, skip_virtualized_objects))
    - 			return 1;
    - 	return 0;
    +-	return !!check_and_freshen_source(source, oid, 1);
    ++	return !!check_and_freshen_source(source, oid, 1, skip_virtualized_objects);
      }
    -@@ object-file.c: int stream_loose_object(struct odb_source *source,
    + 
    + int odb_source_loose_write_stream(struct odb_source *source,
    +@@ object-file.c: int odb_source_loose_write_stream(struct odb_source *source,
    + 		die(_("deflateEnd on stream object failed (%d)"), ret);
      	close_loose_object(source, fd, tmp_file.buf);
      
    - 	if (freshen_packed_object(source->odb, oid) ||
    --	    freshen_loose_object(source->odb, oid)) {
    -+	    freshen_loose_object(source->odb, oid, 1)) {
    +-	if (odb_freshen_object(source->odb, oid)) {
    ++	if (odb_freshen_object(source->odb, oid, 1)) {
      		unlink_or_warn(tmp_file.buf);
      		goto cleanup;
      	}
    -@@ object-file.c: int write_object_file(struct odb_source *source,
    +@@ object-file.c: int odb_source_loose_write_object(struct odb_source *source,
    + 	 * it out into .git/objects/??/?{38} file.
      	 */
      	write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
    - 	if (freshen_packed_object(source->odb, oid) ||
    --	    freshen_loose_object(source->odb, oid))
    -+	    freshen_loose_object(source->odb, oid, 1))
    +-	if (odb_freshen_object(source->odb, oid))
    ++	if (odb_freshen_object(source->odb, oid, 1))
      		return 0;
      	if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
      		return -1;
     
    + ## object-file.h ##
    +@@ object-file.h: int odb_source_loose_has_object(struct odb_source *source,
    + 				const struct object_id *oid);
    + 
    + int odb_source_loose_freshen_object(struct odb_source *source,
    +-				    const struct object_id *oid);
    ++				    const struct object_id *oid,
    ++				    int skip_virtualized_objects);
    + 
    + int odb_source_loose_write_object(struct odb_source *source,
    + 				  const void *buf, size_t len,
    +
    + ## odb.c ##
    +@@ odb.c: int odb_has_object(struct object_database *odb, const struct object_id *oid,
    + }
    + 
    + int odb_freshen_object(struct object_database *odb,
    +-		       const struct object_id *oid)
    ++		       const struct object_id *oid,
    ++		       int skip_virtualized_objects)
    + {
    + 	struct odb_source *source;
    + 
    +@@ odb.c: int odb_freshen_object(struct object_database *odb,
    + 
    + 	odb_prepare_alternates(odb);
    + 	for (source = odb->sources; source; source = source->next)
    +-		if (odb_source_loose_freshen_object(source, oid))
    ++		if (odb_source_loose_freshen_object(source, oid, skip_virtualized_objects))
    + 			return 1;
    + 
    + 	return 0;
    +
    + ## odb.h ##
    +@@ odb.h: int odb_has_object(struct object_database *odb,
    + 		   unsigned flags);
    + 
    + int odb_freshen_object(struct object_database *odb,
    +-		       const struct object_id *oid);
    ++		       const struct object_id *oid,
    ++		       int skip_virtualized_objects);
    + 
    + void odb_assert_oid_type(struct object_database *odb,
    + 			 const struct object_id *oid, enum object_type expect);
    +
      ## t/t0410/read-object ##
     @@ t/t0410/read-object: while (1) {
      		system ('git --git-dir="' . $DIR . '" cat-file blob ' . $sha1 . ' | git -c core.virtualizeobjects=false hash-object -w --stdin >/dev/null 2>&1');

This got quite a bit larger because a function that formerly had been file-local is now public. Meaning that its declaration also needs to be edited, this one in the header file.

  • 56: 6f4ff2d ! 56: 6d27bae status: add status serialization mechanism
    @@ Documentation/git-status.adoc: ignored, then the directory is not shown, but all
      	threshold.
      	See also linkgit:git-diff[1] `--find-renames`.
      
    -+--serialize[=<version>]::
    ++`--serialize[=<version>]`::
     +	(EXPERIMENTAL) Serialize raw status results to stdout in a
     +	format suitable for use by `--deserialize`.  Valid values for
     +	`<version>` are "1" and "v1".
     +
    -+--deserialize[=<path>]::
    ++`--deserialize[=<path>]`::
     +	(EXPERIMENTAL) Deserialize raw status results from a file or
     +	stdin rather than scanning the worktree.  If `<path>` is omitted
     +	and `status.deserializePath` is unset, input is read from stdin.
    -+--no-deserialize::
    ++`--no-deserialize`::
     +	(EXPERIMENTAL) Disable implicit deserialization of status results
     +	from the value of `status.deserializePath`.
     +
    - <pathspec>...::
    + `<pathspec>...`::
      	See the 'pathspec' entry in linkgit:gitglossary[7].
      
     @@ Documentation/git-status.adoc: quoted as explained for the configuration variable `core.quotePath`

It is unfortunately not visible in the context, but Upstream changed the surrounding text to enclose the terms in backticks.

  • 87: be563b7 < -: ------------ sha1-file: add function to update existing loose object cache

  • 88: 4e743e6 < -: ------------ packfile: add install_packed_git_and_mru()

  • -: ------------ > 87: 32da4d2 sha1-file: add function to update existing loose object cache

This now unfortunately looks a little strange. Basically we dropped the commit sha1-file: add function to update existing loose object cache via a fixup! commit. The other commit got rewritten so much that range-diff requires a --creation-factor=99 to recognize it:

Range-diff
  • 1: be563b7 ! 1: 32da4d2 sha1-file: add function to update existing loose object cache

    @@ Commit message
         Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
     
      ## object-file.c ##
    -@@ object-file.c: struct oidtree *odb_loose_cache(struct odb_source *source,
    - 	return source->loose_objects_cache;
    +@@ object-file.c: struct oidtree *odb_source_loose_cache(struct odb_source *source,
    + 	return source->loose->cache;
      }
      
    -+void odb_loose_cache_add_new_oid(struct odb_source *source,
    -+				 const struct object_id *oid)
    ++void odb_source_loose_cache_add_new_oid(struct odb_source *source,
    ++					const struct object_id *oid)
     +{
    -+	struct oidtree *cache = odb_loose_cache(source, oid);
    ++	struct oidtree *cache = odb_source_loose_cache(source, oid);
     +	append_loose_object(oid, NULL, cache);
     +}
     +
    - void odb_clear_loose_cache(struct odb_source *source)
    + static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
      {
    - 	oidtree_clear(source->loose_objects_cache);
    + 	oidtree_clear(loose->cache);
     
      ## object-file.h ##
    -@@ object-file.h: struct odb_source;
    - struct oidtree *odb_loose_cache(struct odb_source *source,
    - 				const struct object_id *oid);
    +@@ object-file.h: int odb_source_loose_write_stream(struct odb_source *source,
    + struct oidtree *odb_source_loose_cache(struct odb_source *source,
    + 				       const struct object_id *oid);
      
     +/*
     + * Add a new object to the loose object cache (possibly after the
     + * cache was populated).  This might be used after dynamically
     + * fetching a missing object.
     + */
    -+void odb_loose_cache_add_new_oid(struct odb_source *source,
    ++void odb_source_loose_cache_add_new_oid(struct odb_source *source,
     +				 const struct object_id *oid);
     +
    - /* Empty the loose object cache for the specified object directory. */
    - void odb_clear_loose_cache(struct odb_source *source);
    - 
    + /*
    +  * Put in `buf` the name of the file in the local object database that
    +  * would be used to store a loose object with the specified oid.
  • 90: b46ad0a ! 118: d96249f gvfs-helper: create tool to fetch objects using the GVFS Protocol
    ```diff
    @@ gvfs-helper-client.c (new)
    + if (!skip_prefix(line, "loose ", &v1_oid))
    + BUG("update_loose_cache: invalid line '%s'", line);
    +
    -+ odb_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
    -+}
    -+
    -+/*
    -+ * Update the packed-git list to include the newly created packfile.
    -+ */
    -+static void gh_client__update_packed_git(const char *line)
    -+{
    -+ struct strbuf path = STRBUF_INIT;
    -+ const char *v1_filename;
    -+ struct packed_git p;
    -+ int is_local;
    -+
    -+ if (!skip_prefix(line, "packfile ", &v1_filename))
    -+ BUG("update_packed_git: invalid line '%s'", line);
    -+
    -+ /

    -+ * ODB[0] is the local .git/objects. All others are alternates.
    -+ /
    -+ is_local = (gh_client__chosen_odb == the_repository->objects->sources);
    -+
    -+ strbuf_addf(&path, "%s/pack/%s",
    -+ gh_client__chosen_odb->path, v1_filename);
    -+ strbuf_strip_suffix(&path, ".pack");
    -+ strbuf_addstr(&path, ".idx");
    -+
    -+ p = add_packed_git(the_repository, path.buf, path.len, is_local);
    -+ if (p)
    -+ packfile_store_add_pack_also_to_mru(the_repository, p);
    -+ strbuf_release(&path);
    ++ odb_source_loose_cache_add_new_oid(gh_client__chosen_odb, &oid);
    +}
    +
    +/

    @@ gvfs-helper-client.c (new)
    + }
    +
    + else if (starts_with(line, "packfile")) {
    -+ gh_client__update_packed_git(line);
    + ghc |= GHC__CREATED__PACKFILE;
    + *p_nr_packfile += 1;
    + }
    @@ gvfs-helper-client.c (new)
    + }
    + }
    +
    ++ if (ghc & GHC__CREATED__PACKFILE)
    ++ packfile_store_reprepare(the_repository->objects->packfiles);
    ++
    + *p_ghc = ghc;
    +
    + return err;
    @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    const struct object_id *real = oid;
    int already_retried = 0;
    int tried_hook = 0;
    --
    + int tried_gvfs_helper = 0;

      	if (flags & OBJECT_INFO_LOOKUP_REPLACE)
      		real = lookup_replace_object(odb->repo, oid);
     @@ odb.c: retry:
    - 	}
    + 	odb_prepare_alternates(odb);
      
      	while (1) {
     +		extern int core_use_gvfs_helper;
    -+
    - 		if (find_pack_entry(odb->repo, real, &e))
    - 			break;
    + 		struct odb_source *source;
      
    + 		if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
     @@ odb.c: retry:
    - 		if (!loose_object_info(odb->repo, real, oi, flags))
    - 			return 0;
    + 			if (!odb_source_loose_read_object_info(source, real, oi, flags))
    + 				return 0;
      
     +		if (core_use_gvfs_helper && !tried_gvfs_helper) {
     +			enum gh_client__created ghc;
    @@ odb.c: retry:
      		/* Not a loose object; someone else may have just packed it. */
      		if (!(flags & OBJECT_INFO_QUICK)) {
      			odb_reprepare(odb->repo->objects);
    - 			if (find_pack_entry(odb->repo, real, &e))
    - 				break;
    + 			if (!packfile_store_read_object_info(odb->packfiles, real, oi, flags))
    + 				return 0;
      			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
     +				// TODO Assert or at least trace2 if gvfs-helper
     +				// TODO was tried and failed and then read-object-hook
    ```
    

Here, a function was dropped via a fix up commit.

  • 98: de4f7af ! 126: 14aea24 test-gvfs-prococol, t5799: tests for gvfs-helper
    diff @@ t/t5799-gvfs-helper.sh (new) + >OUT.output 2>OUT.stderr +' + ++trace_has_queue_oid () { ++ oid=$1 ++ grep "gh_client__queue_oid: $oid" ++} ++ ++trace_has_immediate_oid () { ++ oid=$1 ++ grep "gh_client__get_immediate: $oid" ++} ++ +test_expect_success 'integration: fully implicit: diff 2 commits' ' + test_when_finished "per_test_cleanup" && + start_gvfs_protocol_server && + + # Implicitly demand-load everything without any pre-seeding. + # ++ GIT_TRACE2_EVENT="$(pwd)/diff-trace.txt" \ + git -C "$REPO_T1" -c core.useGVFSHelper=true \ + diff $(cat m1.branch)..$(cat m3.branch) \ -+ >OUT.output 2>OUT.stderr ++ >OUT.output 2>OUT.stderr && ++ ++ oid=$(git -C "$REPO_SRC" rev-parse main:file9.txt.t) && ++ trace_has_queue_oid $oid <diff-trace.txt && ++ ! trace_has_immediate_oid $oid <diff-trace.txt +' + +test_done

This came from a fixup commit.

We'll skip over the workflows fixups that have been squashed into the original commits.

  • 166: 339f964 < -: ------------ scalar: enable untracked cache unconditionally

  • -: ------------ > 193: 0c58fd6 scalar: enable untracked cache unconditionally

Here comes that range-diff
  • 1: 339f964 ! 1: 0c58fd6 scalar: enable untracked cache unconditionally

    @@ Commit message
     
      ## scalar.c ##
     @@ scalar.c: static int set_recommended_config(int reconfigure)
    - 		{ "core.FSCache", "true", 1 },
    - 		{ "core.multiPackIndex", "true", 1 },
    - 		{ "core.preloadIndex", "true", 1 },
    + 		{ "core.configWriteLockTimeoutMS", "150" },
    + 		{ "core.logAllRefUpdates", "true" },
    + 		{ "core.safeCRLF", "false" },
    ++		{ "core.untrackedCache", "true" },
    + 		{ "credential.https://dev.azure.com.useHttpPath", "true" },
    + 		{ "feature.experimental", "false" },
    + 		{ "feature.manyFiles", "false" },
    +@@ scalar.c: static int set_recommended_config(int reconfigure)
    + 		{ "status.aheadBehind", "false" },
    + 
    + 		/* platform-specific */
     -#ifndef WIN32
    - 		{ "core.untrackedCache", "true", 1 },
    +-		{ "core.untrackedCache", "true" },
     -#else
     -		/*
     -		 * Unfortunately, Scalar's Functional Tests demonstrated
    @@ scalar.c: static int set_recommended_config(int reconfigure)
     -		 * Therefore, with a sad heart, we disable this very useful
     -		 * feature on Windows.
     -		 */
    --		{ "core.untrackedCache", "false", 1 },
    --#endif
    - 		{ "core.logAllRefUpdates", "true", 1 },
    - 		{ "credential.https://dev.azure.com.useHttpPath", "true", 1 },
    - 		{ "credential.validate", "false", 1 }, /* GCM4W-only */
    +-		{ "core.untrackedCache", "false" },
    +-
    ++#ifdef WIN32
    + 		/* Other Windows-specific required settings: */
    + 		{ "http.sslBackend", "schannel" },
    + #endif
  • 238: 99b6e73 < -: ------------ mimalloc: prevent crashes in the post-command hook handling

This is actually in the mimalloc version 2.2.6; I managed to upstream this patch, just not into Git, but into mimalloc instead.

"That's all, folks!"

@dscho dscho requested a review from mjcheetham January 20, 2026 14:48
@dscho dscho self-assigned this Jan 20, 2026
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.