Skip to content

Rebase to Git for Windows 2.56.0-rc0 - #984

Merged
Johannes Schindelin (dscho) merged 402 commits into
vfs-2.56.0-rc0from
tentative/vfs-2.56.0-rc0
Sep 23, 2026
Merged

Johannes Schindelin (dscho) merged 402 commits into
vfs-2.56.0-rc0from
tentative/vfs-2.56.0-rc0

Conversation

@dscho

@dscho Johannes Schindelin (dscho) commented Sep 16, 2026 •

Copy link
Copy Markdown
Member
Range-diff relative to vfs-2.55.0
  • 2: 05a95ad = 1: 46eb66c t5300: confirm failure of git index-pack when non-idx suffix requested

  • 3: 4705e95 = 2: 7500633 t: remove advice from some tests

  • 1: c8fc5a2 = 3: 48e4d91 sparse-index.c: fix use of index hashes in expand_index

  • 5: 51bf99d ! 4: 43c6a23 index-pack: disable rev-index if index file has non .idx suffix

    @@ builtin/index-pack.c: int cmd_index_pack(int argc,
      	int hash_algo = 0;
     +	int dash_o = 0;
      
    - 	/*
    - 	 * index-pack never needs to fetch missing objects except when
    + 	show_usage_if_asked(argc, argv, index_pack_usage);
    + 
     @@ builtin/index-pack.c: int cmd_index_pack(int argc,
      				if (index_name || (i+1) >= argc)
      					usage(index_pack_usage);
  • 6: 59be980 = 5: 931ef57 trace2: prefetch value of GIT_TRACE2_DST_DEBUG at startup

  • -: ------------ > 6: 3bb63cd survey: calculate more stats on refs

  • -: ------------ > 7: a951c83 survey: show some commits/trees/blobs histograms

  • -: ------------ > 8: 15a44ba survey: add vector of largest objects for various scaling dimensions

  • -: ------------ > 9: 3e6cb79 survey: add pathname of blob or tree to large_item_vec

  • -: ------------ > 10: 6a6ebb7 survey: add commit-oid to large_item detail

  • -: ------------ > 11: 04f8577 survey: add commit name-rev lookup to each large_item

  • -: ------------ > 12: eb19db5 survey: add --no-name-rev option

  • 14: 98073ca ! 13: 26da3b5 survey: started TODO list at bottom of source file

    @@ Commit message
         survey: started TODO list at bottom of source file
     
      ## builtin/survey.c ##
    -@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
    - 	clear_survey_context(&ctx);
    - 	return 0;
    +@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix,
    + 	strvec_clear(&child_argv);
    + 	return 1;
      }
     +
     +/*
  • 15: d034930 ! 14: 576c08a survey: expanded TODO list at the bottom of the source file

    @@ Commit message
         Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
     
      ## builtin/survey.c ##
    -@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
    +@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix,
      }
      
      /*
    @@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix
     - * going to have a long "haves" message, so every fetch will be slow
     - * just because of that overhead (not counting new objects to be
     - * downloaded).
    +- *
    +- * Note that the local set of tags in "refs/tags/" is a union over all
    +- * remotes.  However, since most people only have one remote, we can
    +- * probaly estimate the overhead value directly from the size of the
    +- * set of "refs/tags/" that we visited while building the `ref_info`
    +- * and `ref_array` and not need to ask the remote.
     + *    of the response is related to the set of haves on the server.
     + *    You can see this in `GIT_TRACE_CURL=1 git fetch`. We get a
     + *    `ls-refs` payload that lists all of the branches and tags on the
    @@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix
     + *    on a remote, you're going to have a long "haves" message, so
     + *    every fetch will be slow just because of that overhead (not
     + *    counting new objects to be downloaded).
    -  *
    -- * Note that the local set of tags in "refs/tags/" is a union over all
    -- * remotes.  However, since most people only have one remote, we can
    -- * probaly estimate the overhead value directly from the size of the
    -- * set of "refs/tags/" that we visited while building the `ref_info`
    -- * and `ref_array` and not need to ask the remote.
    ++ *
     + *    Note that the local set of tags in "refs/tags/" is a union over
     + *    all remotes.  However, since most people only have one remote,
     + *    we can probaly estimate the overhead value directly from the
  • 16: 60aae5c ! 15: 2b8b68a survey: expanded TODO with more notes

    @@ Commit message
         Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
     
      ## builtin/survey.c ##
    -@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
    +@@ builtin/survey.c: int cmd_survey(int argc, const char **argv, const char *prefix,
       *    size of the set of "refs/tags/" that we visited while building
       *    the `ref_info` and `ref_array` and not need to ask the remote.
       *
  • 199: 8454287 = 16: e3e3851 cat_one_file(): make it easy to see that the size variable is initialized

  • 212: 3dc3f08 = 17: d63a724 fsck: avoid using an uninitialized variable

  • 215: dfd98c7 = 18: a361fa5 load_revindex_from_disk(): avoid accessing uninitialized data

  • 201: 08c6fe8 = 19: 6588c6c revision: defensive programming

  • 202: 0ede4f0 = 20: ea5e631 get_parent(): defensive programming

  • 203: d1559b6 = 21: bd8de2b fetch-pack: defensive programming

  • 205: 6458e23 = 22: 6f8a428 unparse_commit(): defensive programming

  • 207: 7396e03 = 23: 7e7cb7c verify_commit_graph(): defensive programming

  • 209: 59e1d5f = 24: 85c2ba1 stash: defensive programming

  • 211: 1a1ed99 = 25: 18015c7 stash: defensive programming

  • 214: 3b147ac ! 26: f1c57ea push: defensive programming

    @@ Commit message
     
      ## builtin/push.c ##
     @@ builtin/push.c: static void refspec_append_mapped(struct refspec *refspec, const char *ref,
    - 	if (push_default == PUSH_DEFAULT_UPSTREAM &&
    + 	if (cfg->push_default == PUSH_DEFAULT_UPSTREAM &&
      	    skip_prefix(matched->name, "refs/heads/", &branch_name)) {
      		struct branch *branch = branch_get(branch_name);
     -		if (branch->merge_nr == 1 && branch->merge[0]->src) {
  • 217: b340977 = 27: 7de2e14 test-tool repository: check return value of lookup_commit()

  • 218: c49b40b = 28: ed03b01 fetch: defensive programming

  • 221: 989f755 = 29: 476eec5 shallow: handle missing shallow commits gracefully

  • 219: 40d8927 = 30: 4a57bae load_pack_mtimes_file(): avoid accessing uninitialized data

  • 222: 3c3c954 = 31: e8e1878 inherit_tracking(): defensive programming

  • 224: a84a0a5 ! 32: e5f4a77 commit-graph: suppress warning about using a stale stack addresses

    @@ commit-graph.c: int write_commit_graph(struct odb_source *source,
      	g = prepare_commit_graph(ctx.r);
      	for (struct commit_graph *chain = g; chain; chain = chain->base_graph)
     +		/* Intentional: codeql[cpp/stack-address-escape] */
    - 		g->topo_levels = &topo_levels;
    + 		chain->topo_levels = &topo_levels;
      
      	if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
  • 204: 6b367b9 = 33: ddf80bb codeql: run static analysis as part of CI builds

  • 206: f7d940e = 34: 0b9521f codeql: publish the sarif file as build artifact

  • 208: 708febc = 35: 3925ef3 codeql: disable a couple of non-critical queries for now

  • 210: 5242574 = 36: 6287b25 date: help CodeQL understand that there are no leap-year issues here

  • 213: 47a2e33 = 37: b5bc320 help: help CodeQL understand that consuming envvars is okay here

  • 216: cb2a751 = 38: de364d2 ctype: help CodeQL understand that sane_istest() does not access array past end

  • 220: 629c2d7 = 39: 2929e38 ctype: accommodate for CodeQL misinterpreting the z in mallocz()

  • 223: c3ea9ed ! 40: 86dfdfd strbuf_read: help with CodeQL misunderstanding that strbuf_read() does NUL-terminate correctly

    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
      			ctx->current_fixup_count = 1;
     -			while ((p = strchr(p, '\n'))) {
     +			while ((p = strchr(p, '\n'))) { // CodeQL [SM01932] justification: CodeQL is wrong here because the value is read from a file via strbuf_read() which does NUL-terminate the string, something CodeQL fails to understand
    - 				ctx->current_fixup_count++;
    - 				p++;
    - 			}
    + 				/*
    + 				 * Older versions of git accidentally
    + 				 * inserted blank lines when a fixup
     
      ## strvec.c ##
     @@ strvec.c: void strvec_push_nodup(struct strvec *array, char *value)
  • 225: 2745bbf = 41: 3d565bc codeql: also check JavaScript code

  • 4: 23959ce ! 42: 1c354c3 t1092: add test for untracked files and directories

    @@ t/t1092-sparse-checkout-compatibility.sh: test_expect_success 'root directory ca
     +	# Untracked files outside sparse cone are not deleted
     +	run_on_sparse touch folder1/another &&
     +	test_sparse_match git sparse-checkout set folder2 &&
    -+	grep "directory ${SQ}folder1/${SQ} contains untracked files" sparse-checkout-err &&
    ++	test_grep "directory ${SQ}folder1/${SQ} contains untracked files" sparse-checkout-err &&
     +	run_on_sparse test_path_exists folder1/another
     +'
     +
  • 7: 3a96a80 < -: ------------ survey: calculate more stats on refs

  • 8: 70c4057 < -: ------------ survey: show some commits/trees/blobs histograms

  • 9: 7a0e7a5 < -: ------------ survey: add vector of largest objects for various scaling dimensions

  • 10: cce6031 < -: ------------ survey: add pathname of blob or tree to large_item_vec

  • 11: 6ae68e1 < -: ------------ survey: add commit-oid to large_item detail

  • 12: c45f59a < -: ------------ survey: add commit name-rev lookup to each large_item

  • 13: fe472de < -: ------------ survey: add --no-name-rev option

  • 17: e7baa92 = 43: f7509b2 reset --stdin: trim carriage return from the paths

  • 18: 3f93442 ! 44: 8898d56 Identify microsoft/git via a distinct version suffix

    @@ Commit message
      ## GIT-VERSION-GEN ##
     @@
      
    - DEF_VER=v2.55.0
    + DEF_VER=v2.56.0-rc0
      
     +# Identify microsoft/git via a distinct version suffix
     +DEF_VER=$DEF_VER.vfs.0.0
  • 19: c0019ec = 45: 20d9942 gvfs: ensure that the version is based on a GVFS tag

  • 20: 2db17f6 = 46: de0389c gvfs: add a GVFS-specific header file

  • 21: 2541c2b = 47: 9e3fc95 gvfs: add the core.gvfs config setting

  • 22: fea2984 ! 48: 7bf4222 gvfs: add the feature to skip writing the index' SHA-1

    @@ repo-settings.c: void prepare_repo_settings(struct repository *r)
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
    -   't1014-read-tree-confusing.sh',
        't1015-read-index-unmerged.sh',
        't1016-compatObjectFormat.sh',
    -+  't1017-read-tree-skip-sha-on-read.sh',
    +   't1017-cat-file-remote-object-info.sh',
    ++  't1018-read-tree-skip-sha-on-read.sh',
        't1020-subdirectory.sh',
        't1022-read-tree-partial-clone.sh',
        't1050-large.sh',
     
    - ## t/t1017-read-tree-skip-sha-on-read.sh (new) ##
    + ## t/t1018-read-tree-skip-sha-on-read.sh (new) ##
     @@
     +#!/bin/sh
     +
  • 23: 910f887 = 49: 27e8992 gvfs: add the feature that blobs may be missing

  • 24: 4e6ade6 = 50: ec32376 gvfs: prevent files to be deleted outside the sparse checkout

  • 25: e8d4c0c = 51: 08088e0 gvfs: optionally skip reachability checks/upload pack during fetch

  • 26: 4fa282a = 52: 11a158f gvfs: ensure all filters and EOL conversions are blocked

  • 27: 178e0a5 ! 53: 89c477d gvfs: allow "virtualizing" objects

    @@ connected.c: int check_connected(oid_iterate_fn fn, void *cb_data,
      		opt = &defaults;
     
      ## environment.c ##
    -@@ environment.c: enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
    - enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
    +@@ environment.c: char *check_roundtrip_encoding;
    + #endif
      int grafts_keep_true_parents;
      unsigned long pack_size_limit_cfg;
     +int core_virtualize_objects;
    @@ gvfs.h: struct repository;
     
      ## odb.c ##
     @@
    - #include "dir.h"
    + #include "config.h"
      #include "environment.h"
      #include "gettext.h"
     +#include "gvfs.h"
    + #include "hashmap.h"
      #include "hex.h"
     +#include "hook.h"
    - #include "khash.h"
      #include "lockfile.h"
      #include "loose.h"
    + #include "midx.h"
     @@
      #include "submodule.h"
      #include "tmp-objdir.h"
    @@ odb.c
     +#include "trace.h"
      #include "write-or-die.h"
      
    - KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
    + /*
     @@ odb.c: void disable_obj_read_lock(void)
      	pthread_mutex_destroy(&obj_read_mutex);
      }
    @@ odb.c: void disable_obj_read_lock(void)
     +	return ret;
     +}
     +
    - int fetch_if_missing = 1;
    - 
      static int register_all_submodule_sources(struct object_database *odb)
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
      {
    - 	const struct object_id *real = oid;
    + 	int ret = odb->submodule_source_paths.nr;
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 	enum odb_read_status ret;
      	int already_retried = 0;
    + 	bool corrupt = false;
     +	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,
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
      	if (is_null_oid(real))
      		return -1;
      
     +retry:
    - 	if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags))
    + 	if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags, NULL))
      		return 0;
      
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 				if (!odb_source_read_object_info(source, real, oi,
    - 								 flags | OBJECT_INFO_SECOND_READ))
    - 					return 0;
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 				if (ret != ODB_READ_NOT_FOUND)
    + 					corrupt = true;
    + 			}
     +			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
     +				tried_hook = 1;
     +				if (!run_read_object_hook(odb->repo, oid))
  • 28: 7e65dde ! 54: 400e1cd Hydrate missing loose objects in check_and_freshen()

    @@ contrib/long-running-read-object/example.pl (new)
     
      ## odb.c ##
     @@
    + #include "odb.h"
      #include "odb/source-inmemory.h"
    - #include "packfile.h"
      #include "path.h"
     +#include "pkt-line.h"
      #include "promisor-remote.h"
    @@ odb.c: void disable_obj_read_lock(void)
     -	return ret;
     -}
     -
    - int fetch_if_missing = 1;
    - 
      static int register_all_submodule_sources(struct object_database *odb)
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 					return 0;
    + {
    + 	int ret = odb->submodule_source_paths.nr;
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 			}
      			if (gvfs_virtualize_objects(odb->repo) && !tried_hook) {
      				tried_hook = 1;
     -				if (!run_read_object_hook(odb->repo, oid))
    @@ odb/source-loose.c: static int odb_source_loose_freshen_object(struct odb_source
      	struct odb_source_loose *loose = odb_source_loose_downcast(source);
      	static struct strbuf path = STRBUF_INIT;
     +	int ret, tried_hook = 0;
    -+
      	odb_loose_path(loose, &path, oid);
    --	return !!check_and_freshen_file(path.buf, 1);
    +-	return !!check_and_freshen_file(path.buf, 1, mtime);
     +retry:
    -+	ret = check_and_freshen_file(path.buf, 1);
    ++	ret = !!check_and_freshen_file(path.buf, 1, mtime);
     +	if (!ret && gvfs_virtualize_objects(source->odb->repo) && !tried_hook) {
     +		tried_hook = 1;
     +		if (!read_object_process(source->odb->repo, oid))
    @@ odb/source-loose.c: static int odb_source_loose_freshen_object(struct odb_source
     +	return ret;
      }
      
    - static int odb_source_loose_write_object(struct odb_source *source,
    + /* Finalize a file on disk, and close it. */
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
  • 29: 4c7f91a ! 55: e0f0518 sha1_file: when writing objects, skip the read_object_hook

    @@ Commit message
     
         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
    - ## object-file.c ##
    -@@ object-file.c: int odb_source_loose_write_stream(struct odb_source_loose *loose,
    - 		die(_("deflateEnd on stream object failed (%d)"), ret);
    - 	close_loose_object(loose, fd, tmp_file.buf);
    - 
    --	if (odb_freshen_object(loose->base.odb, oid)) {
    -+	if (odb_freshen_object(loose->base.odb, oid, 1)) {
    - 		unlink_or_warn(tmp_file.buf);
    - 		goto cleanup;
    - 	}
    -
      ## odb.c ##
     @@ odb.c: int odb_has_object(struct object_database *odb, const struct object_id *oid,
      }
    @@ odb.c: int odb_has_object(struct object_database *odb, const struct object_id *o
     +		       int skip_virtualized_objects)
      {
      	struct odb_source *source;
    - 	odb_prepare_alternates(odb);
      	for (source = odb->sources; source; source = source->next)
    --		if (odb_source_freshen_object(source, oid))
    -+		if (odb_source_freshen_object(source, oid, skip_virtualized_objects))
    +-		if (odb_source_freshen_object(source, oid, NULL))
    ++		if (odb_source_freshen_object(source, oid, NULL,
    ++					      skip_virtualized_objects))
      			return 1;
      	return 0;
      }
    +@@ odb.c: int odb_write_object_ext(struct object_database *odb,
    + 	 * We can skip the write in case we already have the object available.
    + 	 * In that case, we only freshen its mtime.
    + 	 */
    +-	if (odb_freshen_object(odb, oid))
    ++	if (odb_freshen_object(odb, oid, 1))
    + 		return 0;
    + 
    + 	if (compat) {
     
      ## odb.h ##
     @@ odb.h: int odb_has_object(struct object_database *odb,
    @@ odb.h: int odb_has_object(struct object_database *odb,
     
      ## odb/source-files.c ##
     @@ odb/source-files.c: static int odb_source_files_find_abbrev_len(struct odb_source *source,
    - }
      
      static int odb_source_files_freshen_object(struct odb_source *source,
    --					   const struct object_id *oid)
    -+					   const struct object_id *oid,
    + 					   const struct object_id *oid,
    +-					   const time_t *mtime)
    ++					   const time_t *mtime,
     +					   int skip_virtualized_objects)
      {
      	struct odb_source_files *files = odb_source_files_downcast(source);
    - 	if (packfile_store_freshen_object(files->packed, oid) ||
    --	    odb_source_freshen_object(&files->loose->base, oid))
    -+	    odb_source_freshen_object(&files->loose->base, oid, skip_virtualized_objects))
    +-	if (odb_source_freshen_object(&files->packed->base, oid, mtime) ||
    +-	    odb_source_freshen_object(&files->loose->base, oid, mtime))
    ++	if (odb_source_freshen_object(&files->packed->base, oid, mtime,
    ++				      skip_virtualized_objects) ||
    ++	    odb_source_freshen_object(&files->loose->base, oid, mtime,
    ++				      skip_virtualized_objects))
      		return 1;
      	return 0;
      }
     
      ## odb/source-inmemory.c ##
     @@ odb/source-inmemory.c: static int odb_source_inmemory_write_object_stream(struct odb_source *source,
    - }
      
      static int odb_source_inmemory_freshen_object(struct odb_source *source,
    --					      const struct object_id *oid)
    -+					      const struct object_id *oid,
    + 					      const struct object_id *oid,
    +-					      const time_t *mtime UNUSED)
    ++					      const time_t *mtime UNUSED,
     +					      int skip_virtualized_objects UNUSED)
      {
      	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
    @@ odb/source-inmemory.c: static int odb_source_inmemory_write_object_stream(struct
     
      ## odb/source-loose.c ##
     @@ odb/source-loose.c: static int odb_source_loose_count_objects(struct odb_source *source,
    - }
      
      static int odb_source_loose_freshen_object(struct odb_source *source,
    --					   const struct object_id *oid)
    -+					   const struct object_id *oid,
    + 					   const struct object_id *oid,
    +-					   const time_t *mtime)
    ++					   const time_t *mtime,
     +					   int skip_virtualized_objects)
      {
      	struct odb_source_loose *loose = odb_source_loose_downcast(source);
    @@ odb/source-loose.c: static int odb_source_loose_count_objects(struct odb_source
     @@ odb/source-loose.c: static int odb_source_loose_freshen_object(struct odb_source *source,
      	odb_loose_path(loose, &path, oid);
      retry:
    - 	ret = check_and_freshen_file(path.buf, 1);
    + 	ret = !!check_and_freshen_file(path.buf, 1, mtime);
     -	if (!ret && gvfs_virtualize_objects(source->odb->repo) && !tried_hook) {
     +	if (!ret && gvfs_virtualize_objects(source->odb->repo) &&
     +	    !skip_virtualized_objects && !tried_hook) {
      		tried_hook = 1;
      		if (!read_object_process(source->odb->repo, oid))
      			goto retry;
    -@@ odb/source-loose.c: static 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 (odb_freshen_object(source->odb, oid))
    -+	if (odb_freshen_object(source->odb, oid, 1))
    - 		return 0;
    - 	if (write_loose_object(loose, oid, hdr, hdrlen, buf, len, 0, flags))
    - 		return -1;
    +@@ odb/source-loose.c: static int odb_source_loose_write_object_stream(struct odb_source *source,
    + 		die(_("deflateEnd on stream object failed (%d)"), ret);
    + 	close_loose_object(loose, fd, tmp_file.buf);
    + 
    +-	if (odb_freshen_object(loose->base.odb, oid)) {
    ++	if (odb_freshen_object(loose->base.odb, oid, 1)) {
    + 		unlink_or_warn(tmp_file.buf);
    + 		goto cleanup;
    + 	}
    +
    + ## odb/source-packed.c ##
    +@@ odb/source-packed.c: static int odb_source_packed_find_abbrev_len(struct odb_source *source,
    + 
    + static int odb_source_packed_freshen_object(struct odb_source *source,
    + 					    const struct object_id *oid,
    +-					    const time_t *mtime)
    ++					    const time_t *mtime,
    ++					    int skip_virtualized_objects UNUSED)
    + {
    + 	struct odb_source_packed *packed = odb_source_packed_downcast(source);
    + 	struct utimbuf times, *timesp = NULL;
     
      ## odb/source.h ##
     @@ odb/source.h: struct odb_source {
    - 	 * has been freshened.
      	 */
      	int (*freshen_object)(struct odb_source *source,
    --			      const struct object_id *oid);
    -+			      const struct object_id *oid,
    + 			      const struct object_id *oid,
    +-			      const time_t *mtime);
    ++			      const time_t *mtime,
     +			      int skip_virtualized_objects);
      
      	/*
      	 * This callback is expected to persist the given object into the
     @@ odb/source.h: static inline int odb_source_find_abbrev_len(struct odb_source *source,
    -  * not exist.
       */
      static inline int odb_source_freshen_object(struct odb_source *source,
    --					    const struct object_id *oid)
    -+					    const struct object_id *oid,
    + 					    const struct object_id *oid,
    +-					    const time_t *mtime)
    ++					    const time_t *mtime,
     +					    int skip_virtualized_objects)
      {
    --	return source->freshen_object(source, oid);
    -+	return source->freshen_object(source, oid, skip_virtualized_objects);
    +-	return source->freshen_object(source, oid, mtime);
    ++	return source->freshen_object(source, oid, mtime,
    ++				      skip_virtualized_objects);
      }
      
      /*
    @@ t/unit-tests/u-odb-inmemory.c: void test_odb_inmemory__freshen_object(void)
      	const char *end;
      
      	cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
    --	cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid), 0);
    -+	cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid, 0), 0);
    +-	cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid, NULL), 0);
    ++	cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid, NULL, 0), 0);
      
    - 	cl_must_pass(odb_source_write_object(&source->base, "foobar",
    - 					     strlen("foobar"), OBJ_BLOB,
    - 					     &written_oid, NULL, 0));
    + 	cl_assert_write_object(source, "foobar", OBJ_BLOB, &written_oid);
      	cl_assert_equal_i(odb_source_freshen_object(&source->base,
    --						    &written_oid), 1);
    -+						    &written_oid, 0), 1);
    +-						    &written_oid, NULL), 1);
    ++						    &written_oid, NULL, 0), 1);
      
      	odb_source_free(&source->base);
      }
  • 30: 39c1754 ! 56: f31e54c gvfs: add global command pre and post hook procs

    @@ git.c: static int handle_alias(struct strvec *args, struct string_list *expanded
     +
     +	/*
     +	 * Only run post_command if pre_command succeeded in this process
    ++	 * and we haven't attempted post_command yet.
     +	 */
     +	if (!run_post_hook)
     +		return 0;
    ++	run_post_hook = 0;
    ++
     +	lock = getenv("COMMAND_HOOK_LOCK");
     +	if (!lock || strcmp(lock, "true"))
     +		return 0;
    @@ git.c: static int handle_alias(struct strvec *args, struct string_list *expanded
     +	strvec_pushf(&opt.args, "--exit_code=%u", exit_code);
     +	ret = run_hooks_opt(r, "post-command", &opt);
     +
    -+	run_post_hook = 0;
     +	strvec_clear(&sargv);
     +	setenv("COMMAND_HOOK_LOCK", "false", 1);
     +	return ret;
  • 31: 96f43a6 = 57: a5eee4d t0400: verify that the hook is called correctly from a subdirectory

  • 32: c4ebeb4 = 58: f1116c1 t0400: verify core.hooksPath is respected by pre-command

  • 33: a025fa9 = 59: 8279bf4 Pass PID of git process to hooks.

  • 34: ad4f004 = 60: 5384825 sparse-checkout: make sure to update files with a modify/delete conflict

  • 35: ca60cb7 = 61: faa9218 worktree: allow in Scalar repositories

  • 36: 80b71d8 = 62: d6f76b0 sparse-checkout: avoid writing entries with the skip-worktree bit

  • 37: 43241a4 = 63: c0c1977 Do not remove files outside the sparse-checkout

  • 38: db40f09 < -: ------------ send-pack: do not check for sha1 file when GVFS_MISSING_OK set

  • -: ------------ > 64: 2e68218 send-pack: do not check for sha1 file when GVFS_MISSING_OK set

  • 39: a5b4adf = 65: 1604701 gvfs: allow corrupt objects to be re-downloaded

  • 40: 85c49ed = 66: 9b19871 cache-tree: remove use of strbuf_addf in update_one

  • 41: c1c5198 ! 67: bc7ba63 gvfs: block unsupported commands when running in a GVFS repo

    @@ builtin/gc.c
      #include "config.h"
      #include "tempfile.h"
      #include "lockfile.h"
    -@@ builtin/gc.c: static int gc_foreground_tasks(struct maintenance_run_opts *opts,
    +@@ builtin/gc.c: static int maintenance_task_odb(struct maintenance_run_opts *opts,
      int cmd_gc(int argc,
      	   const char **argv,
      	   const char *prefix,
    @@ builtin/gc.c: static int gc_foreground_tasks(struct maintenance_run_opts *opts,
      	int aggressive = 0;
      	int force = 0;
     @@ builtin/gc.c: int cmd_gc(int argc,
    - 	if (opts.quiet)
    - 		strvec_push(&repack_args, "-q");
    + 	if (cfg.prune_expire && parse_expiry_date(cfg.prune_expire, &dummy))
    + 		die(_("failed to parse prune expiry value %s"), cfg.prune_expire);
      
    -+	if ((!opts.auto_flag || (opts.auto_flag && cfg.gc_auto_threshold > 0)) &&
    -+	    gvfs_config_is_set(repo, GVFS_BLOCK_COMMANDS))
    -+		die(_("'git gc' is not supported on a GVFS repo"));
    ++	if (gvfs_config_is_set(repo, GVFS_BLOCK_COMMANDS)) {
    ++		int gc_auto_threshold = 6700;
    ++		if (!opts.auto_flag ||
    ++		    repo_config_get_int(repo, "gc.auto", &gc_auto_threshold) ||
    ++		    gc_auto_threshold > 0)
    ++			die(_("'git gc' is not supported on a GVFS repo"));
    ++	}
     +
      	if (opts.auto_flag) {
    - 		if (cfg.detach_auto && opts.detach < 0)
    - 			opts.detach = 1;
    + 		struct odb_optimize_options optimize_opts = {
    + 			.strategy = ODB_OPTIMIZE_INCREMENTAL,
     
      ## builtin/update-index.c ##
     @@
    @@ builtin/update-index.c: int cmd_update_index(int argc,
      			printf(_("%d\n"), the_repository->index->version);
      		} else if (preferred_index_format < INDEX_FORMAT_LB ||
     @@ builtin/update-index.c: int cmd_update_index(int argc,
    - 	odb_transaction_commit(transaction);
    + 	odb_transaction_commit_and_finalize_or_die(transaction);
      
      	if (split_index > 0) {
     +		if (gvfs_config_is_set(repo, GVFS_BLOCK_COMMANDS))
  • 42: bf67e47 = 68: c8b0d6e gvfs: allow overriding core.gvfs

  • 43: 92f98d5 = 69: 7a3a273 BRANCHES.md: Add explanation of branches and using forks

  • 44: 5808f51 ! 70: 8be037d Add virtual file system settings and hook proc

    @@ dir.c: struct path_pattern *last_matching_pattern(struct dir_struct *dir,
      		return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
      	return 0;
     @@ dir.c: static enum path_treatment treat_path(struct dir_struct *dir,
    - 						ignore_case);
    + 						repo_ignore_case(the_repository));
      	if (dtype != DT_DIR && has_path_in_index)
      		return path_none;
     +	if (is_excluded_from_virtualfilesystem(path->buf, path->len, dtype) > 0)
    @@ dir.c: static void add_path_to_appropriate_result_list(struct dir_struct *dir,
      		else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
     
      ## environment.c ##
    -@@ environment.c: enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
    +@@ environment.c: char *check_roundtrip_encoding;
    + #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
      #endif
    - enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
      int grafts_keep_true_parents;
     +char *core_virtualfilesystem;
      unsigned long pack_size_limit_cfg;
    @@ environment.c: int git_default_core_config(const char *var, const char *value,
      
     
      ## environment.h ##
    -@@ environment.h: extern char *apply_default_whitespace;
    - extern char *apply_default_ignorewhitespace;
    +@@ environment.h: extern int minimum_abbrev, default_abbrev;
    + extern int assume_unchanged;
      extern unsigned long pack_size_limit_cfg;
      
     +extern char *core_virtualfilesystem;
    - extern int protect_hfs;
    - extern int protect_ntfs;
    ++
    + extern int grafts_keep_true_parents;
      
    + const char *get_log_output_encoding(void);
     
      ## meson.build ##
     @@ meson.build: libgit_sources = [
    @@ virtualfilesystem.c (new)
     +	 * Build a hashmap of the virtual file system data we can use to look
     +	 * for cache entry matches quickly
     +	 */
    -+	vfshash = ignore_case ? memihash : memhash;
    -+	vfscmp = ignore_case ? strncasecmp : strncmp;
    ++	vfshash = repo_ignore_case(the_repository) ? memihash : memhash;
    ++	vfscmp = repo_ignore_case(the_repository) ? strncasecmp : strncmp;
     +	hashmap_init(map, vfs_hashmap_cmp, NULL, 0);
     +
     +	entry = buf = vfs_data->buf;
    @@ virtualfilesystem.c (new)
     +	 * Build a hashmap of the parent directories contained in the virtual
     +	 * file system data we can use to look for matches quickly
     +	 */
    -+	vfshash = ignore_case ? memihash : memhash;
    -+	vfscmp = ignore_case ? strncasecmp : strncmp;
    ++	vfshash = repo_ignore_case(the_repository) ? memihash : memhash;
    ++	vfscmp = repo_ignore_case(the_repository) ? strncasecmp : strncmp;
     +	hashmap_init(map, vfs_hashmap_cmp, NULL, 0);
     +
     +	entry = buf = vfs_data->buf;
    @@ virtualfilesystem.c (new)
     +
     +			/* look for a directory wild card (ie "dir1/") */
     +			if (buf[i - 1] == '/') {
    -+				if (ignore_case)
    ++				if (repo_ignore_case(the_repository))
     +					adjust_dirname_case(istate, entry);
     +				pos = index_name_pos(istate, entry, len);
     +				if (pos < 0) {
    @@ virtualfilesystem.c (new)
     +					}
     +				}
     +			} else {
    -+				if (ignore_case) {
    -+					struct cache_entry *ce = index_file_exists(istate, entry, len, ignore_case);
    ++				if (repo_ignore_case(the_repository)) {
    ++					struct cache_entry *ce = index_file_exists(istate, entry, len, repo_ignore_case(the_repository));
     +					if (ce)
     +						ce->ce_flags &= ~CE_SKIP_WORKTREE;
     +				} else {
  • 45: e097438 = 71: c65ea20 virtualfilesystem: don't run the virtual file system hook if the index has been redirected

  • 46: 6e86501 = 72: c952e85 virtualfilesystem: check if directory is included

  • 47: 5dd8286 = 73: de4b8af backwards-compatibility: support the post-indexchanged hook

  • 48: 86ab76d = 74: 855d3b8 gvfs: verify that the built-in FSMonitor is disabled

  • 50: 04d948c = 75: 867a83b wt-status: add trace2 data for sparse-checkout percentage

  • 52: 7d7f617 = 76: f6c4d2d status: add status serialization mechanism

  • 54: 0ce937b = 77: 215288c Teach ahead-behind and serialized status to play nicely together

  • 56: ba35418 = 78: 2656192 status: serialize to path

  • 58: 796495c = 79: de4f79f status: reject deserialize in V2 and conflicts

  • 49: ac119a2 = 80: 1ece460 git.c: add VFS enabled cmd blocking

  • 60: 2c83191 ! 81: b718d21 serialize-status: serialize global and repo-local exclude file metadata

    @@ wt-status-deserialize.c: static int my_validate_index(const char *path, const st
     +	 * Fake the setup here.
     +	 */
     +
    -+	if (excludes_file) {
    -+		return my_validate_excludes(excludes_file, "core_excludes", line);
    ++	if (repo_excludes_file(the_repository)) {
    ++		return my_validate_excludes(repo_excludes_file(the_repository),
    ++					    "core_excludes", line);
     +	} else {
     +		char *path = xdg_config_home("ignore");
     +		int r = my_validate_excludes(path, "core_excludes", line);
    @@ wt-status-serialize.c
     +	 *
     +	 * See dir.c:setup_standard_excludes()
     +	 */
    -+	append_exclude_info(fd, excludes_file, "core_excludes");
    ++	append_exclude_info(fd, repo_excludes_file(the_repository), "core_excludes");
     +}
     +
     +static void append_repo_excludes_file_info(int fd)
  • 51: 39b0448 = 82: e9e37fc git.c: permit repack cmd in Scalar repos

  • 61: e0d756e = 83: d79f587 status: deserialization wait

  • 53: 7fa2269 = 84: f210a33 git.c: permit fsck cmd in Scalar repos

  • 62: 66103c5 = 85: c368c8a status: deserialize with -uno does not print correct hint

  • 55: 9068cca = 86: 38522bf git.c: permit prune cmd in Scalar repos

  • 63: c2a37c9 = 87: 8aa951f fsmonitor: check CE_FSMONITOR_VALID in ce_uptodate

  • 57: 8d716e2 = 88: 2df0c7a worktree: remove special case GVFS cmd blocking

  • 64: 202ed70 = 89: b0a4157 fsmonitor: add script for debugging and update script for tests

  • 59: 4b6cf43 ! 90: 0884a87 builtin/repack.c: emit warning when shared cache is present

    @@ Commit message
     
      ## builtin/repack.c ##
     @@
    - #include "promisor-remote.h"
    - #include "repack.h"
    - #include "shallow.h"
    + #include "hex.h"
    + #include "wt-status.h"
    + #include "read-cache-ll.h"
     +#include "gvfs.h"
      
      #define ALL_INTO_ONE 1
  • 65: b3213ee = 91: d9b53b8 status: disable deserialize when verbose output requested.

  • 66: 0cc6e57 = 92: f0230a2 t7524: add test for verbose status deserialzation

  • 67: 3896778 = 93: 26c5c22 deserialize-status: silently fallback if we cannot read cache file

  • 68: c18a1ca ! 94: faf8f27 gvfs:trace2:data: add trace2 tracing around read_object_process

    @@ odb.c
     +#include "trace2.h"
      #include "write-or-die.h"
      
    - KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
    + /*
     @@ odb.c: int read_object_process(struct repository *r, const struct object_id *oid)
      
      	start = getnanotime();
  • 69: 2152b3c = 95: 1d7ca2e gvfs:trace2:data: status deserialization information

  • 70: d69c714 = 96: a1dbde8 gvfs:trace2:data: status serialization

  • 71: e93d1cd ! 97: 3798b4e gvfs:trace2:data: add vfs stats

    @@ virtualfilesystem.c: void apply_virtualfilesystem(struct index_state *istate)
      			/* look for a directory wild card (ie "dir1/") */
      			if (buf[i - 1] == '/') {
     +				nr_vfs_dirs++;
    - 				if (ignore_case)
    + 				if (repo_ignore_case(the_repository))
      					adjust_dirname_case(istate, entry);
      				pos = index_name_pos(istate, entry, len);
      				if (pos < 0) {
    @@ virtualfilesystem.c: void apply_virtualfilesystem(struct index_state *istate)
      					}
     @@ virtualfilesystem.c: void apply_virtualfilesystem(struct index_state *istate)
      			} else {
    - 				if (ignore_case) {
    - 					struct cache_entry *ce = index_file_exists(istate, entry, len, ignore_case);
    + 				if (repo_ignore_case(the_repository)) {
    + 					struct cache_entry *ce = index_file_exists(istate, entry, len, repo_ignore_case(the_repository));
     -					if (ce)
     +					if (ce) {
     +						if (ce->ce_flags & CE_SKIP_WORKTREE)
  • 72: ded7f62 = 98: c136443 trace2: refactor setting process starting time

  • 73: 1ac54a1 = 99: 0f08fce trace2:gvfs:experiment: report_tracking

  • 74: c7ef01e = 100: 5318d54 trace2:gvfs:experiment: read_cache: annotate thread usage in read-cache

  • 75: 9420488 = 101: 3f76cc8 trace2:gvfs:experiment: read-cache: time read/write of cache-tree extension

  • 76: 360d4d9 = 102: fb14655 trace2:gvfs:experiment: add region to apply_virtualfilesystem()

  • 77: 059cd69 = 103: 284ad93 trace2:gvfs:experiment: add region around unpack_trees()

  • 78: 272d18f = 104: d39ae4d trace2:gvfs:experiment: add region to cache_tree_fully_valid()

  • 79: 7cc631c = 105: f608743 trace2:gvfs:experiment: add unpack_entry() counter to unpack_trees() and report_tracking()

  • 80: 69190af = 106: 0e5aec8 trace2:gvfs:experiment: increase default event depth for unpack-tree data

  • 81: 134271c = 107: f0095ba trace2:gvfs:experiment: add data for check_updates() in unpack_trees()

  • 82: 283ad1a = 108: 54c0285 Trace2:gvfs:experiment: capture more 'tracking' details

  • 83: c64111d = 109: f60c3f1 credential: set trace2_child_class for credential manager children

  • 84: 4404bff = 110: 56cb975 sub-process: do not borrow cmd pointer from caller

  • 85: cddbc3f = 111: 0db5cde sub-process: add subprocess_start_argv()

  • 86: 2c6dd9e ! 112: da4adb3 sha1-file: add function to update existing loose object cache

    @@ Commit message
         Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
     
      ## object-file.h ##
    -@@ object-file.h: int odb_source_loose_write_stream(struct odb_source_loose *source,
    - 				  struct odb_write_stream *stream, size_t len,
    - 				  struct object_id *oid);
    +@@ object-file.h: int index_path(struct index_state *istate, struct object_id *oid, const char *pa
    + struct object_info;
    + struct odb_source;
      
     +/*
     + * Add a new object to the loose object cache (possibly after the
  • 87: 38c9857 = 113: dcbe0ec index-pack: avoid immediate object fetch while parsing packfile

  • 88: 4ff7b00 ! 114: fee501c gvfs-helper: create tool to fetch objects using the GVFS Protocol

    @@ environment.c
      #include "ws.h"
      #include "write-or-die.h"
      
    -@@ environment.c: int protect_hfs = PROTECT_HFS_DEFAULT;
    +@@ environment.c: int core_virtualize_objects;
    + #ifndef PROTECT_NTFS_DEFAULT
      #define PROTECT_NTFS_DEFAULT 1
      #endif
    - int protect_ntfs = PROTECT_NTFS_DEFAULT;
     +int core_use_gvfs_helper;
     +char *gvfs_cache_server_url;
     +const char *gvfs_shared_cache_pathname;
    @@ environment.c: int git_default_config(const char *var, const char *value,
      }
     
      ## environment.h ##
    -@@ environment.h: extern unsigned long pack_size_limit_cfg;
    +@@ environment.h: extern int assume_unchanged;
    + extern unsigned long pack_size_limit_cfg;
    + 
      extern char *core_virtualfilesystem;
    - extern int protect_hfs;
    - extern int protect_ntfs;
     +extern int core_use_gvfs_helper;
     +extern char *gvfs_cache_server_url;
     +extern const char *gvfs_shared_cache_pathname;
      
    - enum rebase_setup_type {
    - 	AUTOREBASE_NEVER = 0,
    + extern int grafts_keep_true_parents;
    + 
     
      ## gvfs-helper-client.c (new) ##
     @@
    @@ gvfs-helper-client.c (new)
     +
     +	if (ghc & GHC__CREATED__PACKFILE) {
     +		struct odb_source_files *files = odb_source_files_downcast(gh_client__chosen_odb);
    -+		packfile_store_reprepare(files->packed);
    ++		odb_source_prepare(&files->packed->base, ODB_PREPARE_FLUSH_CACHES);
     +	}
     +
     +	*p_ghc = ghc;
    @@ gvfs-helper-client.c (new)
     +	if (gh_client__chosen_odb)
     +		return;
     +
    -+	odb_prepare_alternates(the_repository->objects);
    ++	odb_prepare(the_repository->objects, ODB_PREPARE_FLUSH_CACHES);
     +	gh_client__chosen_odb = the_repository->objects->sources;
     +}
     +
    @@ gvfs-helper.c (new)
     +	if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname)
     +		odb_path = gvfs_shared_cache_pathname;
     +	else {
    -+		odb_prepare_alternates(the_repository->objects);
    ++		odb_prepare(the_repository->objects, ODB_PREPARE_FLUSH_CACHES);
     +		odb_path = repo_get_object_directory(the_repository);
     +	}
     +
    @@ gvfs-helper.c (new)
     + *
     + * TODO Consider using lockfile for this rather than naked tempfile.
     + */
    -+static struct tempfile *create_tempfile_for_packfile(void)
    ++static struct tempfile *create_tempfile_for_packfile(struct repository *repo)
     +{
     +	static unsigned int nth = 0;
     +	static struct timeval tv = {0};
    @@ gvfs-helper.c (new)
     +	strbuf_complete(&buf_path, '/');
     +	strbuf_addf(&buf_path, "pack/vfs-%s-%04d.temp", tbuf, nth++);
     +
    -+	tempfile = create_tempfile(buf_path.buf);
    ++	tempfile = repo_create_tempfile(repo, buf_path.buf);
     +	fdopen_tempfile(tempfile, "w");
     +
     +	strbuf_release(&buf_path);
    @@ gvfs-helper.c (new)
     +static void create_tempfile_for_loose(
     +	struct gh__request_params *params,
     +	struct gh__response_status *status,
    ++	struct repository *repo,
     +	const struct object_id *oid)
     +{
     +	struct strbuf buf_path = STRBUF_INIT;
    @@ gvfs-helper.c (new)
     +	 */
     +	strbuf_addf(&buf_path, ".%08u.temp", getpid());
     +
    -+	params->tempfile = create_tempfile(buf_path.buf);
    ++	params->tempfile = repo_create_tempfile(repo, buf_path.buf);
     +	if (!params->tempfile) {
     +		strbuf_addstr(&status->error_message,
     +			      "could not create tempfile for loose object");
    @@ gvfs-helper.c (new)
     +	params.headers = curl_slist_append(params.headers,
     +					   "Pragma: no-cache");
     +
    -+	create_tempfile_for_loose(&params, status, oid);
    ++	create_tempfile_for_loose(&params, status, the_repository, oid);
     +	if (!params.tempfile)
     +		goto cleanup;
     +
    @@ gvfs-helper.c (new)
     +	params.headers = curl_slist_append(params.headers,
     +					   "Accept: application/x-git-loose-object");
     +
    -+	params.tempfile = create_tempfile_for_packfile();
    ++	params.tempfile = create_tempfile_for_packfile(the_repository);
     +	if (!params.tempfile) {
     +		strbuf_addstr(&status->error_message,
     +			      "could not create tempfile for packfile");
    @@ meson.build: endforeach
     
      ## odb.c ##
     @@
    - #include "environment.h"
      #include "gettext.h"
      #include "gvfs.h"
    + #include "hashmap.h"
     +#include "gvfs-helper-client.h"
      #include "hex.h"
      #include "hook.h"
    - #include "khash.h"
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 	const struct object_id *real = oid;
    + #include "lockfile.h"
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
      	int already_retried = 0;
    + 	bool corrupt = false;
      	int tried_hook = 0;
     +	int tried_gvfs_helper = 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,
    - 	odb_prepare_alternates(odb);
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 		return 0;
      
      	while (1) {
     +		extern int core_use_gvfs_helper;
      		struct odb_source *source;
      
    - 		for (source = odb->sources; source; source = source->next)
    - 			if (!odb_source_read_object_info(source, real, oi, flags))
    - 				return 0;
    + 		for (source = odb->sources; source; source = source->next) {
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 				corrupt = true;
    + 		}
      
     +		if (core_use_gvfs_helper && !tried_gvfs_helper) {
     +			enum gh_client__created ghc;
    @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
      		/*
      		 * When the object hasn't been found we try a second read and
      		 * tell the sources so. This may cause them to invalidate
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 								 flags | OBJECT_INFO_SECOND_READ))
    - 					return 0;
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 					corrupt = true;
    + 			}
      			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
    @@ t/helper/.gitignore
     
      ## t/t1517-outside-repo.sh ##
     @@ t/t1517-outside-repo.sh: do
    + 	credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \
      	daemon | \
    - 	difftool--helper | filter-branch | format-rev | fsck-objects | \
    - 	get-tar-commit-id | \
    + 	difftool--helper | format-rev | fsck-objects |  get-tar-commit-id | \
     -	gui | gui--askpass | \
     +	gui | gui--askpass | gvfs-helper | \
      	http-backend | http-fetch | http-push | init-db | \
    - 	merge-octopus | merge-one-file | merge-resolve | mergetool | \
      	mktag | p4 | p4.py | pickaxe | remote-ftp | remote-ftps | \
    + 	remote-http | remote-https | replay | send-email | \
  • 89: 88dd0e5 ! 115: 8ea10a9 sha1-file: create shared-cache directory if it doesn't exist

    @@ Commit message
         Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
     
      ## environment.c ##
    -@@ environment.c: int protect_hfs = PROTECT_HFS_DEFAULT;
    - int protect_ntfs = PROTECT_NTFS_DEFAULT;
    +@@ environment.c: int core_virtualize_objects;
    + #endif
      int core_use_gvfs_helper;
      char *gvfs_cache_server_url;
     -const char *gvfs_shared_cache_pathname;
    @@ environment.c: static int git_default_gvfs_config(const char *var, const char *v
      
     
      ## environment.h ##
    -@@ environment.h: extern int protect_hfs;
    - extern int protect_ntfs;
    +@@ environment.h: extern unsigned long pack_size_limit_cfg;
    + extern char *core_virtualfilesystem;
      extern int core_use_gvfs_helper;
      extern char *gvfs_cache_server_url;
     -extern const char *gvfs_shared_cache_pathname;
     +extern struct strbuf gvfs_shared_cache_pathname;
      
    - enum rebase_setup_type {
    - 	AUTOREBASE_NEVER = 0,
    + extern int grafts_keep_true_parents;
    + 
     
      ## gvfs-helper-client.c ##
     @@
    @@ gvfs-helper-client.c: static int gh_client__get__receive_response(
      	if (gh_client__chosen_odb)
      		return;
      
    - 	odb_prepare_alternates(the_repository->objects);
    + 	odb_prepare(the_repository->objects, ODB_PREPARE_FLUSH_CACHES);
      	gh_client__chosen_odb = the_repository->objects->sources;
     +
     +	if (!gvfs_shared_cache_pathname.len)
    @@ gvfs-helper.c: static void approve_cache_server_creds(void)
      static void select_odb(void)
      {
     -	const char *odb_path = NULL;
    -+	odb_prepare_alternates(the_repository->objects);
    ++	odb_prepare(the_repository->objects, ODB_PREPARE_FLUSH_CACHES);
      
      	strbuf_init(&gh__global.buf_odb_path, 0);
      
     -	if (gvfs_shared_cache_pathname && *gvfs_shared_cache_pathname)
     -		odb_path = gvfs_shared_cache_pathname;
     -	else {
    --		odb_prepare_alternates(the_repository->objects);
    +-		odb_prepare(the_repository->objects, ODB_PREPARE_FLUSH_CACHES);
     -		odb_path = repo_get_object_directory(the_repository);
     -	}
     -
    @@ odb.c: int odb_mkstemp(struct object_database *odb,
      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;
    + 	struct hashmap_entry key;
      	bool usable = false;
      
      	strbuf_realpath(&normalized_objdir, o->sources->path, 1);
    @@ odb.c: int odb_mkstemp(struct object_database *odb,
      	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)
    + static void odb_prepare_alternates(struct object_database *odb,
    + 				   const char *alternate_db)
      {
     +	extern struct strbuf gvfs_shared_cache_pathname;
      	struct strvec sources = STRVEC_INIT;
      
    - 	if (odb->loaded_alternates)
    -@@ odb.c: void odb_prepare_alternates(struct object_database *odb)
    + 	parse_alternates(alternate_db, PATH_SEP, NULL, &sources);
    +@@ odb.c: static 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);
      
    @@ odb.c: void odb_prepare_alternates(struct object_database *odb)
     +
     +	}
     +
    - 	odb->loaded_alternates = 1;
    - 
      	strvec_clear(&sources);
    + }
    + 
  • 90: ab715cb = 116: 9c74819 gvfs-helper: better handling of network errors

  • 91: d58bf5e = 117: 1461af0 gvfs-helper-client: properly update loose cache with fetched OID

  • 92: e4e032d ! 118: a6c26dd gvfs-helper: V2 robust retry and throttling

    @@ gvfs-helper.c: static void select_odb(void)
       *
       * TODO Consider using lockfile for this rather than naked tempfile.
       */
    --static struct tempfile *create_tempfile_for_packfile(void)
    +-static struct tempfile *create_tempfile_for_packfile(struct repository *repo)
     +static void create_tempfile_for_packfile(
    -+	struct gh__request_params *params,
    -+	struct gh__response_status *status)
    ++			     struct gh__request_params *params,
    ++			     struct gh__response_status *status,
    ++			     struct repository *repo)
      {
      	static unsigned int nth = 0;
      	static struct timeval tv = {0};
    @@ gvfs-helper.c: static void select_odb(void)
     +		goto cleanup;
     +	}
      
    --	tempfile = create_tempfile(buf_path.buf);
    +-	tempfile = repo_create_tempfile(repo, buf_path.buf);
     -	fdopen_tempfile(tempfile, "w");
    -+	params->tempfile = create_tempfile(buf.buf);
    ++	params->tempfile = repo_create_tempfile(repo, buf.buf);
     +	if (!params->tempfile) {
     +		strbuf_addf(&status->error_message,
     +			    "could not create tempfile for packfile: '%s'",
    @@ gvfs-helper.c: static void select_odb(void)
      
     -	strbuf_release(&buf_path);
     +	fdopen_tempfile(params->tempfile, "w");
    -+
    + 
    +-	return tempfile;
     +	/*
     +	 * After the download is complete, we will need to steal the file
     +	 * from the tempfile() class (so that it doesn't magically delete
    @@ gvfs-helper.c: static void select_odb(void)
     +
     +	strbuf_setlen(&params->temp_path_idx, 0);
     +	strbuf_addf(&params->temp_path_idx, "%s.idx", buf.buf);
    - 
    --	return tempfile;
    ++
     +	/*
     +	 * Later, if all goes well, we will install them as:
     +	 *
    @@ gvfs-helper.c: static void select_odb(void)
      }
      
      /*
    -@@ gvfs-helper.c: static struct tempfile *create_tempfile_for_packfile(void)
    -  */
    +@@ gvfs-helper.c: static struct tempfile *create_tempfile_for_packfile(struct repository *repo)
      static void create_tempfile_for_loose(
      	struct gh__request_params *params,
    --	struct gh__response_status *status,
    + 	struct gh__response_status *status,
    +-	struct repository *repo,
     -	const struct object_id *oid)
    -+	struct gh__response_status *status)
    ++	struct repository *repo)
      {
     +	static int nth = 0;
      	struct strbuf buf_path = STRBUF_INIT;
    @@ gvfs-helper.c: static void create_tempfile_for_loose(
     -	strbuf_addf(&buf_path, ".%08u.temp", getpid());
     +	strbuf_addf(&buf_path, ".%08u.%.06u.temp", getpid(), nth++);
      
    - 	params->tempfile = create_tempfile(buf_path.buf);
    + 	params->tempfile = repo_create_tempfile(repo, buf_path.buf);
      	if (!params->tempfile) {
     @@ gvfs-helper.c: static void create_tempfile_for_loose(
      }
    @@ gvfs-helper.c: static void do_req(const char *url_base,
     +			delete_tempfile(&params->tempfile);
     +
     +		if (params->b_is_post)
    -+			create_tempfile_for_packfile(params, status);
    ++			create_tempfile_for_packfile(params, status, the_repository);
     +		else
    -+			create_tempfile_for_loose(params, status);
    ++			create_tempfile_for_loose(params, status, the_repository);
     +		if (!params->tempfile || status->ec != GH__ERROR_CODE__OK)
     +			return;
      	} else {
    @@ gvfs-helper.c: static void do__loose__gvfs_object(struct gh__response_status *st
      	params.headers = curl_slist_append(params.headers,
      					   "Pragma: no-cache");
      
    --	create_tempfile_for_loose(&params, status, oid);
    +-	create_tempfile_for_loose(&params, status, the_repository, oid);
     -	if (!params.tempfile)
     -		goto cleanup;
     +	oidcpy(&params.loose_oid, oid);
    @@ gvfs-helper.c: static void do__packfile__gvfs_objects(struct gh__response_status
      	params.headers = curl_slist_append(params.headers,
      					   "Accept: application/x-git-loose-object");
      
    --	params.tempfile = create_tempfile_for_packfile();
    +-	params.tempfile = create_tempfile_for_packfile(the_repository);
     -	if (!params.tempfile) {
     -		strbuf_addstr(&status->error_message,
     -			      "could not create tempfile for packfile");
  • 93: 83cbcb1 ! 119: c9fdbb5 gvfs-helper: expose gvfs/objects GET and POST semantics

    @@ gvfs-helper.c: static void do_req(const char *url_base,
      
     -		if (params->b_is_post)
     +		if (params->b_is_post && params->object_count > 1)
    - 			create_tempfile_for_packfile(params, status);
    + 			create_tempfile_for_packfile(params, status, the_repository);
      		else
    - 			create_tempfile_for_loose(params, status);
    + 			create_tempfile_for_loose(params, status, the_repository);
     +
      		if (!params->tempfile || status->ec != GH__ERROR_CODE__OK)
      			return;
    @@ gvfs-helper.c: static void do__packfile__gvfs_objects(struct gh__response_status
     +	gh__response_status__zero(status);
     +	if (!nr_oid_total)
     +		return;
    - 
    --			do__packfile__gvfs_objects(status, &iter,
    --						   gh__cmd_opts.block_size,
    --						   j_pack_num, j_pack_den,
    --						   &output_filename,
    --						   &nr_oid_taken);
    ++
     +	oidset_iter_init(oids, &iter);
     +
     +	j_pack_den = ((nr_oid_total + gh__cmd_opts.block_size - 1)
    @@ gvfs-helper.c: static void do__packfile__gvfs_objects(struct gh__response_status
     +	for (k = 0; k < nr_oid_total; k += nr_oid_taken) {
     +		j_pack_num++;
      
    +-			do__packfile__gvfs_objects(status, &iter,
    +-						   gh__cmd_opts.block_size,
    +-						   j_pack_num, j_pack_den,
    +-						   &output_filename,
    +-						   &nr_oid_taken);
     +		do__http_post__gvfs_objects(status, &iter,
     +					    gh__cmd_opts.block_size,
     +					    j_pack_num, j_pack_den,
     +					    result_list,
     +					    &nr_oid_taken);
    -+
    + 
     +		/*
     +		 * Because the oidset iterator has random
     +		 * order, it does no good to say the k-th or
  • 94: 37c8d8f = 120: 0c6955b gvfs-helper: dramatically reduce progress noise

  • 95: 97641e4 ! 121: 34fa525 gvfs-helper: handle pack-file after single POST request

    @@ gvfs-helper.c: static void do_req(const char *url_base,
      
     -		if (params->b_is_post && params->object_count > 1)
     +		if (params->b_is_post)
    - 			create_tempfile_for_packfile(params, status);
    + 			create_tempfile_for_packfile(params, status, the_repository);
     -		else
    --			create_tempfile_for_loose(params, status);
    +-			create_tempfile_for_loose(params, status, the_repository);
     +
    -+		create_tempfile_for_loose(params, status);
    ++		create_tempfile_for_loose(params, status, the_repository);
      
      		if (!params->tempfile || status->ec != GH__ERROR_CODE__OK)
      			return;
  • 96: be8762e = 122: 54b73d9 test-gvfs-prococol, t5799: tests for gvfs-helper

  • 97: 1cbab9e = 123: 1fc11e6 gvfs-helper: move result-list construction into install functions

  • 98: 40ab73b = 124: b827a9c t5799: add support for POST to return either a loose object or packfile

  • 99: 77afaf2 = 125: a944d4a t5799: cleanup wc-l and grep-c lines

  • 100: 4a1e85f = 126: d82eaa1 gvfs-helper: verify loose objects after write

  • 101: 59438b5 = 127: 6035191 t7599: create corrupt blob test

  • 102: fe76797 ! 128: 38528fa gvfs-helper: add prefetch support

    @@ gvfs-helper.c: static void select_odb(void)
     + * tempPacks directory.
       */
     -static void create_tempfile_for_packfile(
    --	struct gh__request_params *params,
    --	struct gh__response_status *status)
    +-			     struct gh__request_params *params,
    +-			     struct gh__response_status *status,
    +-			     struct repository *repo)
     +static void my_create_tempfile(
     +	struct gh__response_status *status,
    ++	struct repository *repo,
     +	int b_fdopen,
     +	const char *suffix1, struct tempfile **t1,
     +	const char *suffix2, struct tempfile **t2)
    @@ gvfs-helper.c: static void create_tempfile_for_packfile(
      		goto cleanup;
      	}
      
    --	params->tempfile = create_tempfile(buf.buf);
    +-	params->tempfile = repo_create_tempfile(repo, buf.buf);
     -	if (!params->tempfile) {
    -+	*t1 = create_tempfile(buf.buf);
    ++	*t1 = repo_create_tempfile(repo, buf.buf);
     +	if (!*t1) {
      		strbuf_addf(&status->error_message,
     -			    "could not create tempfile for packfile: '%s'",
    @@ gvfs-helper.c: static void create_tempfile_for_packfile(
     +		strbuf_setlen(&buf, len_tp);
     +		strbuf_addf(  &buf, "%s.%s", basename.buf, suffix2);
     +
    -+		*t2 = create_tempfile(buf.buf);
    ++		*t2 = repo_create_tempfile(repo, buf.buf);
     +		if (!*t2) {
     +			strbuf_addf(&status->error_message,
     +				    "could not create tempfile: '%s'",
    @@ gvfs-helper.c: static int create_loose_pathname_in_odb(struct strbuf *buf_path,
     - */
     -static void create_tempfile_for_loose(
     -	struct gh__request_params *params,
    --	struct gh__response_status *status)
    +-	struct gh__response_status *status,
    +-	struct repository *repo)
     +static void my_run_index_pack(struct gh__request_params *params UNUSED,
     +			      struct gh__response_status *status,
     +			      const struct strbuf *temp_path_pack,
    @@ gvfs-helper.c: static int create_loose_pathname_in_odb(struct strbuf *buf_path,
     +	child_process_clear(&ip);
     +}
      
    --	params->tempfile = create_tempfile(buf_path.buf);
    +-	params->tempfile = repo_create_tempfile(repo, buf_path.buf);
     -	if (!params->tempfile) {
     -		strbuf_addstr(&status->error_message,
     -			      "could not create tempfile for loose object");
    @@ gvfs-helper.c: static void create_tempfile_for_loose(
     +				 ph.idx_len == 0);
     +
     +	if (b_no_idx_in_multipack) {
    -+		my_create_tempfile(status, 0, "pack", &tempfile_pack, NULL, NULL);
    ++		my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, NULL, NULL);
     +		if (!tempfile_pack)
     +			goto done;
     +	} else {
     +		/* create a pair of tempfiles with the same basename */
    -+		my_create_tempfile(status, 0, "pack", &tempfile_pack, "idx", &tempfile_idx);
    ++		my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, "idx", &tempfile_idx);
     +		if (!tempfile_pack || !tempfile_idx)
     +			goto done;
     +	}
    @@ gvfs-helper.c: static void do_req(const char *url_base,
      			delete_tempfile(&params->tempfile);
      
     -		if (params->b_is_post)
    --			create_tempfile_for_packfile(params, status);
    +-			create_tempfile_for_packfile(params, status, the_repository);
     -
    --		create_tempfile_for_loose(params, status);
    +-		create_tempfile_for_loose(params, status, the_repository);
     -
    -+		my_create_tempfile(status, 1, NULL, &params->tempfile, NULL, NULL);
    ++		my_create_tempfile(status, the_repository, 1, NULL, &params->tempfile, NULL, NULL);
      		if (!params->tempfile || status->ec != GH__ERROR_CODE__OK)
      			return;
      	} else {
  • 103: 8e2b8a7 = 129: af62517 gvfs-helper: add prefetch .keep file for last packfile

  • 104: 75280df = 130: 696f272 gvfs-helper: do one read in my_copy_fd_len_tail()

  • 105: 2fb6021 = 131: 3301680 gvfs-helper: move content-type warning for prefetch packs

  • 106: d72feaa = 132: 01aede6 fetch: use gvfs-helper prefetch under config

  • 107: f162507 = 133: 3c7f3fc gvfs-helper: better support for concurrent packfile fetches

  • 108: e274b65 = 134: 058bc9c remote-curl: do not call fetch-pack when using gvfs-helper

  • 109: 1e27016 = 135: a299db6 fetch: reprepare packs before checking connectivity

  • 110: d8b6330 ! 136: 0345e3d gvfs-helper: retry when creating temp files

    @@ gvfs-helper.c: static void my_create_tempfile(
      	}
      
     +	retries = 0;
    - 	*t1 = create_tempfile(buf.buf);
    + 	*t1 = repo_create_tempfile(repo, buf.buf);
     +	while (!*t1 && retries < 5) {
     +		retries++;
     +		strbuf_setlen(&buf, len_tp);
     +		strbuf_addf(&buf, "%s-%d.%s", basename.buf, retries, suffix1);
    -+		*t1 = create_tempfile(buf.buf);
    ++		*t1 = repo_create_tempfile(repo, buf.buf);
     +	}
     +
      	if (!*t1) {
    @@ gvfs-helper.c: static void my_create_tempfile(
     @@ gvfs-helper.c: static void my_create_tempfile(
      		strbuf_addf(  &buf, "%s.%s", basename.buf, suffix2);
      
    - 		*t2 = create_tempfile(buf.buf);
    + 		*t2 = repo_create_tempfile(repo, buf.buf);
     +		while (!*t2 && retries < 5) {
     +			retries++;
     +			strbuf_setlen(&buf, len_tp);
     +			strbuf_addf(&buf, "%s-%d.%s", basename.buf, retries, suffix2);
    -+			*t2 = create_tempfile(buf.buf);
    ++			*t2 = repo_create_tempfile(repo, buf.buf);
     +		}
     +
      		if (!*t2) {
  • 111: a225fd0 = 137: f1d13cd sparse: avoid warnings about known cURL issues in gvfs-helper.c

  • 118: c246a5d ! 138: 8fb7122 maintenance: care about gvfs.sharedCache config

    @@ builtin/gc.c: static int write_loose_object_to_stdin(const struct object_id *oid
     +
     +	/* If set, use the shared object directory. */
     +	if (shared_object_dir) {
    -+		prev_source =
    -+			odb_set_temporary_primary_source(r->objects,
    -+							 shared_object_dir, 0);
    ++		odb_set_temporary_primary_source(r->objects, shared_object_dir,
    ++						 0, &prev_source);
     +		object_dir = shared_object_dir;
     +	}
      
  • 120: 5efced6 ! 139: c20909f unpack-trees:virtualfilesystem: Improve efficiency of clear_ce_flags

    @@ virtualfilesystem.c: int is_excluded_from_virtualfilesystem(const char *pathname
      			if (buf[i - 1] == '/') {
     -				nr_vfs_dirs++;
     +				stats->nr_vfs_dirs++;
    - 				if (ignore_case)
    + 				if (repo_ignore_case(the_repository))
      					adjust_dirname_case(istate, entry);
      				pos = index_name_pos(istate, entry, len);
      				if (pos < 0) {
    @@ virtualfilesystem.c: int is_excluded_from_virtualfilesystem(const char *pathname
      					}
      				}
      			} else {
    - 				if (ignore_case) {
    --					struct cache_entry *ce = index_file_exists(istate, entry, len, ignore_case);
    + 				if (repo_ignore_case(the_repository)) {
    +-					struct cache_entry *ce = index_file_exists(istate, entry, len, repo_ignore_case(the_repository));
     -					if (ce) {
     -						if (ce->ce_flags & CE_SKIP_WORKTREE)
     -							nr_explicit_skip++;
    @@ virtualfilesystem.c: int is_excluded_from_virtualfilesystem(const char *pathname
     -					else {
     -						nr_unknown++;
     -					}
    -+					ce = index_file_exists(istate, entry, len, ignore_case);
    ++					ce = index_file_exists(istate, entry, len, repo_ignore_case(the_repository));
      				} else {
      					int pos = index_name_pos(istate, entry, len);
     -					if (pos >= 0) {
    @@ virtualfilesystem.c: int is_excluded_from_virtualfilesystem(const char *pathname
     +						 * There may be aliases with different cases of the same
     +						 * name that also need to be modified.
     +						 */
    -+						if (ignore_case)
    -+							ce = index_file_next_match(istate, ce, ignore_case);
    ++						if (repo_ignore_case(the_repository))
    ++							ce = index_file_next_match(istate, ce, repo_ignore_case(the_repository));
     +						else
     +							break;
     +
  • 123: e3417fa ! 140: 9635063 Disable the monitor-components workflow in msft-git

    @@ .github/workflows/monitor-components.yml (deleted)
     -            feed: https://github.com/msys2/MINGW-packages/commits/master/mingw-w64-llvm.atom
     -          - label: innosetup
     -            feed: https://github.com/jrsoftware/issrc/tags.atom
    --          - label: mimalloc
    --            feed: https://github.com/microsoft/mimalloc/tags.atom
    --            title-pattern: ^(?!v1\.|v3\.[01]\.)
     -      fail-fast: false
     -    steps:
     -      - uses: git-for-windows/rss-to-issues@v0
  • 124: 7ae2bba = 141: b6781df .github: enable windows builds on microsoft fork

  • 125: e044ae6 = 142: 8c72d6d .github/actions/akv-secret: add action to get secrets

  • 112: 4639c63 = 143: 7aeebfb gvfs-helper: add --max-retries to prefetch verb

  • 126: db9cc31 = 144: e6a9a08 release: create initial Windows installer build workflow

  • 113: 2961f9e = 145: fa1f95e t5799: add tests to detect corrupt pack/idx files in prefetch

  • 127: 4e3e75d = 146: 7796ea4 help: special-case HOST_CPU universal

  • 114: 22c6486 ! 147: 2b46f7f gvfs-helper: ignore .idx files in prefetch multi-part responses

    @@ gvfs-helper.c: static void extract_packfile_from_multipack(
      				 ph.idx_len == 0);
      
     -	if (b_no_idx_in_multipack) {
    --		my_create_tempfile(status, 0, "pack", &tempfile_pack, NULL, NULL);
    +-		my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, NULL, NULL);
     -		if (!tempfile_pack)
     -			goto done;
     -	} else {
     -		/* create a pair of tempfiles with the same basename */
    --		my_create_tempfile(status, 0, "pack", &tempfile_pack, "idx", &tempfile_idx);
    +-		my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, "idx", &tempfile_idx);
     -		if (!tempfile_pack || !tempfile_idx)
     -			goto done;
     -	}
    @@ gvfs-helper.c: static void extract_packfile_from_multipack(
     +	 * if it is provided and always compute it locally so that we get the
     +	 * added verification that `git index-pack` provides.
     +	 */
    -+	my_create_tempfile(status, 0, "pack", &tempfile_pack, NULL, NULL);
    ++	my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, NULL, NULL);
     +	if (!tempfile_pack)
     +		goto done;
      
  • 128: d533a77 = 148: e971ed7 release: add Mac OSX installer build

  • 115: c81f6fd = 149: d1fa9db t5799: explicitly test gvfs-helper --fallback and --no-fallback

  • 129: bf1e49d = 150: 55b6684 release: build unsigned Ubuntu .deb package

  • 116: 4266eaa = 151: ac213e8 gvfs-helper: don't fallback with new config

  • 130: cbbdfc8 = 152: 4b79ebd release: add signing step for .deb package

  • 117: 3ef5217 = 153: 7d4b3e4 test-gvfs-protocol: add cache_http_503 to mayhem

  • 131: 22d7862 = 154: f63ae5f release: create draft GitHub release with packages & installers

  • 119: 5a9a265 = 155: 6e16f06 t5799: add unit tests for new gvfs.fallback config setting

  • 121: e0c5167 < -: ------------ homebrew: add GitHub workflow to release Cask

  • 122: 6560eff < -: ------------ Adding winget workflows

  • 141: 12a8aa2 = 156: 9b17135 scalar: set the config write-lock timeout to 150ms

  • -: ------------ > 157: 4125b90 scalar: set the config write-lock timeout to 150ms

  • -: ------------ > 158: d8d699f scalar: upgrade the config lock timeout setting automagically

  • 134: 691ddd0 ! 159: 147e2e3 update-microsoft-git: create barebones builtin

    @@ t/t1517-outside-repo.sh
     @@ t/t1517-outside-repo.sh: do
      	mktag | p4 | p4.py | pickaxe | remote-ftp | remote-ftps | \
      	remote-http | remote-https | replay | send-email | \
    - 	sh-i18n--envsubst | shell | show | stage | submodule | survey | svn | \
    + 	sh-i18n--envsubst | shell | show | stage | survey | \
     +	update-microsoft-git | \
    - 	upload-archive--writer | upload-pack | web--browse | whatchanged)
    - 		expect_outcome=expect_failure ;;
    - 	*)
    + 	upload-archive--writer | upload-pack | whatchanged)
    + 		h_expect_outcome=expect_failure
    + 		all_expect_outcome=expect_failure
  • -: ------------ > 160: 20cbcf3 config: (handle and) warn about deprecated lock timeout setting

  • 136: dd99d23 = 161: d24cb6c update-microsoft-git: Windows implementation

  • 142: efbd18c = 162: 14d54bc scalar: add docs from microsoft/scalar

  • 132: 7a03260 = 163: ee5e4a3 build-git-installers: publish gpg public key

  • 138: e58416e = 164: b94d93c update-microsoft-git: use brew on macOS

  • 133: 0904361 = 165: c058b48 release: continue pestering until user upgrades

  • 143: 6ec1e1b = 166: 836da02 .github: reinstate ISSUE_TEMPLATE.md for microsoft/git

  • 144: 48ef38e = 167: e4a1a0c scalar (Windows): use forward slashes as directory separators

  • 135: 810cf31 = 168: 7d1e542 dist: archive HEAD instead of HEAD^{tree}

  • 145: 53b4908 = 169: dc3c0b8 .github: update PULL_REQUEST_TEMPLATE.md

  • 146: 9411981 = 170: 77ea16f scalar: add retry logic to run_git()

  • 137: ef3279a = 171: 0396a95 release: include GIT_BUILT_FROM_COMMIT in MacOS build

  • 147: 790e547 ! 172: aad3450 Adjust README.md for microsoft/git

    @@ README.md
     +sudo apt-get install -y curl debsig-verify
     +
     +# Download public key signature file
    -+curl -s https://api.github.com/repos/microsoft/git/releases/latest \
    -+| grep -E 'browser_download_url.*msft-git-public.asc' \
    -+| cut -d : -f 2,3 \
    -+| tr -d \" \
    -+| xargs -I 'url' curl -L -o msft-git-public.asc 'url'
    ++curl -Os https://packages.microsoft.com/keys/microsoft-2025.asc
     +
     +# De-armor public key signature file
    -+gpg --output msft-git-public.gpg --dearmor msft-git-public.asc
    ++gpg --output microsoft-2025.gpg --dearmor microsoft-2025.asc
     +
    -+# Note that the fingerprint of this key is "B8F12E25441124E1", which you can
    ++# Note that the fingerprint of this key is "EE4D7792F748182B", which you can
     +# determine by running:
    -+gpg --show-keys msft-git-public.asc | head -n 2 | tail -n 1 | tail -c 17
    ++gpg --show-keys microsoft-2025.asc | head -n 2 | tail -n 1 | tail -c 17
     +
     +# Copy de-armored public key to debsig keyring folder
    -+sudo mkdir /usr/share/debsig/keyrings/B8F12E25441124E1
    -+sudo mv msft-git-public.gpg /usr/share/debsig/keyrings/B8F12E25441124E1/
    ++sudo mkdir /usr/share/debsig/keyrings/EE4D7792F748182B
    ++sudo mv microsoft-2025.gpg /usr/share/debsig/keyrings/EE4D7792F748182B/
     +
     +# Create an appropriate policy file
    -+sudo mkdir /etc/debsig/policies/B8F12E25441124E1
    ++sudo mkdir /etc/debsig/policies/EE4D7792F748182B
     +cat > generic.pol << EOL
     +<?xml version="1.0"?>
     +<!DOCTYPE Policy SYSTEM "https://www.debian.org/debsig/1.0/policy.dtd">
     +<Policy xmlns="https://www.debian.org/debsig/1.0/">
    -+  <Origin Name="Microsoft Git" id="B8F12E25441124E1" Description="Microsoft Git public key"/>
    ++  <Origin Name="Microsoft Git" id="EE4D7792F748182B" Description="Microsoft Git public key"/>
     +  <Selection>
    -+    <Required Type="origin" File="msft-git-public.gpg" id="B8F12E25441124E1"/>
    ++    <Required Type="origin" File="microsoft-2025.gpg" id="EE4D7792F748182B"/>
     +  </Selection>
     +  <Verification MinOptional="0">
    -+    <Required Type="origin" File="msft-git-public.gpg" id="B8F12E25441124E1"/>
    ++    <Required Type="origin" File="microsoft-2025.gpg" id="EE4D7792F748182B"/>
     +  </Verification>
     +</Policy>
     +EOL
     +
    -+sudo mv generic.pol /etc/debsig/policies/B8F12E25441124E1/generic.pol
    ++sudo mv generic.pol /etc/debsig/policies/EE4D7792F748182B/generic.pol
     +
    -+# Download Debian package
    ++# Download Debian package (substitute `amd64` with `arm64` on ARM machines)
     +curl -s https://api.github.com/repos/microsoft/git/releases/latest \
    -+| grep "browser_download_url.*deb" \
    ++| grep "browser_download_url.*amd64.deb" \
     +| cut -d : -f 2,3 \
     +| tr -d \" \
     +| xargs -I 'url' curl -L -o msft-git.deb 'url'
  • 148: 3a242e4 = 173: ee0fbf7 scalar: support the config command for backwards compatibility

  • 139: 9f61567 = 174: ed7926d release: add installer validation

  • 140: 43da41b < -: ------------ git_config_set_multivar_in_file_gently(): add a lock timeout

  • 149: 9676f63 = 175: 6850a2b scalar: implement a minimal JSON parser

  • 150: d680224 = 176: 2964490 scalar clone: support GVFS-enabled remote repositories

  • 151: ff853b4 = 177: 463c035 test-gvfs-protocol: also serve smart protocol

  • 152: 4d11773 = 178: d127c85 gvfs-helper: add the endpoint command

  • 153: ba2c7c3 ! 179: 7233259 dir_inside_of(): handle directory separators correctly

    @@ dir.c: static int cmp_icase(char a, char b)
      		return 0;
     +	if (is_dir_sep(a))
     +		return is_dir_sep(b) ? 0 : -1;
    - 	if (ignore_case)
    + 	if (repo_ignore_case(the_repository))
      		return toupper(a) - toupper(b);
      	return a - b;
  • 154: 2a13a39 = 180: b2e9e44 scalar: disable authentication in unattended mode

  • 155: 1b4d513 = 181: 18cfb0f abspath: make strip_last_path_component() global

  • 156: 55226d1 = 182: a50efcd scalar: do initialize gvfs.sharedCache

  • 157: 0cebd3d = 183: dd89765 scalar diagnose: include shared cache info

  • 158: 8bfa488 = 184: f93f7a0 scalar: only try GVFS protocol on https:// URLs

  • 159: 77fcdc7 ! 185: 2bc8102 scalar: verify that we can use a GVFS-enabled repository

    @@ Commit message
     
      ## t/t9210-scalar.sh ##
     @@ t/t9210-scalar.sh: test_expect_success UNZIP 'scalar diagnose' '
    - 	grep "^Total: [1-9]" out
    + 	test_grep "^Total: [1-9]" out
      '
      
     +GIT_TEST_ALLOW_GVFS_VIA_HTTP=1
  • 160: f34397a = 186: 900da33 scalar: add the cache-server command

  • 161: abe682c = 187: bb26b66 scalar: add a test toggle to skip accessing the vsts/info endpoint

  • 162: 214803a = 188: 9ab517b scalar: adjust documentation to the microsoft/git fork

  • 163: 0f5f72b ! 189: cba7a66 scalar: enable untracked cache unconditionally

    @@ Commit message
     
      ## scalar.c ##
     @@ scalar.c: static int set_recommended_config(int reconfigure)
    - 		{ "core.configWriteLockTimeoutMS", "150" },
    + 		{ "core.configLockTimeout", "150" },
      		{ "core.logAllRefUpdates", "true" },
      		{ "core.safeCRLF", "false" },
     +		{ "core.untrackedCache", "true" },
  • 164: 8cacb6b = 190: fb5402a scalar: parse clone --no-fetch-commits-and-trees for backwards compatibility

  • 165: 5436ac8 = 191: 7606c3c scalar: make GVFS Protocol a forced choice

  • 166: 182ec1c = 192: 95b727f scalar: work around GVFS Protocol HTTP/2 failures

  • 167: 4aa2b23 = 193: 14fd7fa gvfs-helper-client: clean up server process(es)

  • 168: 236cdb2 = 194: e0f4138 scalar diagnose: accommodate Scalar's Functional Tests

  • 169: 80777af = 195: f66ff9f ci: run Scalar's Functional Tests

  • 170: bda6599 ! 196: 42ad261 add/rm: allow adding sparse entries when virtual

    @@ read-cache.c: static void update_callback(struct diff_queue_struct *q,
      
     -		if (!data->include_sparse &&
     +		if (!data->include_sparse && !core_virtualfilesystem &&
    - 			!path_in_sparse_checkout(path, data->index))
    + 		    !path_in_sparse_checkout(path, data->index))
      			continue;
      
  • 171: 6ffbe4c ! 197: 000acf6 scalar: upgrade to newest FSMonitor config setting

    @@ Commit message
     
      ## scalar.c ##
     @@ scalar.c: static int set_recommended_config(int reconfigure)
    - 	int i;
    - 	char *value;
    + 		free(dummy);
    + 	}
      
     +	/*
     +	 * If a user has "core.usebuiltinfsmonitor" enabled, try to switch to
  • 172: 723152f = 198: 70bc42a sparse-checkout: add config to disable deleting dirs

  • 173: 1911816 = 199: 8e70595 diff: ignore sparse paths in diffstat

  • 174: 685b656 = 200: a1e616c repo-settings: enable sparse index by default

  • 175: 5d50249 = 201: 7336823 TO-UPSTREAM: sequencer: avoid progress when stderr is redirected

  • 176: 8fd9544 = 202: dc9772d TO-CHECK: t1092: use quiet mode for rebase tests

  • 177: c9d1123 = 203: cc48611 reset: fix mixed reset when using virtual filesystem

  • 178: ad67805 = 204: d826a42 diff(sparse-index): verify with partially-sparse

  • 179: b25ab9a = 205: d69540d stash: expand testing for git stash -u

  • 180: 3c7d3e8 = 206: 8cbf278 sparse-index: add ensure_full_index_with_reason()

  • 181: 67ab008 = 207: f1d1ff8 treewide: add reasons for expanding index

  • 182: f0dcb27 = 208: dbff646 treewide: custom reasons for expanding index

  • 183: c94d991 ! 209: ac73315 sparse-index: add macro for unaudited expansions

    @@ builtin/merge-index.c: int cmd_merge_index(int argc,
      	i = 1;
      	if (!strcmp(argv[i], "-o")) {
     
    - ## builtin/stash.c ##
    -@@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
    - 		char *ps_matched = xcalloc(ps->nr, 1);
    - 
    - 		/* TODO: audit for interaction with sparse-index. */
    --		ensure_full_index(the_repository->index);
    -+		ensure_full_index_unaudited(the_repository->index);
    - 		for (size_t i = 0; i < the_repository->index->cache_nr; i++)
    - 			ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
    - 				      ps_matched);
    -
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static void die_on_index_match(const char *path, int force)
      		char *ps_matched = xcalloc(ps.nr, 1);
  • 184: 3e02eaf = 210: cfa950a Docs: update sparse index plan with logging

  • 185: f4654eb = 211: 86ab498 sparse-index: log failure to clear skip-worktree

  • 186: 52f1b9d = 212: dceb582 stash: use -f in checkout-index child process

  • 187: ba03c72 = 213: c67d25b sparse-index: do not copy hashtables during expansion

  • 188: a0366b9 = 214: 7012abf TO-UPSTREAM: sub-process: avoid leaking cmd

  • 189: 14117b7 = 215: e8b7250 remote-curl: release filter options before re-setting them

  • 190: c80c493 = 216: 73a1480 transport: release object filter options

  • 191: 416a515 ! 217: 2280091 push: don't reuse deltas with path walk

    @@ builtin/push.c: int cmd_push(int argc,
      		repo = argv[0];
      
     
    + ## odb.h ##
    +@@ odb.h: struct odb_generate_pack_options {
    + 
    + 	/* Do not use bitmap indices when computing reachability. */
    + 	unsigned disable_bitmaps:1;
    ++
    ++	/* Do not reuse deltas. */
    ++	unsigned no_reuse_delta:1;
    + };
    + 
    + #define ODB_GENERATE_PACK_OPTIONS_INIT { \
    +
    + ## odb/source-files.c ##
    +@@ odb/source-files.c: static int odb_source_files_generate_pack(struct odb_source *source UNUSED,
    + 		strvec_push(&cp->args, "--missing=allow-promisor");
    + 	if (opts->disable_bitmaps)
    + 		strvec_push(&cp->args, "--no-use-bitmap-index");
    ++	if (opts->no_reuse_delta)
    ++		strvec_push(&cp->args, "--no-reuse-delta");
    + 	switch (opts->progress) {
    + 	case ODB_GENERATE_PACK_PROGRESS_NONE:
    + 		strvec_push(&cp->args, "--quiet");
    +
      ## send-pack.c ##
     @@ send-pack.c: static int pack_objects(struct repository *r,
    - 		strvec_push(&po.args, "--shallow");
    - 	if (args->disable_bitmaps)
    - 		strvec_push(&po.args, "--no-use-bitmap-index");
    -+	if (args->no_reuse_delta)
    -+		strvec_push(&po.args, "--no-reuse-delta");
    - 	po.in = -1;
    - 	po.out = args->stateless_rpc ? -1 : fd;
    - 	po.git_cmd = 1;
    + 		opts.progress = ODB_GENERATE_PACK_PROGRESS_VERBOSE;
    + 	opts.shallow = is_repository_shallow(r);
    + 	opts.disable_bitmaps = args->disable_bitmaps;
    ++	opts.no_reuse_delta = args->no_reuse_delta;
    + 
    + 	/*
    + 	 * The pack is either written directly to the remote's descriptor, or,
     
      ## send-pack.h ##
     @@ send-pack.h: struct send_pack_args {
  • 192: 3b37512 = 218: d91895f t7900-maintenance.sh: reset config between tests

  • 193: 7a5af41 ! 219: c524933 maintenance: add cache-local-objects maintenance task

    @@ builtin/gc.c: enum maintenance_task_label {
      
      	/* Leave as final value */
      	TASK__COUNT
    -@@ builtin/gc.c: static int geometric_repack_auto_condition(struct gc_config *cfg UNUSED)
    - 	return ret;
    +@@ builtin/gc.c: static int geometric_repack_auto_condition(struct gc_config *cfg)
    + 	return odb_optimize_required(the_repository->objects, &opts);
      }
      
     +static void link_or_copy_or_die(const char *src, const char *dst)
    @@ builtin/gc.c: static int geometric_repack_auto_condition(struct gc_config *cfg U
     +	if (errno != EXDEV)
     +		warning_errno(_("failed to link '%s' to '%s'"), src, dst);
     +
    -+	if (copy_file(dst, src, 0444))
    ++	if (copy_file(the_repository, dst, src, 0444))
     +		die_errno(_("failed to copy '%s' to '%s'"), src, dst);
     +}
     +
    @@ builtin/gc.c: static int geometric_repack_auto_condition(struct gc_config *cfg U
     +	if (errno != EXDEV)
     +		warning_errno(_("failed to move '%s' to '%s'"), src, dst);
     +
    -+	if (copy_file(dst, src, 0444))
    ++	if (copy_file(the_repository, dst, src, 0444))
     +		die_errno(_("failed to copy '%s' to '%s'"), src, dst);
     +
     +	if (unlink(src))
    @@ t/t7900-maintenance.sh: test_systemd_analyze_verify () {
     +}
     +
      test_expect_success 'help text' '
    - 	test_expect_code 129 git maintenance -h >actual &&
    + 	git maintenance -h >actual &&
      	test_grep "usage: git maintenance <subcommand>" actual &&
     @@ t/t7900-maintenance.sh: test_expect_success 'maintenance aborts with existing lock file' '
      	test_grep "Another scheduled git-maintenance(1) process seems to be running" err
  • 194: 13e557f = 220: 1bd9f7b scalar.c: add cache-local-objects task

  • 195: 07b4d06 = 221: 7de23a8 hooks: add custom post-command hook config

  • 196: 2be625e ! 222: ad973bf TO-UPSTREAM: Docs: fix asciidoc failures from short delimiters

    @@ Documentation/config/sendemail.adoc: the documentation of the email program of t
      +
     ---
     +----
    - sendmail;;
    + `sendmail`;;
      *	Quoted aliases and quoted addresses are not supported: lines that
      	contain a `"` symbol are ignored.
    -@@ Documentation/config/sendemail.adoc: sendmail;;
    +@@ Documentation/config/sendemail.adoc: described below:
      *	Warnings are printed on the standard error output for any
      	explicitly unsupported constructs, and any other lines that are not
      	recognized by the parser.
     ---
     +----
    - sendemail.annotate::
    - sendemail.bcc::
    - sendemail.cc::
    + `sendemail.annotate`::
    + `sendemail.bcc`::
    + `sendemail.cc`::
     
      ## Documentation/config/ssh.adoc ##
     @@ Documentation/config/ssh.adoc: overridden via the environment variable `GIT_SSH_VARIANT`.
    @@ Documentation/config/survey.adoc: survey.*::
      +
     ---
     +----
    - 	survey.namerev::
    - 		Boolean to show/hide `git name-rev` information for each
    - 		reported commit and the containing commit of each
    + 	verbose::
    + 		This boolean value implies the `--[no-]verbose` option.
    + 	progress::
     @@ Documentation/config/survey.adoc: survey.*::
    - 		long file or subdirectory entry names.  Provides a
    - 		default value for `--tree-sizes=<n>` in
    - 		linkgit:git-survey[1].
    + 	top::
    + 		This integer value implies `--top=<N>`, specifying the
    + 		number of entries in the detail tables.
     ---
     +----
     
  • 197: 233cfcb ! 223: a43ad11 hooks: make hook logic memory-leak free

    @@ Commit message
     
      ## git.c ##
     @@ git.c: static int run_post_command_hook(struct repository *r)
    + 	ret = run_hooks_opt(r, "post-command", &opt);
      
    - 	run_post_hook = 0;
      	strvec_clear(&sargv);
     +	strvec_clear(&opt.args);
      	setenv("COMMAND_HOOK_LOCK", "false", 1);
  • 198: 04afdcf = 224: 5f1ad26 t0401: test post-command for alias, version, typo

  • 200: 63dd7fa = 225: 3398e22 hooks: better handle config without gitdir

  • 226: e9a6793 = 226: 6a50415 scalar: add run_git_argv

  • 227: 0613e00 = 227: b4c4303 scalar: add --ref-format option to scalar clone

  • 228: 6a0d2da = 228: 649def5 gvfs-helper: skip collision check for loose objects

  • 229: 3dc9149 = 229: 598439a gvfs-helper: emit advice on transient errors

  • 230: b4fd903 = 230: 46035f6 gvfs-helper: avoid collision check for packfiles

  • 231: 75845f4 = 231: 32c58ca t5799: update cache-server methods for multiple instances

  • 232: fd6407c = 232: af377eb gvfs-helper: override cache server for prefetch

  • 233: b4668fc = 233: 7699e5f gvfs-helper: override cache server for get

  • 234: 97eae2e = 234: 8426311 gvfs-helper: override cache server for post

  • 235: 92a9687 = 235: 114380f t5799: add test for all verb-specific cache-servers together

  • 236: 548af29 = 236: ee608e4 lib-gvfs-helper: create helper script for protocol tests

  • 237: 27de0de = 237: 2e4c327 t579*: split t5799 into several parts

  • 238: 0bff5ab = 238: 403c82c scalar: add ---cache-server-url options

  • 239: 06616a4 ! 239: 10225f2 Restore previous errno after post command hook

    @@ Metadata
      ## Commit message ##
         Restore previous errno after post command hook
     
    +    Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
    +
      ## git.c ##
     @@ git.c: static int run_post_command_hook(struct repository *r)
      {
    @@ git.c: static int run_post_command_hook(struct repository *r)
      
      	/*
     @@ git.c: static int run_post_command_hook(struct repository *r)
    + 	strvec_pushf(&opt.args, "--exit_code=%u", exit_code);
      	ret = run_hooks_opt(r, "post-command", &opt);
      
    - 	run_post_hook = 0;
    -+
     +	errno = saved_errno;
      	strvec_clear(&sargv);
      	strvec_clear(&opt.args);
  • 240: 1d9095a = 240: 2ba71ba t9210: differentiate origin and cache servers

  • 241: 6f693c2 = 241: 64e750d unpack-trees: skip lstats for deleted VFS entries in checkout

  • 242: 8f83a69 = 242: 681489b worktree: conditionally allow worktree on VFS-enabled repos

  • 243: 5b6b46c = 243: 5afbf3c gvfs-helper: send X-Session-Id headers

  • 244: 17848a9 = 244: 1879e07 gvfs-helper: create shared object cache if missing

  • 245: 4c41212 = 245: c743eb9 gvfs: add gvfs.sessionKey config

  • 246: bed370f ! 246: 1be7f3f gvfs: clear DIE_IF_CORRUPT in streaming incore fallback

    @@ odb/streaming.c
      #include "odb.h"
      #include "odb/source.h"
      #include "odb/streaming.h"
    -@@ odb/streaming.c: static int open_istream_incore(struct odb_read_stream **out,
    +@@ odb/streaming.c: static int open_istream_incore(struct odb_stream **out,
      		.base.read = read_istream_incore,
      	};
      	struct odb_incore_read_stream *st;
  • 247: 241f1c4 < -: ------------ workflow: add release-vfsforgit to automate VFS for Git updates

  • 248: 21ae6a1 = 247: d6fd0da worktree remove: use GVFS_SUPPORTS_WORKTREES for skip-clean-check gate

  • 249: a4a48d4 = 248: 222cf6e ci: add new VFS for Git functional tests workflow

  • 250: a771e02 = 249: a90d0de azure-pipelines: add stub release pipeline for Azure

  • 251: e551e62 = 250: d0cc02e diff: add renameThreshold configuration option

  • 252: 4cade5c ! 251: 8604b8a gvfs-helper: separate packfile extraction from indexing

    @@ gvfs-helper.c: static void extract_packfile_from_multipack(
      	b_no_idx_in_multipack = (ph.idx_len == maximum_unsigned_value_of_type(uint64_t) ||
     @@ gvfs-helper.c: static void extract_packfile_from_multipack(
      	 */
    - 	my_create_tempfile(status, 0, "pack", &tempfile_pack, NULL, NULL);
    + 	my_create_tempfile(status, the_repository, 0, "pack", &tempfile_pack, NULL, NULL);
      	if (!tempfile_pack)
     -		goto done;
     +		return;
  • 253: 32936ce = 252: 628f9e4 blame: add blame.renames, blame.renameThreshold, blame.renameLimit

  • 254: 5b7027b ! 253: e84e216 gvfs-helper: run prefetch index-pack in parallel

    @@ gvfs-helper.c: static void index_and_finalize_packfile(struct gh__request_params
     +	strvec_pushl(&cp->args, "-o", entry->temp_path_idx.buf, NULL);
     +	strvec_push(&cp->args, entry->temp_path_pack.buf);
     +	cp->no_stdin = 1;
    ++	cp->no_stdout = 1;
     +
     +	return 1;
     +}
  • 255: 355cd4a ! 254: 272427b gvfs-helper: add gvfs.prefetchThreads config for parallel prefetch

    @@ t/t5797-gvfs-helper-prefetch-threads.sh (new)
     +		--no-progress \
     +		prefetch >OUT.output 2>OUT.stderr &&
     +
    ++	test_must_be_empty OUT.stderr &&
     +	verify_received_packfile_count 3 &&
     +	verify_prefetch_keeps 1200000000
     +}
    @@ t/t5797-gvfs-helper-prefetch-threads.sh (new)
     +		--no-progress \
     +		prefetch --since="1000000000" >OUT.output 2>OUT.stderr &&
     +
    ++	test_must_be_empty OUT.stderr &&
     +	verify_received_packfile_count 2 &&
     +	verify_prefetch_keeps 1200000000
     +}
    @@ t/t5797-gvfs-helper-prefetch-threads.sh (new)
     +		--no-progress \
     +		prefetch --since="1000000000" >OUT.output 2>OUT.stderr &&
     +
    ++	test_must_be_empty OUT.stderr &&
     +	verify_received_packfile_count 2 &&
     +	verify_prefetch_keeps 1200000000 &&
     +
    @@ t/t5797-gvfs-helper-prefetch-threads.sh (new)
     +		--no-progress \
     +		prefetch >OUT.output 2>OUT.stderr &&
     +
    ++	test_must_be_empty OUT.stderr &&
     +	verify_received_packfile_count 0 &&
     +	verify_prefetch_keeps 1200000000
     +}
  • 256: 2e8febb ! 255: 76750cb azure-pipelines: add ESRP code signing

    @@ .azure-pipelines/release.yml: extends:
     +                      version: '8.x'
     +                  - template: .azure-pipelines/esrp/sign.yml@self
     +                    parameters:
    -+                      displayName: 'Example sign Debian package'
    ++                      displayName: 'Sign Debian package'
     +                      folderPath: '$(Build.ArtifactStagingDirectory)/app'
     +                      pattern: '**/*.deb'
     +                      inlineOperation: |
     +                        [
     +                          {
    -+                            "KeyCode": "CP-453387-Pgp",
    ++                            "KeyCode": "CP-500207-Pgp",
     +                            "OperationCode": "LinuxSign",
     +                            "ToolName": "sign",
     +                            "ToolVersion": "1.0",
  • 257: 41500d3 = 256: e9a4eee azure-pipelines: allow overriding Git version

  • 258: 08a9f59 = 257: 6566b84 azure-pipelines: build, sign and stage the Linux Debian package

  • 259: 02e2d8e ! 258: 1b3c09d azure-pipelines: build, sign, notarize and stage the macOS installer

    @@ .azure-pipelines/release.yml: extends:
     +                        "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
     +                      arch -x86_64 /usr/local/bin/brew install gettext
     +
    ++                      # Ensure native Homebrew (arm64) is up-to-date beforehand.
    ++                      HOMEBREW_NO_AUTO_UPDATE='' brew update
    ++
     +                      # Native (arm64) build dependencies.
     +                      brew install automake asciidoc xmlto docbook
     +                      brew link --force gettext
    @@ .azure-pipelines/release.yml: extends:
     +                      # library. The C code is compiled with
     +                      # `-arch arm64 -arch x86_64` via config.mak, but
     +                      # `cargo build` only targets the host architecture
    -+                      # by default, so we must build both targets
    -+                      # explicitly and lipo them together before `make`
    -+                      # tries to link the final binary.
    ++                      # by default. RUST_TARGETS lets the Makefile build
    ++                      # both slices and combine them with lipo before
    ++                      # linking, including in the installer's sub-makes.
     +                      rustup target add x86_64-apple-darwin
    -+                      BUILD_UNIVERSAL_LIBGITCORE=YesPlease
    -+                      export BUILD_UNIVERSAL_LIBGITCORE
    ++                      RUST_TARGETS="aarch64-apple-darwin x86_64-apple-darwin"
    ++                      export RUST_TARGETS
     +
     +                      make -j"$(sysctl -n hw.physicalcpu)" GIT-VERSION-FILE dist dist-doc
     +
    @@ .github/macos-installer/Makefile: GIT_PREFIX := $(PREFIX)/git
      
      BUILD_DIR := $(GITHUB_WORKSPACE)/payload
      DESTDIR := $(PWD)/stage/git-$(ARCH_UNIV)-$(VERSION)
    -
    - ## Makefile ##
    -@@ Makefile: $(LIB_FILE): $(LIB_OBJS)
    - 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
    - 
    - $(RUST_LIB): Cargo.toml $(RUST_SOURCES) $(LIB_FILE)
    -+ifeq (,$(BUILD_UNIVERSAL_LIBGITCORE))
    - 	$(QUIET_CARGO)cargo build $(CARGO_ARGS)
    -+else
    -+	$(QUIET_CARGO)cargo build $(CARGO_ARGS) --target aarch64-apple-darwin && \
    -+		cargo build $(CARGO_ARGS) --target x86_64-apple-darwin && \
    -+		mkdir -p target/release && \
    -+		lipo -create \
    -+			target/aarch64-apple-darwin/release/libgitcore.a \
    -+			target/x86_64-apple-darwin/release/libgitcore.a \
    -+			-output $(RUST_LIB)
    -+endif
    - 
    - .PHONY: rust
    - rust: $(RUST_LIB)
  • 260: c4ded8c = 259: b641226 azure-pipelines: build, sign and stage the Windows installer

  • 261: 8a204a2 = 260: 7e87917 azure-pipelines: enable on tag push, default ESRP and GitHub release on

  • 262: 61ff841 = 261: f78bbe2 release: binskim for Windows

  • 263: 3f15f8c = 262: 77fe339 release: suppress unfixable binskim findings

  • 264: 41d240c = 263: 543ce26 binskim: add baseline

  • 265: b7252dd = 264: 2b27981 checkout: preserve skip-worktree for virtual filesystem paths

  • 266: e96535a ! 265: d15d223 rust: pick a GCC-compatible Cargo target under MSYS2/MinGW

    @@ Commit message
         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## Makefile ##
    -@@ Makefile: TEST_SHELL_PATH = $(SHELL_PATH)
    - LIB_FILE = libgit.a
    - 
    - ifdef DEBUG
    --RUST_TARGET_DIR = target/debug
    -+RUST_TARGET_DIR = target$(if $(CARGO_TARGET),/$(CARGO_TARGET))/debug
    +@@ Makefile: RUST_LIB_NAME = gitcore.lib
      else
    --RUST_TARGET_DIR = target/release
    -+RUST_TARGET_DIR = target$(if $(CARGO_TARGET),/$(CARGO_TARGET))/release
    + RUST_LIB_NAME = libgitcore.a
    + endif
    +-RUST_LIB = target/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME)
    ++RUST_LIB = target$(if $(CARGO_TARGET),/$(CARGO_TARGET))/$(RUST_BUILD_CONFIG)/$(RUST_LIB_NAME)
      endif
      
    - ifeq ($(uname_S),Windows)
    + GITLIBS = common-main.o $(LIB_FILE)
     @@ Makefile: endif
      ifndef DEBUG
      CARGO_ARGS += --release
  • 267: de444c4 = 266: f40f886 ci(vfs): install the GCC-compatible Rust target before building

  • 268: 23047e6 < -: ------------ fixup! azure-pipelines: add ESRP code signing

  • 269: b2ce41e < -: ------------ fixup! Adjust README.md for microsoft/git

  • 270: 1f95d4c < -: ------------ fixup! gvfs-helper: run prefetch index-pack in parallel

  • 271: f8dae33 ! 267: 0057d2b release-homebrew: add a hand-run script to replace the workflow

    @@ .github/release-homebrew.sh (new)
     +	exit 1
     +}
     +
    -+case "$1" in
    ++case "${1-}" in
     +--force) force=t; shift;;
     +*) force=;;
     +esac
    @@ .github/release-homebrew.sh (new)
     +esac
     +
     +# Enforce 64 lowercase hex chars without spawning grep.
    -+	*[!0-9a-f]*|"")
    ++case "$sha256" in
    ++*[!0-9a-f]*|"")
    ++       die "asset digest is not lowercase hex: $sha256" ;;
     +esac
     +test ${#sha256} -eq 64 ||
     +	die "asset digest is not 64 chars long: $sha256"
    @@ .github/release-homebrew.sh (new)
     +pr_url=$(gh pr create \
     +	--repo "$REPO" \
     +	--head "$BRANCH" \
    -+	--title "$TITLE")
    ++	--title "$TITLE" \
    ++	--body "See https://github.com/microsoft/git/releases/tag/$TAG_NAME")
     +
     +echo "==> Created:   $pr_url"
  • 272: 7d11706 ! 268: 4b1a1d9 release-vfsforgit: add hand-run script to supersede the workflow

    @@ .github/release-vfsforgit.sh (new)
     +	exit 1
     +}
     +
    -+case "$1" in
    ++case "${1-}" in
     +--force) force=t; shift;;
     +*) force=;;
     +esac
  • 273: b3438cd ! 269: 9d733d5 .github: add release-winget.sh to open winget-pkgs PR

    @@ .github/release-winget.sh (new)
     +	;;
     +esac
     +
    -+case "$1" in
    ++case "${1-}" in
     +--force) force=t; shift;;
     +*) force=;;
     +esac
    @@ .github/release-winget.sh (new)
     +echo "==> Version:   $version"
     +
     +workdir=$(mktemp -d)
    ++origdir="$(pwd)"
     +success=0
     +cleanup () {
    ++	cd "$origdir"
     +	if [ "$success" = 1 ]; then
     +		rm -rf "$workdir"
     +	else
  • 274: 0d9bced < -: ------------ fixup! homebrew: add GitHub workflow to release Cask

  • 275: ade4eb6 < -: ------------ fixup! workflow: add release-vfsforgit to automate VFS for Git updates

  • 276: 877e61c < -: ------------ fixup! Adding winget workflows

  • 277: a762728 < -: ------------ fixup! gvfs: add global command pre and post hook procs

  • 278: c694a5b = 270: 466e832 trace2: tolerate failed timestamp formatting

  • 279: 3ceda5e < -: ------------ wincred: avoid memory corruption when erasing a credential

  • 280: 2ec24be < -: ------------ wincred: prevent silent credential loss when storing OAuth tokens

  • 281: 067a5e0 < -: ------------ fixup! release-homebrew: add a hand-run script to replace the workflow

  • 282: 4fa91e7 < -: ------------ fixup! release-vfsforgit: add hand-run script to supersede the workflow

  • 283: 9870ae8 < -: ------------ fixup! .github: add release-winget.sh to open winget-pkgs PR

  • 284: d9e606a < -: ------------ fixup! release-homebrew: add a hand-run script to replace the workflow

  • 285: 923a097 < -: ------------ fixup! release-homebrew: add a hand-run script to replace the workflow

  • 286: d283b1a < -: ------------ fixup! .github: add release-winget.sh to open winget-pkgs PR

  • 287: 72134ee = 271: 7a4be8b reset --mixed: clear skip-worktree for all changed entries in VFS mode

  • 288: 8a3eba9 ! 272: e7bccf1 send-pack: add gvfs.negativeRefCheck to skip missing negatives

    @@ Documentation/config/gvfs.adoc: gvfs.fallback::
     
      ## send-pack.c ##
     @@ send-pack.c: int option_parse_push_signed(const struct option *opt,
    - }
      
    - static void feed_object(struct repository *r,
    --			const struct object_id *oid, FILE *fh, int negative)
    -+			const struct object_id *oid, FILE *fh, int negative,
    -+			int check_missing)
    + static void append_negative_object(struct repository *r,
    + 				   struct oid_array *haves,
    +-				   const struct object_id *oid)
    ++				   const struct object_id *oid,
    ++				   int check_missing)
      {
    --	if (negative && !gvfs_config_is_set(r, GVFS_MISSING_OK) && !odb_has_object(r->objects, oid, 0))
    -+	if (negative && check_missing &&
    -+	    !odb_has_object(r->objects, oid, 0))
    + 	/*
    + 	 * The remote end may have advertised objects that we do not have in
    + 	 * our object database. Skip those, as we cannot use them as boundary.
    + 	 */
    +-	if (!gvfs_config_is_set(r, GVFS_MISSING_OK) && !odb_has_object(r->objects, oid, 0))
    ++	if (check_missing && !odb_has_object(r->objects, oid, 0))
      		return;
    - 
    - 	if (negative)
    + 	oid_array_append(haves, oid);
    + }
     @@ send-pack.c: static int pack_objects(struct repository *r,
    - 	struct child_process po = CHILD_PROCESS_INIT;
    - 	FILE *po_in;
    + 	struct odb_generate_pack_options opts = ODB_GENERATE_PACK_OPTIONS_INIT;
    + 	struct odb_pack_generator *generator;
      	int rc;
     +	int negative_ref_check = 0;
     +	int check_missing;
      
      	trace2_region_enter("send_pack", "pack_objects", r);
    - 	strvec_push(&po.args, "pack-objects");
    + 
     @@ send-pack.c: static int pack_objects(struct repository *r,
    - 	 * parameters by writing to the pipe.
      	 */
    - 	po_in = xfdopen(po.in, "w");
    -+
    + 	opts.pack_fd = args->stateless_rpc ? -1 : fd;
    + 
     +	/*
     +	 * Normally we omit a negative (exclusion) object that we do not have
     +	 * locally. The core.gvfs GVFS_MISSING_OK bit disables that check, so
    @@ send-pack.c: static int pack_objects(struct repository *r,
     +			!gvfs_config_is_set(r, GVFS_MISSING_OK);
     +
      	for (size_t i = 0; i < advertised->nr; i++)
    --		feed_object(r, &advertised->oid[i], po_in, 1);
    -+		feed_object(r, &advertised->oid[i], po_in, 1, check_missing);
    +-		append_negative_object(r, &opts.haves, &advertised->oid[i]);
    ++		append_negative_object(r, &opts.haves, &advertised->oid[i],
    ++				       check_missing);
      	for (size_t i = 0; i < negotiated->nr; i++)
    --		feed_object(r, &negotiated->oid[i], po_in, 1);
    -+		feed_object(r, &negotiated->oid[i], po_in, 1, check_missing);
    +-		append_negative_object(r, &opts.haves, &negotiated->oid[i]);
    ++		append_negative_object(r, &opts.haves, &negotiated->oid[i],
    ++				       check_missing);
      
      	while (refs) {
      		if (!is_null_oid(&refs->old_oid))
    --			feed_object(r, &refs->old_oid, po_in, 1);
    -+			feed_object(r, &refs->old_oid, po_in, 1, check_missing);
    +-			append_negative_object(r, &opts.haves, &refs->old_oid);
    ++			append_negative_object(r, &opts.haves, &refs->old_oid,
    ++					       check_missing);
      		if (!is_null_oid(&refs->new_oid))
    --			feed_object(r, &refs->new_oid, po_in, 0);
    -+			feed_object(r, &refs->new_oid, po_in, 0, check_missing);
    + 			oid_array_append(&opts.wants, &refs->new_oid);
      		refs = refs->next;
    - 	}
    - 
  • 289: 11f7294 < -: ------------ packfile: fix perf regression with many packs

  • 290: a16eda4 < -: ------------ fixup! azure-pipelines: build, sign, notarize and stage the macOS installer

  • 291: dc305fc ! 273: 07316d5 odb: scan all sources' packfiles before loose objects

    @@ odb.c: static int register_all_submodule_sources(struct object_database *odb)
      	return ret;
      }
      
    -+static int read_object_info_from_sources(struct object_database *odb,
    -+					 const struct object_id *oid,
    -+					 struct object_info *oi,
    -+					 enum object_info_flags flags)
    ++static enum odb_read_status read_object_info_from_sources(
    ++	struct object_database *odb, const struct object_id *oid,
    ++	struct object_info *oi, enum object_info_flags flags,
    ++	struct strbuf *errmsg)
     +{
     +	struct odb_source *source;
    ++	enum odb_read_status ret = ODB_READ_NOT_FOUND;
     +
    -+	for (source = odb->sources; source; source = source->next)
    -+		if (!odb_source_read_object_info(source, oid, oi, flags))
    -+			return 0;
    -+	return -1;
    ++	for (source = odb->sources; source; source = source->next) {
    ++		enum odb_read_status source_ret = odb_source_read_object_info(
    ++			source, oid, oi, flags, errmsg->len ? NULL : errmsg);
    ++
    ++		if (!source_ret)
    ++			return ODB_READ_OK;
    ++		if (source_ret != ODB_READ_NOT_FOUND)
    ++			ret = source_ret;
    ++	}
    ++
    ++	return ret;
     +}
     +
    - static int do_oid_object_info_extended(struct object_database *odb,
    - 				       const struct object_id *oid,
    - 				       struct object_info *oi, unsigned flags)
    -@@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    + static enum odb_read_status do_oid_object_info_extended(struct object_database *odb,
    + 							const struct object_id *oid,
    + 							struct object_info *oi, unsigned flags)
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 
    + 	while (1) {
      		extern int core_use_gvfs_helper;
    - 		struct odb_source *source;
    +-		struct odb_source *source;
    ++		enum object_info_flags source_flags = flags;
      
    --		for (source = odb->sources; source; source = source->next)
    --			if (!odb_source_read_object_info(source, real, oi, flags))
    -+		/*
    -+		 * With one or more alternates, scan the packfiles of
    -+		 * every source before consulting any source's loose
    -+		 * object store. Otherwise a primary-source loose lookup
    -+		 * -- a filesystem stat that, without OBJECT_INFO_QUICK,
    -+		 * bypasses the cached loose index -- runs for every
    -+		 * object that resides in an alternate's packfile.
    -+		 * cache_tree_fully_valid() checks many tree objects that
    -+		 * live in an alternate, so this avoids a stat() per
    -+		 * object.
    -+		 */
    -+		if (odb->sources && odb->sources->next) {
    -+			if (!read_object_info_from_sources(odb, real, oi,
    -+					flags | OBJECT_INFO_SKIP_LOOSE))
    - 				return 0;
    -+			if (!read_object_info_from_sources(odb, real, oi,
    -+					flags | OBJECT_INFO_SKIP_PACKED))
    -+				return 0;
    -+		} else if (!read_object_info_from_sources(odb, real, oi,
    -+							  flags)) {
    -+			return 0;
    -+		}
    +-		for (source = odb->sources; source; source = source->next) {
    +-			ret = odb_source_read_object_info(source, real, oi, flags,
    +-							  corrupt_err.len ? NULL : &corrupt_err);
    ++		if (odb_has_alternates(odb)) {
    ++			ret = read_object_info_from_sources(odb, real, oi,
    ++				flags | OBJECT_INFO_SKIP_LOOSE, &corrupt_err);
    + 			if (!ret)
    + 				goto out;
    + 			if (ret != ODB_READ_NOT_FOUND)
    + 				corrupt = true;
    ++			source_flags |= OBJECT_INFO_SKIP_PACKED;
    + 		}
      
    ++		ret = read_object_info_from_sources(odb, real, oi, source_flags,
    ++						   &corrupt_err);
    ++		if (!ret)
    ++			goto out;
    ++		if (ret != ODB_READ_NOT_FOUND)
    ++			corrupt = true;
    ++
      		if (core_use_gvfs_helper && !tried_gvfs_helper) {
      			enum gh_client__created ghc;
    + 
    +@@ odb.c: static enum odb_read_status do_oid_object_info_extended(struct object_database *
    + 		 * caches or reload on-disk state.
    + 		 */
    + 		if (!(flags & OBJECT_INFO_QUICK)) {
    +-			for (source = odb->sources; source; source = source->next) {
    +-				ret = odb_source_read_object_info(source, real, oi,
    +-								  flags | OBJECT_INFO_SECOND_READ,
    +-								  corrupt_err.len ? NULL : &corrupt_err);
    +-				if (!ret)
    +-					goto out;
    +-				if (ret != ODB_READ_NOT_FOUND)
    +-					corrupt = true;
    +-			}
    ++			ret = read_object_info_from_sources(odb, real, oi,
    ++					flags | OBJECT_INFO_SECOND_READ,
    ++					&corrupt_err);
    ++			if (!ret)
    ++				goto out;
    ++			if (ret != ODB_READ_NOT_FOUND)
    ++				corrupt = true;
    + 			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
     
      ## odb.h ##
     @@ odb.h: enum object_info_flags {
    @@ odb.h: enum object_info_flags {
      	 * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK.
     
      ## odb/source-files.c ##
    -@@ odb/source-files.c: static int odb_source_files_read_object_info(struct odb_source *source,
    +@@ odb/source-files.c: static enum odb_read_status odb_source_files_read_object_info(struct odb_source
    + 							      struct strbuf *errmsg)
      {
      	struct odb_source_files *files = odb_source_files_downcast(source);
    - 
    --	if (!packfile_store_read_object_info(files->packed, oid, oi, flags) ||
    -+	if (!(flags & OBJECT_INFO_SKIP_PACKED) &&
    -+	    !packfile_store_read_object_info(files->packed, oid, oi, flags))
    -+		return 0;
    +-	enum odb_read_status ret_packed, ret_loose;
    +-
    +-	ret_packed = odb_source_read_object_info(&files->packed->base, oid, oi,
    +-						 flags, errmsg);
    +-	if (!ret_packed)
    +-		return 0;
    ++	enum odb_read_status ret_packed = ODB_READ_NOT_FOUND;
    ++	enum odb_read_status ret_loose = ODB_READ_NOT_FOUND;
     +
    -+	if (!(flags & OBJECT_INFO_SKIP_LOOSE) &&
    - 	    !odb_source_read_object_info(&files->loose->base, oid, oi, flags))
    - 		return 0;
    ++	if (!(flags & OBJECT_INFO_SKIP_PACKED)) {
    ++		ret_packed = odb_source_read_object_info(&files->packed->base,
    ++			oid, oi, flags, errmsg);
    ++		if (!ret_packed)
    ++			return 0;
    ++	}
    + 
    +-	ret_loose = odb_source_read_object_info(&files->loose->base, oid, oi, flags,
    +-						ret_packed == ODB_READ_NOT_FOUND ? errmsg : NULL);
    +-	if (!ret_loose)
    +-		return 0;
    ++	if (!(flags & OBJECT_INFO_SKIP_LOOSE)) {
    ++		ret_loose = odb_source_read_object_info(&files->loose->base,
    ++			oid, oi, flags,
    ++			ret_packed == ODB_READ_NOT_FOUND ? errmsg : NULL);
    ++		if (!ret_loose)
    ++			return 0;
    ++	}
      
    + 	/*
    + 	 * Reading the packed object may have failed even though the object
    +
    + ## odb/source.h ##
    +@@ odb/source.h: struct odb_source {
    + 	 *     second read in case they know that the first read would have
    + 	 *     already surfaced the object without reloading any on-disk state.
    + 	 *
    ++	 *   - `OBJECT_INFO_SKIP_LOOSE` and `OBJECT_INFO_SKIP_PACKED` tell the
    ++	 *     files backend not to consult its loose or packed source,
    ++	 *     respectively.
    ++	 *
    + 	 * The callback is expected to return an `enum odb_read_status`. Please
    + 	 * refer to the individual values that can be returned. In case reading
    + 	 * the object has failed with a generic error and `errmsg` is non-NULL,
     
      ## t/t5615-alternate-env.sh ##
     @@ t/t5615-alternate-env.sh: test_expect_success !MINGW 'broken quoting falls back to interpreting raw' '
  • 292: 84d8b49 = 274: 7cb6723 scalar: add --[no-]prefetch option

  • 293: ee1dea7 = 275: af74b96 scalar: request for commit via POST

By default, GVFS Protocol-enabled Scalar clones will fall back to the
origin server if there is a network issue with the cache servers.
However (and especially for the prefetch endpoint) this may be a very
expensive operation for the origin server, leading to the user being
throttled. This shows up later in cases such as 'git push' or other web
operations.

To avoid this, create a new config option, 'gvfs.fallback', which
defaults to true. When set to 'false', pass '--no-fallback' from the
gvfs-helper client to the child gvfs-helper server process.

This will allow users who have hit this problem to avoid it in the
future. In case this becomes a more widespread problem, engineering
systems can enable the config option more broadly.

Enabling the config will of course lead to immediate failures for users,
but at least that will help diagnose the problem when it occurs instead
of later when the throttling shows up and the server load has already
passed, damage done.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
- sign using Azure-stored certificates & client
- sign on Windows agent via python script
- 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>
When we create temp files for downloading packs, we use a name
based on the current timestamp. There is no randomness in the
name, so we can have collisions in the same second.

Retry the temp pack names using a new "-<retry>" suffix to the
name before the ".temp".

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Add "mayhem" keys to generate corrupt packfiles and/or corrupt idx
files in prefetch by trashing the trailing checksum SHA.

Add unit tests to t5799 to verify that `gvfs-helper` detects these
corrupt pack/idx files.

Currently, only the (bad-pack, no-idx) case is correctly detected,
Because `gvfs-helper` needs to locally compute the idx file itself.

A test for the (bad-pack, any-idx) case was also added (as a known
breakage) because `gvfs-helper` assumes that when the cache server
provides both, it doesn't need to verify them.  We will fix that
assumption in the next commit.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Create new `cache_http_503` mayhem method where only the cache server
sends a 503.  The normal `http_503` directs both cache and origin
server to send 503s.  This will be used to help test fallback.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
It really does not make sense to run that workflow in any fork of
git-for-windows/git. Typically, it is enough to simply disable it (since
it is a scheduled workflow, it is disabled by default in any new fork).

However, in microsoft/git, we switch the default branch whenever we
rebase to a new upstream version, and every time we do so, this
scheduled workflow gets re-enabled.

Let's just delete it in microsoft/git and never be bothered by it again.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
- create release & uploads artifact using Octokit
- use job "if" condition to handle uploading signed *or* unsigned .deb

Co-authored-by: Lessley Dennington <ldennington@github.com>
`sparse` complains with an error message like this:

	gvfs-helper.c:2912:17: error: expression using sizeof on a
	function

The culprit is this line:

	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);

Similar lines exist in `http-push.c` and other files that are in
upstream Git, and to avoid these bogus warnings, they are already
exempted from `sparse`'s tender, loving care. We simply add
`gvfs-helper.c` to that list.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
The GVFS cache server can return multiple pairs of (.pack, .idx)
files.  If both are provided, `gvfs-helper` assumes that they are
valid without any validation.  This might cause problems if the
.pack file is corrupt inside the data stream.  (This might happen
if the cache server sends extra unexpected STDERR data or if the
.pack file is corrupt on the cache server's disk.)

All of the .pack file verification logic is already contained
within `git index-pack`, so let's ignore the .idx from the data
stream and force compute it.

This defeats the purpose of some of the data cacheing on the cache
server, but safety is more important.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
For Scalar and VFS for Git, we use an alternate as a shared object
cache. We need to enable the maintenance builtin to work on that
shared object cache, especially in the background.

'scalar run <task>' would set GIT_OBJECT_DIRECTORY to handle this.

We set GIT_OBJECT_DIRECTORY based on the gvfs.sharedCache config,
but we also need the checks in pack_loose() to look at that object
directory instead of the current ODB's.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
When the virtualfilesystem is enabled the previous implementation of
clear_ce_flags would iterate all of the cache entries and query whether
each one is in the virtual filesystem to determine whether to clear one
of the SKIP_WORKTREE bits. For each cache entry, we would do a hash
lookup for each parent directory in the is_included_in_virtualfilesystem
function.

The former approach is slow for a typical Windows OS enlistment with
3 million files where only a small percentage is in the virtual
filesystem. The cost is
O(n_index_entries * n_chars_per_path * n_parent_directories_per_path).

In this change, we use the same approach as apply_virtualfilesystem,
which iterates the set of entries in the virtualfilesystem and searches
in the cache for the corresponding entries in order to clear their
flags. This approach has a cost of
O(n_virtual_filesystem_entries * n_chars_per_path * log(n_index_entries)).

The apply_virtualfilesystem code was refactored a bit and modified to
clear flags for all names that 'alias' a given virtual filesystem name
when ignore_case is set.

n_virtual_filesystem_entries is typically much less than
n_index_entries, in which case the new approach is much faster. We wind
up building the name hash for the index, but this occurs quickly thanks
to the multi-threading.

Signed-off-by: Neeraj Singh <neerajsi@ntdev.microsoft.com>
This was disabled by a0da6de (ci: only run win+VS build & tests in
Git for Windows' fork, 2022-12-19) to avoid other forks doing too many
builds. But we want to keep these builds for the microsoft/git fork.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
By default, Git fails immediately when locking a config file for writing
fails due to an existing lock. With this change, Scalar-registered
repositories will fall back to trying a couple times within a 150ms
timeout.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
By default, Git fails immediately when locking a config file for writing
fails due to an existing lock. With this change, Scalar-registered
repositories will fall back to trying a couple times within a 150ms
timeout.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
In Git v2.56.0, a different name was chosen for that setting, and also a
different default value. Let's automagically upgrade the name of the
config setting, if present.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit reintroduces the 'core.configWriteLockTimeoutMS' config
setting (originally added in 6333d1a
(git_config_set_multivar_in_file_gently(): add a lock timeout,
2021-05-18) as part of Microsoft v2.32.0).

Upstream, this setting was superseded by the `core.configLockTimeout`
setting that was added in df67d73 (config: retry acquiring
config.lock, configurable via core.configLockTimeout, 2026-05-17).
However, several applications (e.g., 'scalar') utilize the original
config setting, so it should be preserved for a deprecation period
before complete removal:

Additionally, for this deprecation period, advise users to switch to
using 'core.configLockTimeout' to specify their preferred timeout.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
These docs have been altered to fit the version implemented in C within
microsoft/git. This means in particular that the advanced.md file no
longer applied at all. Some other areas were removed or significantly
edited.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Allow concurrent `scalar register` and `scalar unregister` calls to be
more collaborative when trying to lock the global Git config at the very
same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Adding the extra documentation from the Scalar project.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Git traditionally uses those, not backslashes, ever.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Update `git archive` tree-ish argument from `HEAD^{tree}` to `HEAD`. By
using a commit (rather than tree) reference, the commit hash will be stored
as an extended pax header, extractable git `git get-tar-commit-id`.

The intended use-case for this change is building `git` from the output of
`make dist` - in combination with the ability to specify a fallback
`GIT_BUILT_FROM_COMMIT`, a user can extract the commit ID used to build the
archive and set it as `GIT_BUILT_FROM_COMMIT`. The result is fully-populated
information for the commit hash in `git version --build-options`.

Signed-off-by: Victoria Dye <vdye@github.com>
Just do the boilerplate stuff of making a new builtin, including
documentation and integration with git.c.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
We had been using the default issue template from git-for-windows/git,
but we should ask different questions than Git for Windows. Update the
issue template to ask these helpful questions.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Use a fixed 3 tries total to see how that increases our chances of
success for subcommands such as 'git fetch'.

We special-case the `diagnose` command here: When 672196a
(scalar-diagnose: use 'git diagnose --mode=all', 2022-08-12) updated
'scalar diagnose' to run 'git diagnose' as a subprocess, it was passed
through the run_git() caller. We need to avoid repeating the call when
the underlying 'git diagnose' command fails.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Update build-git-installers workflow to publish `microsoft/git`'s GPG public
key as part of each release. Add explanation for how to use this key to verify
the Debian package's signature to the README.
Set the `GIT_BUILT_FROM_COMMIT` based on the version specified in the `make
dist` output archive header. This ensures the commit hash is shown in
`git version --build-options`.

Signed-off-by: Victoria Dye <vdye@github.com>
On Windows, we have the 'git update-git-for-windows' command. It is
poorly named within the microsoft/git fork, because the script has been
updated to look at the GitHub releases of microsoft/git, not
git-for-windows/git.

Still, it handles all the complicated details about downloading,
verifying, and running the installer.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
We have long inherited the pull request template from
git-for-windows/git, but we should probably do a better job of
specifying the need for why a PR in microsoft/git exists instead of an
upstream contribution.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
The .NET version supported running `scalar config` to reconfigure the
current enlistment, and now the C port does, too.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
…taller

The macOS release build at
https://dev.azure.com/mseng/1ES/_build/results?buildId=32088856 fails with
"Homebrew on macOS is only supported on Apple Silicon processors!"
after Homebrew/install#1140 removed Intel
installer support on September 11, 2026.

Pin the installer to the revision preceding that removal so the universal
macOS package can still obtain Intel gettext alongside the ARM64 library,
without changing the existing pool or Homebrew commands.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Homebrew removed Intel installer support on September 11, 2026 in
Homebrew/install#1140, rejecting the Intel
dependency setup with
"Homebrew on macOS is only supported on Apple Silicon processors!"

Pin the installer to the revision preceding that removal to keep this
obsolete, manual-only workflow usable for debugging. Its universal macOS
package still needs both Intel and ARM64 gettext.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Johannes Schindelin (dscho) added a commit that referenced this pull request Sep 21, 2026
Changes outside of `microsoft/git` require some reaction work (these
changes are all cherry-picked from #984, where I needed to make sure
that our rebase to v2.56.0-rc0 works, all except the t4141 fix which is
cherry-picked from v2.56.0-rc1):

- Git for Windows moved from MINGW64 to UCRT64:
- tons of changes required in the Azure Pipeline to create new GitHub
Releases
- The VFS for Git tests need to be adjusted so they use the correct
`git.exe` even if `C:\Program Files\Git` is _still_ a MINGW64
  - The Scalar Functional tests as well
  - The Nano Server CI workflow as well
  - `AGENTS.md` also needs to be updated
- The CMake definition needs to be updated (one additional change to the
one I managed to get integrated into core Git's `master`, but of course
we're still targeting `maint-2.55` here)
  - Upstream's `cargo-meson.sh` needs to be updated, too
- t0060 needs to be adapted (this was done in upstream's `master`
already, but we're based on `maint-2.55` here)
- Homebrew's latest update prevents universal builds
- vcpkg telemetry seems to cause problems where it locks `vcpkg.exe`
(ever so briefly, but it still causes problems)
- test failures are _still_ not surfaced correctly in Docker
container-based CI jobs
- An upstream change changed how the virtual functions of the
`hash_algo` object have to be called; Let's take this change early
- Git v2.56 will forbid "bare" `grep` invocations in the test scripts;
Let's convert them to `test_grep` invocations preemptively
- The `git maintenance` command will change what it runs by default in
v2.56, which would confuse our additions to the corresponding test
script; Adapt our additions early
- While debugging test failures in a draft of the rebase to v2.56 with
`valgrind`, I noticed an incorrect return value in
`make_valgrind_symlink` which I had to fix to continue debugging
- While at it, backport an upstream Git improvement that lets t4141 pass
in ~5 seconds instead of ~3,000 seconds on Windows
@mjcheetham

Copy link
Copy Markdown
Member

I had Astra take a look at the interesting parts of the range-diff and this is what it had to say...


Most differences are straightforward rebase adaptations or absorbed fixups. A few points before merging:

  1. GVFS/ODB error handling: The no-fetch path in odb.c still uses the old direct return. Under the new API, this misclassifies missing objects and can bypass corruption handling and error-buffer cleanup. Please preserve the new status and cleanup contract on this path.

  2. Scalar timeout migration: The migration misses positive config-write error returns and can delete the old setting after writing its replacement fails. Please require a successful replacement write before removing the legacy value.

  3. Survey compatibility: The shim no longer supports the old object-list and name-rev options/configuration, and --detached no longer includes detached-HEAD-only history. Revision naming also changes from effectively off to on by default for ranked commits. Please confirm these changes are intentional and document the migration, or preserve the old behavior.

  4. Advice documentation: The lock-timeout advice toggle documented in Documentation/config/advice.adoc does not match the key registered in advice.c, so the documented opt-out does not work. Please align them.


cc Johannes Schindelin (@dscho)

Prepare the configuration surface for parallel POST workers before the
worker implementation is introduced. Add gvfs.postThreads with a default
of one so this commit does not change request execution on its own.

Document the intended concurrent behavior and clamp values below one.
Later commits in the series consume the value while introducing the
parallel success path and then its complete failure handling.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
The parallel POST implementation needs standalone curl handles with the
same runtime settings as handles allocated through get_active_slot().
Creating raw handles would otherwise omit cookies, configured host
resolutions, redirect policy, IP selection, and current authentication
defaults.

Extract the per-request handle preparation into a shared helper and use
it from get_active_slot(). Expose a function that duplicates the
initialized default handle and applies the same preparation for callers
that manage a handle outside the active-slot machinery.

Also expose whether cookies are configured. Libcurl cannot safely share
cookie state across concurrently performing handles, so callers can
retain an established sequential path in that case.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Fetching a large set of missing objects through gvfs-helper performs
each HTTP POST and index-pack operation sequentially. This leaves the
client waiting on individual network transfers even when the server and
local machine can support concurrent work.

Introduce the parallel success-path mechanism. Use a mutex-protected
queue to distribute full object batches across worker threads. Each
worker owns a curl handle and streams each response into a fresh
index-pack process.

Serialize child startup while marking pipe descriptors close-on-exec so
concurrent index-pack children cannot keep sibling pipes open. Keep OID
formatting and result collection thread-local, and partition work into
batches containing at least two objects because a single non-commit
object can be returned loose instead of as a pack.

This commit deliberately establishes the core worker and transfer
mechanics first. The next commit completes authentication, throttling,
fallback, retry, and concurrent pack installation behavior before tests
exercise the new path.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
The initial parallel POST path handles successful requests but does not
yet match the sequential path when authentication, throttling, corrupt
responses, or concurrent pack installation interfere with a request.
Those differences can turn recoverable network failures into hard
errors or allow sibling processes to manipulate the same pack paths.

Classify HTTP and curl failures with the existing retry rules, refresh
credentials outside worker threads, and preserve the cache, backup
cache, and origin fallback order. Share response-header parsing with the
sequential path so workers retain rate-limit telemetry while keeping
soft-throttle state local to each worker.

Coordinate Retry-After delays across workers without overflowing sleep
intervals, and wait before starting index-pack. Serialize child setup and
completion because finish_command() invalidates process-global path
state. Limit the worker count to the number of queued object batches.

Give each index-pack attempt unique pack and index paths, validate its
reported pack hash, and retry corrupt or truncated responses. A complete
final pack and index pair remains sufficient when another process wins
the installation race.

Group the per-attempt buffers behind one cleanup helper so success,
retry, fallback, and failure paths release the same state.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Exercise gvfs-helper POST requests with both one and four configured
workers so the sequential and parallel paths must fetch identical object
sets. Cover multiple batches, a final single-OID remainder, and duplicate
requests while checking both installed objects and packfile counts.

Require pthread support for parallel cases and use Trace2 assertions to
prove that each test reaches its intended execution mode.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Parallel requests need to preserve the sequential path's behavior for
configuration boundaries, authentication, throttling, cache fallback,
corrupt responses, and request headers.

Extend the protocol test server with targeted failure modes. Verify that
parallel POST refreshes authentication, honors Retry-After, falls back
from cache 404 responses only when permitted, retries a one-time corrupt
pack, and reports permanent corruption as an index-pack failure.

Also cover absent and invalid thread configuration, cookie-enabled
sequential fallback, configured headers, multiple participating workers,
and a timeout-protected child-pipe stress case.

Helped-by: GPT-5.6 Sol
Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
The deadlock test fails before Git runs in the minimal Git for Windows
SDK: GNU `timeout` is absent, but the availability check finds
`System32/timeout.exe`, which rejects the arguments.

Reuse t7812's Perl `alarm`/`exec` idiom to retain the deadline without
that dependency. `PERL_TEST_HELPERS` checks `PERL_PATH`, unlike `PERL`, so
`NO_PERL` builds with an available interpreter retain coverage.

Assisted-by: GPT-6 Astra
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The cache-404 fallback test fails in CI with HTTP 501 on curl 7.68.0.
The mock server returns 404 before consuming the POST body but leaves
the connection open. On reuse, the unread JSON prefixes the next
request's method.

For compatibility, `gvfs-helper` disables `CURLOPT_FAILONERROR` for
curl older than 7.75.0 and enables it otherwise. The enabled option
closes the connection on the HTTP error, masking the framing defect.

The early 404 must close the connection, as the cache-503 response
already does. Keep production curl behavior unchanged.

Assisted-by: GPT-6 Astra
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Improve full-clone performance by allowing `gvfs-helper post` to
download
object batches concurrently. The new `gvfs.postThreads` configuration
defaults to 1, preserving the existing sequential behavior unless
explicitly
enabled.

The series is organized into six reviewable commits:

1. Add the configuration surface.
2. Factor reusable curl-handle preparation.
3. Introduce the parallel POST success path.
4. Complete retry, authentication, fallback, throttling, and pack
   installation behavior.
5. Test sequential and parallel success paths.
6. Test failure handling and request-header parity.

Each worker owns an independently prepared curl handle and streams
responses
into a dedicated `index-pack --stdin` child. Work is distributed through
a
mutex-protected queue, with the worker count capped by the number of
object
batches. Requests with configured cookies retain the established
sequential
path because libcurl cookie state cannot safely be shared by
concurrently
performing handles.

The parallel path preserves the sequential HTTP policy for
authentication
refresh, transient errors, Retry-After, cache and backup-cache fallback,
`--no-fallback`, configured headers, `X-Session-Id`, and `X-VSS-E2EID`.
Sequential and parallel requests share response-header parsing, while
soft-throttle state remains local to each worker.

Child pipes are created and marked close-on-exec before spawning, and
child
setup and completion are serialized around process-global run-command
state.
Each `index-pack` attempt uses unique pack and index paths, validates
its
reported pack hash, retries corrupt or truncated responses, and
tolerates
another process winning installation of the same final pack.

The focused `t5798-gvfs-helper-post-threads.sh` suite contains 22 tests
covering configuration boundaries, sequential and parallel requests,
multi-worker participation, singleton remainders, duplicate downloads,
deadlock prevention, authentication, throttling, cache fallback,
`--no-fallback`, corrupt packs, cookies, and configured headers.
Parallel
tests use pthread prerequisites and Trace2 assertions to prove the
intended
execution path.

Neil Kainga diagnosed and tested the pipe-inheritance fix on a 1JS full
clone
with `gvfs.postThreads=8` and is credited throughout the series.
release: remove the obsolete GitHub installer workflow

The manual GitHub installer workflow depends on secrets and expired
code-signing certificates. Retire this unusable entry point now that
signed releases use the Azure release pipeline.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
…taller

azure-pipelines: add signed macOS ARM64 releases

Release signing is moving from the obsolete, certificate-dependent
GitHub workflow to Azure/ESRP. Use that path to provide signed and
notarized macOS installers with the existing packaging machinery.

Universal macOS builds are no longer viable, so target ARM64 only.
Windows and Linux releases remain unchanged.

Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com>
Assisted-by: Claude Opus 4.7
Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
After 2135b14 (odb: handle `OBJECT_INFO_DIE_IF_CORRUPT` generically),
the GVFS no-fetch shortcut bypasses shared failure handling. It returns
an error status even for missing objects and can hide a corruption
diagnostic behind a generic traversal failure.

Preserve the no-fetch guarantee while restoring correct failure reporting
and diagnostic cleanup.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The timeout migration can discard the legacy value after a failed
replacement write, silently falling back to the default. It mistakes
positive errors from `repo_config_set_gently()` for success.

Recognize failures of either sign to preserve the legacy value when its
replacement cannot be written and to report errors removing the old key.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The documented opt-out uses the wrong advice key, so following it does
not silence the warning about deprecated `core.configWriteLockTimeoutMS`.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The deprecation note still promises behavior intentionally removed by
the shim. Explain the migration to `git repo structure` so users can
choose their ref scope and output, including opting out of name lookup,
without relying on ignored configuration or unsupported options.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho

Copy link
Copy Markdown
Member Author

Matthew John Cheetham (@mjcheetham) thanks! All valid points. I also forward-ported #980 and dropped the universal macOS binaries (because Homebrew dropped support for Intel, quite prematurely if you ask me).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments

Comment thread odb.c Outdated

if (flags & OBJECT_INFO_SKIP_FETCH_OBJECT)
return -1;
goto not_found;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This goto not_found jump lands us after the OBJECT_INFO_DIE_IF_CORRUPT check, so no-fetch lookups still skip that handling.

Should we move the not_found: label above the OBJECT_INFO_DIE_IF_CORRUPT block? That lets the no-fetch path skip fetching while still performing corruption handling, choosing the correct return status, and releasing corrupt_err.

Might also then want to rename the label to something like check_corrupt: for clarity?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Astra suggested that placement at first, but is it really appropriate? After all, we're entering the "not found" path for a reason: the object was not found and therefore the "is this object corrupt?" check would try to compare the desired vs the actual OID, a comparison that must fail (because the object was not found, and therefore the actual OID could not possibly be initialized, right?).

Or am I missing anything?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a corrupt local object, with no replacement refs or other usable copy, and the GVFS helper enabled. The caller passes flags for both OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_DIE_IF_CORRUPT.

The local read sets corrupt = true, but goto not_found skips the fatal check and just returns an error (not die). "Don't fetch" has now overridden the flag to "die on corruption".

There's also no uninitialised OID, since real starts as oid, so without replacement refs they are identical whether the read succeeds or not.

Comment thread .azure-pipelines/release.yml
Comment thread scalar.c
Let's handle corrupt objects correctly (not as "missing"). I erroneously
thought originally that the check in this conditional block that is now
exercised in the "not_found" case was bogus, but `oid` and `real` start
out being identical (I thought that `real` was only populated once an
object was read).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
With the GVFS helper enabled, a no-fetch lookup can report a parent commit
as missing despite a readable copy in another local pack. The early jump
skips the second read needed to recover from a stale multi-pack-index.

Keep local recovery available before applying the usual corruption
policy. `OBJECT_INFO_QUICK`, not `OBJECT_INFO_SKIP_FETCH_OBJECT`, controls
whether to reprepare the local sources.

The no-fetch request must also apply to the read-object hook: otherwise
falling through to local recovery can end up fetching the object anyway.

Assisted-by: GPT-6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Address error: "FOO=bar shell_func" is not portable

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
We need to prevent the test case that disables writing the commit graph
from forcefully using the (now stale) commit graph.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
Johannes Schindelin (dscho) merged commit 6d69130 into vfs-2.56.0-rc0 Sep 23, 2026
183 checks passed
@dscho
Johannes Schindelin (dscho) deleted the tentative/vfs-2.56.0-rc0 branch September 23, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants