Skip to content

Rebase to v2.55.0-rc0#936

Merged
dscho merged 363 commits into
vfs-2.55.0-rc0from
tentative/vfs-2.55.0-rc0
Jun 16, 2026
Merged

Rebase to v2.55.0-rc0#936
dscho merged 363 commits into
vfs-2.55.0-rc0from
tentative/vfs-2.55.0-rc0

Conversation

@dscho

@dscho dscho commented Jun 15, 2026

Copy link
Copy Markdown
Member
Range-diff relative to vfs-2.54.0
  • 1: a8452ef ! 1: e976362 survey: calculate more stats on refs

    @@ builtin/survey.c: static void survey_report_plaintext_refs(struct survey_context
      }
     @@ builtin/survey.c: static void survey_phase_refs(struct survey_context *ctx)
      	for (int i = 0; i < ctx->ref_array.nr; i++) {
    - 		unsigned long size;
    + 		size_t size;
      		struct ref_array_item *item = ctx->ref_array.items[i];
     +		size_t len = strlen(item->refname);
      
  • 2: 2321fd6 ! 2: cfcc439 survey: show some commits/trees/blobs histograms

    @@ builtin/survey.c: static void survey_report_plaintext(struct survey_context *ctx
      		&ctx->report.top_paths_by_count[REPORT_TYPE_TREE]);
      	survey_report_plaintext_sorted_size(
     @@ builtin/survey.c: static void increment_totals(struct survey_context *ctx,
    - 		unsigned long object_length = 0;
    + 		size_t object_length = 0;
      		off_t disk_sizep = 0;
      		enum object_type type;
     +		struct survey_stats_base_object *base;
  • 3: 6fdf945 = 3: 3ad2074 survey: add vector of largest objects for various scaling dimensions

  • 4: 0ca3873 = 4: 6ba2177 survey: add pathname of blob or tree to large_item_vec

  • 5: db1cdd0 = 5: ca28c42 survey: add commit-oid to large_item detail

  • 6: e6ba70a = 6: bd06347 survey: add commit name-rev lookup to each large_item

  • 7: 8534ebc = 7: 7e6152a survey: add --no-name-rev option

  • 10: 891f70c = 8: e2461c2 sparse-index.c: fix use of index hashes in expand_index

  • 8: cacaeb0 = 9: 4f0737c survey: started TODO list at bottom of source file

  • 12: 71a54dc = 10: 5252692 t5300: confirm failure of git index-pack when non-idx suffix requested

  • 11: 4cdb47d = 11: e55b8e0 t: remove advice from some tests

  • 14: 411229f = 12: 6ca2226 t1092: add test for untracked files and directories

  • 9: 84c21fb = 13: b55b3e8 survey: expanded TODO list at the bottom of the source file

  • 15: d48ea40 = 14: f2debf2 index-pack: disable rev-index if index file has non .idx suffix

  • 16: 7a375bf = 15: 101caff trace2: prefetch value of GIT_TRACE2_DST_DEBUG at startup

  • 13: 4a3622e = 16: 39d9294 survey: expanded TODO with more notes

  • 17: bfe187a = 17: d1e7207 reset --stdin: trim carriage return from the paths

  • 18: b8ce767 ! 18: 4b60ba5 Identify microsoft/git via a distinct version suffix

    @@ Commit message
      ## GIT-VERSION-GEN ##
     @@
      
    - DEF_VER=v2.54.0
    + DEF_VER=v2.55.0-rc0
      
     +# Identify microsoft/git via a distinct version suffix
     +DEF_VER=$DEF_VER.vfs.0.0
  • 19: 74aaaed = 19: 16135da gvfs: ensure that the version is based on a GVFS tag

  • 20: 9037a12 = 20: ba57143 gvfs: add a GVFS-specific header file

  • 21: 3bb5350 = 21: 48abb0e gvfs: add the core.gvfs config setting

  • 22: 2e5589d = 22: f5c5222 gvfs: add the feature to skip writing the index' SHA-1

  • 23: 8d12904 = 23: f21096b gvfs: add the feature that blobs may be missing

  • 24: 1210bf1 = 24: 6e5b6c4 gvfs: prevent files to be deleted outside the sparse checkout

  • 25: 2a26b3e = 25: 1527ed1 gvfs: optionally skip reachability checks/upload pack during fetch

  • 26: 6c77873 = 26: f32c48f gvfs: ensure all filters and EOL conversions are blocked

  • 27: 25b9de3 ! 27: 4a43cc3 gvfs: allow "virtualizing" objects

    @@ odb.c: void disable_obj_read_lock(void)
      
      static int register_all_submodule_sources(struct object_database *odb)
     @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
    - 	const struct cached_object *co;
    + {
      	const struct object_id *real = oid;
      	int already_retried = 0;
     +	int tried_hook = 0;
    @@ odb.c: static int do_oid_object_info_extended(struct object_database *odb,
      		return -1;
      
     +retry:
    - 	co = find_cached_object(odb, real);
    - 	if (co) {
    - 		if (oi) {
    + 	if (!odb_source_read_object_info(odb->inmemory_objects, oid, oi, flags))
    + 		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))
  • 28: 397b60d ! 28: 776bc05 Hydrate missing loose objects in check_and_freshen()

    @@ contrib/long-running-read-object/example.pl (new)
     +	}
     +}
     
    - ## object-file.c ##
    -@@
    - #include "environment.h"
    - #include "fsck.h"
    - #include "gettext.h"
    -+#include "gvfs.h"
    - #include "hex.h"
    - #include "loose.h"
    - #include "object-file-convert.h"
    -@@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
    - 				    int freshen)
    - {
    - 	static struct strbuf path = STRBUF_INIT;
    -+	int ret, tried_hook = 0;
    -+
    - 	odb_loose_path(source, &path, oid);
    --	return check_and_freshen_file(path.buf, freshen);
    -+retry:
    -+	ret = check_and_freshen_file(path.buf, freshen);
    -+	if (!ret && gvfs_virtualize_objects(source->odb->repo) && !tried_hook) {
    -+		tried_hook = 1;
    -+		if (!read_object_process(source->odb->repo, oid))
    -+			goto retry;
    -+	}
    -+	return ret;
    - }
    - 
    - int odb_source_loose_has_object(struct odb_source *source,
    -
      ## odb.c ##
     @@
    - #include "odb.h"
    + #include "odb/source-inmemory.h"
      #include "packfile.h"
      #include "path.h"
     +#include "pkt-line.h"
    @@ odb.h: void parse_alternates(const char *string,
     +
      #endif /* ODB_H */
     
    + ## odb/source-loose.c ##
    +@@
    + #include "abspath.h"
    + #include "chdir-notify.h"
    + #include "gettext.h"
    ++#include "gvfs.h"
    + #include "hex.h"
    + #include "loose.h"
    + #include "object-file.h"
    +@@ odb/source-loose.c: static int odb_source_loose_freshen_object(struct odb_source *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);
    ++retry:
    ++	ret = check_and_freshen_file(path.buf, 1);
    ++	if (!ret && gvfs_virtualize_objects(source->odb->repo) && !tried_hook) {
    ++		tried_hook = 1;
    ++		if (!read_object_process(source->odb->repo, oid))
    ++			goto retry;
    ++	}
    ++	return ret;
    + }
    + 
    + static int odb_source_loose_write_object(struct odb_source *source,
    +
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
        't0410-partial-clone.sh',
  • 29: e611a66 ! 29: 32db9e1 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 check_and_freshen_file(const char *fn, int freshen)
    - 
    - static int check_and_freshen_source(struct odb_source *source,
    - 				    const struct object_id *oid,
    --				    int freshen)
    -+				    int freshen, int skip_virtualized_objects)
    - {
    - 	static struct strbuf path = STRBUF_INIT;
    - 	int ret, tried_hook = 0;
    -@@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
    - 	odb_loose_path(source, &path, oid);
    - retry:
    - 	ret = check_and_freshen_file(path.buf, freshen);
    --	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;
    -@@ object-file.c: static int check_and_freshen_source(struct odb_source *source,
    - int odb_source_loose_has_object(struct odb_source *source,
    - 				const struct object_id *oid)
    - {
    --	return check_and_freshen_source(source, oid, 0);
    -+	return check_and_freshen_source(source, oid, 0, 0);
    - }
    - 
    - int format_object_header(char *str, size_t size, enum object_type type,
    -@@ object-file.c: static int write_loose_object(struct odb_source *source,
    - }
    - 
    - int odb_source_loose_freshen_object(struct odb_source *source,
    --				    const struct object_id *oid)
    -+				    const struct object_id *oid,
    -+				    int skip_virtualized_objects)
    - {
    --	return !!check_and_freshen_source(source, oid, 1);
    -+	return !!check_and_freshen_source(source, oid, 1, skip_virtualized_objects);
    - }
    - 
    - int odb_source_loose_write_stream(struct odb_source *source,
    -@@ object-file.c: int odb_source_loose_write_stream(struct odb_source *source,
    +@@ 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(source, fd, tmp_file.buf);
    + 	close_loose_object(loose, fd, tmp_file.buf);
      
    --	if (odb_freshen_object(source->odb, oid)) {
    -+	if (odb_freshen_object(source->odb, oid, 1)) {
    +-	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;
      	}
    -@@ object-file.c: int odb_source_loose_write_object(struct odb_source *source,
    - 	 * it out into .git/objects/??/?{38} file.
    - 	 */
    - 	write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
    --	if (odb_freshen_object(source->odb, oid))
    -+	if (odb_freshen_object(source->odb, oid, 1))
    - 		return 0;
    - 	if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags))
    - 		return -1;
    -
    - ## object-file.h ##
    -@@ object-file.h: int odb_source_loose_has_object(struct odb_source *source,
    - 				const struct object_id *oid);
    - 
    - int odb_source_loose_freshen_object(struct odb_source *source,
    --				    const struct object_id *oid);
    -+				    const struct object_id *oid,
    -+				    int skip_virtualized_objects);
    - 
    - int odb_source_loose_write_object(struct odb_source *source,
    - 				  const void *buf, size_t len,
     
      ## odb.c ##
     @@ odb.c: int odb_has_object(struct object_database *odb, const struct object_id *oid,
    @@ odb/source-files.c: static int odb_source_files_find_abbrev_len(struct odb_sourc
      {
      	struct odb_source_files *files = odb_source_files_downcast(source);
      	if (packfile_store_freshen_object(files->packed, oid) ||
    --	    odb_source_loose_freshen_object(source, oid))
    -+	    odb_source_loose_freshen_object(source, oid, skip_virtualized_objects))
    +-	    odb_source_freshen_object(&files->loose->base, oid))
    ++	    odb_source_freshen_object(&files->loose->base, oid, 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,
    ++					      int skip_virtualized_objects UNUSED)
    + {
    + 	struct odb_source_inmemory *inmemory = odb_source_inmemory_downcast(source);
    + 	if (find_cached_object(inmemory, oid))
    +
    + ## 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,
    ++					   int skip_virtualized_objects)
    + {
    + 	struct odb_source_loose *loose = odb_source_loose_downcast(source);
    + 	static struct strbuf path = STRBUF_INIT;
    +@@ 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);
    +-	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.h ##
     @@ odb/source.h: struct odb_source {
      	 * has been freshened.
    @@ t/t0499-read-object.sh: test_expect_success 'invalid blobs generate errors' '
     +'
      
      test_done
    +
    + ## t/unit-tests/u-odb-inmemory.c ##
    +@@ 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_must_pass(odb_source_write_object(&source->base, "foobar",
    + 					     strlen("foobar"), OBJ_BLOB,
    + 					     &written_oid, NULL, 0));
    + 	cl_assert_equal_i(odb_source_freshen_object(&source->base,
    +-						    &written_oid), 1);
    ++						    &written_oid, 0), 1);
    + 
    + 	odb_source_free(&source->base);
    + }
  • 30: ce00e56 ! 30: 50d1db2 gvfs: add global command pre and post hook procs

    @@ git.c: static int handle_alias(struct strvec *args, struct string_list *expanded
      	int status, help;
     @@ git.c: static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
      	if (!help && p->option & NEED_WORK_TREE)
    - 		setup_work_tree();
    + 		setup_work_tree(the_repository);
      
     +	if (run_pre_command_hook(the_repository, argv))
     +		die("pre-command hook aborted command");
    @@ hook.c
      #include "advice.h"
      #include "config.h"
      #include "environment.h"
    -@@
    - #include "strbuf.h"
    - #include "strmap.h"
    +@@ hook.c: bool is_known_hook(const char *name)
    + 	return false;
    + }
      
     +static int early_hooks_path_config(const char *var, const char *value,
     +				   const struct config_context *ctx UNUSED, void *cb)
  • 31: ee0ef83 = 31: f2b66b6 t0400: verify that the hook is called correctly from a subdirectory

  • 32: e774e3b = 32: df50ef1 t0400: verify core.hooksPath is respected by pre-command

  • 33: 9b93fc1 = 33: d2f23e0 Pass PID of git process to hooks.

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

  • 35: 5486f75 = 35: ca172b4 worktree: allow in Scalar repositories

  • 36: 0142ed0 = 36: 5028c2a sparse-checkout: avoid writing entries with the skip-worktree bit

  • 37: 4d5da32 = 37: b80f9a5 Do not remove files outside the sparse-checkout

  • 38: da90ad3 = 38: db40a15 send-pack: do not check for sha1 file when GVFS_MISSING_OK set

  • 39: 8581441 = 39: 52e78d9 gvfs: allow corrupt objects to be re-downloaded

  • 40: 83160f2 = 40: 9e485b0 cache-tree: remove use of strbuf_addf in update_one

  • 41: 2dd58ca ! 41: 9a24c61 gvfs: block unsupported commands when running in a GVFS repo

    @@ git.c
      	const char *cmd;
     @@ git.c: static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct
      	if (!help && p->option & NEED_WORK_TREE)
    - 		setup_work_tree();
    + 		setup_work_tree(the_repository);
      
     +	if (!help && p->option & BLOCK_ON_GVFS_REPO && gvfs_config_is_set(repo, GVFS_BLOCK_COMMANDS))
     +		die("'git %s' is not supported on a GVFS repo", p->cmd);
    @@ git.c: static int run_builtin(struct cmd_struct *p, int argc, const char **argv,
      		die("pre-command hook aborted command");
      
     @@ git.c: static struct cmd_struct commands[] = {
    - 	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
      	{ "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY },
      	{ "format-patch", cmd_format_patch, RUN_SETUP },
    + 	{ "format-rev", cmd_format_rev, RUN_SETUP },
     -	{ "fsck", cmd_fsck, RUN_SETUP },
     +	{ "fsck", cmd_fsck, RUN_SETUP | BLOCK_ON_GVFS_REPO},
      	{ "fsck-objects", cmd_fsck, RUN_SETUP },
  • 42: e0a2d36 = 42: 412e76a gvfs: allow overriding core.gvfs

  • 43: 3378e7d = 43: c46d073 BRANCHES.md: Add explanation of branches and using forks

  • 44: 0e19dd5 = 44: 1d12566 git.c: add VFS enabled cmd blocking

  • 45: 4685c72 = 45: c5537b0 git.c: permit repack cmd in Scalar repos

  • 50: 8c94b78 ! 46: 14fad46 Add virtual file system settings and hook proc

    @@ virtualfilesystem.c (new)
     +
     +static void parent_directory_hashmap_add(struct hashmap *map, const char *pattern, const int patternlen)
     +{
    -+	char *slash;
    ++	const char *slash;
     +	struct virtualfilesystem *vfs;
     +
     +	/*
  • 46: 6df5778 ! 47: 428d8b1 git.c: permit fsck cmd in Scalar repos

    @@ Commit message
     
      ## git.c ##
     @@ git.c: static struct cmd_struct commands[] = {
    - 	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
      	{ "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY },
      	{ "format-patch", cmd_format_patch, RUN_SETUP },
    + 	{ "format-rev", cmd_format_rev, RUN_SETUP },
     -	{ "fsck", cmd_fsck, RUN_SETUP | BLOCK_ON_GVFS_REPO},
     +	{ "fsck", cmd_fsck, RUN_SETUP | BLOCK_ON_VFS_ENABLED },
      	{ "fsck-objects", cmd_fsck, RUN_SETUP },
  • 51: a9491f2 = 48: f50a091 virtualfilesystem: don't run the virtual file system hook if the index has been redirected

  • 47: fc28ad1 = 49: 6a164e5 git.c: permit prune cmd in Scalar repos

  • 52: 969b601 = 50: 3d193f4 virtualfilesystem: check if directory is included

  • 48: 467879d = 51: 95dc2f2 worktree: remove special case GVFS cmd blocking

  • 53: 8299cb9 = 52: e866ac6 backwards-compatibility: support the post-indexchanged hook

  • 49: ee1a38f ! 53: 5b1ba06 builtin/repack.c: emit warning when shared cache is present

    @@ builtin/repack.c: int cmd_repack(int argc,
     +	    !repo_config_get_value(repo, "gvfs.sharedcache", &tmp_obj_dir))
     +		warning(_("shared object cache is configured but will not be repacked"));
     +
    - 	if (write_midx && write_bitmaps) {
    - 		struct strbuf path = STRBUF_INIT;
    - 
    + 	if (config_ctx.midx_split_factor < 2)
    + 		die(_("invalid value for %s: %d"), "--midx-split-factor",
    + 		    config_ctx.midx_split_factor);
     
      ## gvfs.h ##
     @@ gvfs.h: struct repository;
  • 54: 919620b = 54: de354cb gvfs: verify that the built-in FSMonitor is disabled

  • 55: 710d5a7 = 55: 23beb1c wt-status: add trace2 data for sparse-checkout percentage

  • 56: 5079d37 = 56: c5220bf status: add status serialization mechanism

  • 57: 85d7f19 = 57: 9bf6ebb Teach ahead-behind and serialized status to play nicely together

  • 58: be5fefb = 58: 2802c05 status: serialize to path

  • 59: 008f2c5 = 59: 3a1d711 status: reject deserialize in V2 and conflicts

  • 60: 185e4e7 = 60: cc627c7 serialize-status: serialize global and repo-local exclude file metadata

  • 61: 1ae7a94 = 61: 28dfcd5 status: deserialization wait

  • 62: bfa7736 = 62: f5f1046 status: deserialize with -uno does not print correct hint

  • 63: e059869 = 63: c196940 fsmonitor: check CE_FSMONITOR_VALID in ce_uptodate

  • 64: 9e9d115 = 64: 752fe86 fsmonitor: add script for debugging and update script for tests

  • 65: eda895f = 65: fef7294 status: disable deserialize when verbose output requested.

  • 66: 0ae8e59 = 66: 8621406 t7524: add test for verbose status deserialzation

  • 67: ddea586 = 67: 547a913 deserialize-status: silently fallback if we cannot read cache file

  • 68: 90e5e1e = 68: 6580151 gvfs:trace2:data: add trace2 tracing around read_object_process

  • 69: e7cc49b = 69: ef6f7da gvfs:trace2:data: status deserialization information

  • 70: b8030ad = 70: 0b52ddb gvfs:trace2:data: status serialization

  • 71: 2e5f21e = 71: fca47cd gvfs:trace2:data: add vfs stats

  • 72: ab6aaac = 72: b2571a7 trace2: refactor setting process starting time

  • 73: 7cac489 = 73: fb668b3 trace2:gvfs:experiment: clear_ce_flags_1

  • 74: db7a92d = 74: e84f185 trace2:gvfs:experiment: report_tracking

  • 75: 4b307ec = 75: abb13c8 trace2:gvfs:experiment: read_cache: annotate thread usage in read-cache

  • 76: f9a0cc7 = 76: f16700a trace2:gvfs:experiment: read-cache: time read/write of cache-tree extension

  • 77: 5ccc665 = 77: 5dc7845 trace2:gvfs:experiment: add region to apply_virtualfilesystem()

  • 78: a1c4e16 = 78: a9a8775 trace2:gvfs:experiment: add region around unpack_trees()

  • 79: 3844304 ! 79: 48f6f56 trace2:gvfs:experiment: add region to cache_tree_fully_valid()

    @@ cache-tree.c: static void discard_unused_subtrees(struct cache_tree *it)
      	int i;
      	if (!it)
     @@ cache-tree.c: int cache_tree_fully_valid(struct cache_tree *it)
    - 			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
    + 			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
      		return 0;
      	for (i = 0; i < it->subtree_nr; i++) {
     -		if (!cache_tree_fully_valid(it->down[i]->cache_tree))
  • 80: d2883d8 ! 80: a9ccacf trace2:gvfs:experiment: add unpack_entry() counter to unpack_trees() and report_tracking()

    @@ builtin/checkout.c: static void update_refs_for_switch(const struct checkout_opt
     
      ## packfile.c ##
     @@ packfile.c: struct unpack_entry_stack_ent {
    - 	unsigned long size;
    + 	size_t size;
      };
      
     +static unsigned long g_nr_unpack_entry;
    @@ packfile.c: struct unpack_entry_stack_ent {
     +}
     +
      void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
    - 		   enum object_type *final_type, unsigned long *final_size)
    + 		   enum object_type *final_type, size_t *final_size)
      {
     @@ packfile.c: void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
      	int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
  • 81: c3f8841 = 81: b20970a trace2:gvfs:experiment: increase default event depth for unpack-tree data

  • 82: 654a504 = 82: 3694625 trace2:gvfs:experiment: add data for check_updates() in unpack_trees()

  • 83: 733d0f5 = 83: 8a72600 Trace2:gvfs:experiment: capture more 'tracking' details

  • 84: c047e0a = 84: a99f420 credential: set trace2_child_class for credential manager children

  • 85: 68b5bf2 = 85: 4fba917 sub-process: do not borrow cmd pointer from caller

  • 86: a36b94d = 86: fe084d5 sub-process: add subprocess_start_argv()

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

  • -: ------------ > 87: 969ad0f sha1-file: add function to update existing loose object cache

  • 88: f431105 = 88: d0bb15a index-pack: avoid immediate object fetch while parsing packfile

  • 89: 2467774 ! 89: 31c9a4e gvfs-helper: create tool to fetch objects using the GVFS Protocol

    @@ Documentation/config.adoc: include::config/gui.adoc[]
     +
      include::config/help.adoc[]
      
    - include::config/http.adoc[]
    + include::config/hook.adoc[]
     
      ## Documentation/config/core.adoc ##
     @@ Documentation/config/core.adoc: core.gvfs::
    @@ gvfs-helper.c (new)
     +
     +	trace2_cmd_name("gvfs-helper");
     +
    -+	setup_git_directory_gently(NULL);
    ++	setup_git_directory_gently(the_repository, NULL);
     +
     +	repo_config(the_repository, git_default_config, NULL);
     +
    @@ t/helper/.gitignore
     
      ## t/t1517-outside-repo.sh ##
     @@ t/t1517-outside-repo.sh: do
    - 	credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \
      	daemon | \
    - 	difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \
    + 	difftool--helper | filter-branch | format-rev | fsck-objects | \
    + 	get-tar-commit-id | \
     -	gui | gui--askpass | \
     +	gui | gui--askpass | gvfs-helper | \
      	http-backend | http-fetch | http-push | init-db | \
  • 90: 5834c1e = 90: 523bd35 sha1-file: create shared-cache directory if it doesn't exist

  • 91: 0d8ca0d ! 91: 1a24c7d gvfs-helper: better handling of network errors

    @@ gvfs-helper.c: int cmd_main(int argc, const char **argv)
      	trace2_cmd_name("gvfs-helper");
     +	packet_trace_identity("gvfs-helper");
      
    - 	setup_git_directory_gently(NULL);
    + 	setup_git_directory_gently(the_repository, NULL);
      
  • 92: 5a42fd6 = 92: e86792f gvfs-helper-client: properly update loose cache with fetched OID

  • 93: a4fa869 ! 93: 9554904 gvfs-helper: V2 robust retry and throttling

    @@ gvfs-helper.c: static enum gh__error_code do_sub_cmd__server(int argc, const cha
      
     @@ gvfs-helper.c: int cmd_main(int argc, const char **argv)
      
    - 	setup_git_directory_gently(NULL);
    + 	setup_git_directory_gently(the_repository, NULL);
      
     -	repo_config(the_repository, git_default_config, NULL);
     -
  • 94: 30cc86b ! 94: 4d22a03 gvfs-helper: expose gvfs/objects GET and POST semantics

    @@ gvfs-helper.c: static enum gh__error_code do_sub_cmd(int argc, const char **argv
      		return do_sub_cmd__config(argc, argv);
      
     @@ gvfs-helper.c: int cmd_main(int argc, const char **argv)
    - 	setup_git_directory_gently(NULL);
    + 	setup_git_directory_gently(the_repository, NULL);
      
      	/* Set any non-zero initial values in gh__cmd_opts. */
     -	gh__cmd_opts.depth = GH__DEFAULT_COMMIT_DEPTH;
  • 95: 8f5bfd7 = 95: 1cff3ec gvfs-helper: dramatically reduce progress noise

  • 96: 4c4dd7f = 96: af1324f gvfs-helper: handle pack-file after single POST request

  • 97: 01b2f6c ! 97: 0dfdfb0 test-gvfs-prococol, t5799: tests for gvfs-helper

    @@ t/helper/test-gvfs-protocol.c (new)
     +	int i;
     +
     +	trace2_cmd_name("test-gvfs-protocol");
    -+	setup_git_directory_gently(NULL);
    ++	setup_git_directory_gently(the_repository, NULL);
     +
     +	for (i = 1; i < argc; i++) {
     +		const char *arg = argv[i];
  • 98: aa254e6 = 98: 803741b gvfs-helper: move result-list construction into install functions

  • 99: 22de5b3 = 99: 978fa8c t5799: add support for POST to return either a loose object or packfile

  • 100: 693dcc9 = 100: 7b5308e t5799: cleanup wc-l and grep-c lines

  • 101: 38dc180 = 101: e10006c gvfs-helper: verify loose objects after write

  • 102: 9ebc0f3 = 102: 00da5b8 t7599: create corrupt blob test

  • 103: 577aabd = 103: f6ba3f3 gvfs-helper: add prefetch support

  • 104: 3ff796a = 104: cf3ca53 gvfs-helper: add prefetch .keep file for last packfile

  • 105: 7c13748 = 105: 2d561af gvfs-helper: do one read in my_copy_fd_len_tail()

  • 106: 3fb8569 = 106: 34d8b93 gvfs-helper: move content-type warning for prefetch packs

  • 107: 9e95b97 = 107: 5765d2e fetch: use gvfs-helper prefetch under config

  • 108: c44a206 = 108: a230cf5 gvfs-helper: better support for concurrent packfile fetches

  • 109: 01a538f = 109: c6a70dd remote-curl: do not call fetch-pack when using gvfs-helper

  • 110: 056bb72 = 110: 133d130 fetch: reprepare packs before checking connectivity

  • 111: af57ac7 = 111: 6cfe2bb gvfs-helper: retry when creating temp files

  • 112: 4742bc4 = 112: 863666e sparse: avoid warnings about known cURL issues in gvfs-helper.c

  • 113: 9bf8e27 = 113: 81a2719 gvfs-helper: add --max-retries to prefetch verb

  • 120: cf9dee3 = 114: a2b6c77 t5799: add tests to detect corrupt pack/idx files in prefetch

  • 122: 6c82f84 = 115: 199a51f gvfs-helper: ignore .idx files in prefetch multi-part responses

  • 114: 135eb5d = 116: 3fee605 maintenance: care about gvfs.sharedCache config

  • 115: ed9fd0d = 117: cc81aec unpack-trees:virtualfilesystem: Improve efficiency of clear_ce_flags

  • 116: 4dde591 < -: ------------ homebrew: add GitHub workflow to release Cask

  • 305: abc77c4 ! 118: b11a8e1 homebrew: use Releases API digest for Cask checksum

    @@ Metadata
     Author: Matthew John Cheetham <mjcheetham@outlook.com>
     
      ## Commit message ##
    -    homebrew: use Releases API digest for Cask checksum
    +    homebrew: add GitHub workflow to release Cask
     
    -    The release-homebrew workflow previously delegated checksum computation
    -    to the third-party action `mjcheetham/asset-hash`, which downloads the
    -    release asset and hashes the bytes locally. GitHub has been observed to
    -    occasionally serve an HTML error page (the "unicorn" page) with a 200
    -    status code in place of release-asset content. When that happens the
    -    local hash succeeds against the wrong bytes and an incorrect SHA-256
    -    ends up in the Cask, which then prevents users from installing.
    +    Add a GitHub workflow that is triggered on the `release` event to
    +    automatically update the `microsoft-git` Homebrew Cask on the
    +    `microsoft/git` Tap.
     
    -    This is exactly what happened with v2.54.0.vfs.0.2, reported in
    -    microsoft/homebrew-git#102: the recorded checksum hashed the unicorn
    -    page (8e8052a0...) rather than the `.pkg` (983dd1b1...).
    +    A secret `HOMEBREW_TOKEN` with push permissions to the
    +    `microsoft/homebrew-git` repository must exist. A pull request will be
    +    created at the moment to allow for last minute manual verification.
     
    -    The GitHub Releases API itself reports the asset's SHA-256 in the
    -    `digest` field of every asset entry, computed server-side when the
    -    asset is uploaded. Read that value directly instead of recomputing
    -    locally, so a corrupted download can no longer poison the checksum.
    -
    -    The replacement step uses `gh api` (already available on
    -    ubuntu-latest) and emits collapsible log groups with the release
    -    metadata, the full asset list (name, size, digest), and the full
    -    selected-asset record. If the API ever returns something bogus
    -    again, the workflow run will contain everything needed to diagnose
    -    it without re-running the release.
    -
    -    Asset selection mirrors the previous regex (`git-(.*)\.pkg`); the
    -    step fails fast if zero or multiple assets match, or if the digest
    -    is missing, non-sha256, or not a 64-character hex string.
    -
    -    Assisted-by: Claude Opus 4.7
         Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
     
    - ## .github/workflows/release-homebrew.yml ##
    -@@ .github/workflows/release-homebrew.yml: jobs:
    -       run: |
    -         echo "result=$(echo $GITHUB_REF | sed -e "s/^refs\/tags\/v//")" >>$GITHUB_OUTPUT
    -     - id: hash
    --      name: Compute release asset hash
    --      uses: mjcheetham/asset-hash@v1.1
    --      with:
    --        asset: /git-(.*)\.pkg/
    --        hash: sha256
    --        token: ${{ secrets.GITHUB_TOKEN }}
    + ## .github/workflows/release-homebrew.yml (new) ##
    +@@
    ++name: Update Homebrew Tap
    ++on:
    ++  release:
    ++    types: [released]
    ++
    ++permissions:
    ++  id-token: write # required for Azure login via OIDC
    ++
    ++jobs:
    ++  release:
    ++    runs-on: ubuntu-latest
    ++    environment: release
    ++    steps:
    ++    - id: version
    ++      name: Compute version number
    ++      run: |
    ++        echo "result=$(echo $GITHUB_REF | sed -e "s/^refs\/tags\/v//")" >>$GITHUB_OUTPUT
    ++    - id: hash
     +      name: Look up release asset digest
     +      env:
     +        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    @@ .github/workflows/release-homebrew.yml: jobs:
     +
     +        echo "Asset SHA-256: $sha256"
     +        echo "result=$sha256" >>"$GITHUB_OUTPUT"
    -     - name: Log into Azure
    -       uses: azure/login@v3
    -       with:
    ++    - name: Log into Azure
    ++      uses: azure/login@v3
    ++      with:
    ++        client-id: ${{ secrets.AZURE_CLIENT_ID }}
    ++        tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    ++        subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    ++    - name: Retrieve token
    ++      id: token
    ++      run: |
    ++        az keyvault secret show \
    ++          --name ${{ secrets.HOMEBREW_TOKEN_SECRET_NAME }} \
    ++          --vault-name ${{ secrets.AZURE_VAULT }} \
    ++          --query "value" -o tsv >token &&
    ++        # avoid outputting the token under `set -x` by using `sed` instead of `echo`
    ++        sed s/^/::add-mask::/ <token &&
    ++        sed s/^/result=/ <token >>$GITHUB_OUTPUT &&
    ++        rm token
    ++    - name: Update scalar Cask
    ++      uses: mjcheetham/update-homebrew@v1.5.1
    ++      with:
    ++        token: ${{ steps.token.outputs.result }}
    ++        tap: microsoft/git
    ++        name: microsoft-git
    ++        type: cask
    ++        version: ${{ steps.version.outputs.result }}
    ++        sha256: ${{ steps.hash.outputs.result }}
    ++        alwaysUsePullRequest: false
  • 117: b911fc3 = 119: ecf23dd Adding winget workflows

  • 118: b16eb3e = 120: 1ab35db Disable the monitor-components workflow in msft-git

  • 119: dcf7a55 = 121: f631495 .github: enable windows builds on microsoft fork

  • 121: 29c7f02 = 122: 1c9104a .github/actions/akv-secret: add action to get secrets

  • 123: 39fdcb5 ! 123: 4428e85 release: create initial Windows installer build workflow

    @@
      ## Metadata ##
    -Author: Victoria Dye <vdye@github.com>
    +Author: Johannes Schindelin <Johannes.Schindelin@gmx.de>
     
      ## Commit message ##
         release: create initial Windows installer build workflow
     
    -    - trigger on tag matching basic "vfs" version pattern
    -    - validate tag is annotated & matches stricter checks
    -    - include `scalar`
    -    - build x86_64 & portable git installers, upload artifacts to workflow
    +    Add a manual-only GitHub Actions workflow for building the
    +    Windows installer (x86_64 plus portable Git), driven via
    +    `workflow_dispatch:`. The production release path for the
    +    official microsoft/git installers lives in
    +    .azure-pipelines/release.yml; this workflow is kept around as a
    +    fallback so the Windows installer can still be produced on
    +    demand for debugging or comparison.
     
    -    Update Apr 18, 2022: these steps are built explicitly on 'windows-2019'
    -    agents (rather than 'windows-latest') to ensure the correct version of
    -    Visual Studio is used (verified in the pipeline via 'type -p mspdb140.dll').
    -    Additionally, due to a known (but not-yet-fixed) issue downloading the
    -    'build-installers' flavor of the Git for Windows SDK with the
    -    'git-for-windows/setup-git-for-windows-sdk' Action, the SDK used is the
    -    'full' flavor.
    +    The build steps are pinned to `windows-2019` (rather than
    +    `windows-latest`) to ensure the correct Visual Studio version
    +    is used (verified in the pipeline via `type -p mspdb140.dll`),
    +    and the SDK used is the `full` flavor rather than
    +    `build-installers` due to a known (but not-yet-fixed) issue
    +    downloading the `build-installers` flavor with the
    +    `git-for-windows/setup-git-for-windows-sdk` Action.
    +
    +    There is no code-signing certificate available to this workflow,
    +    so the artifacts it produces are unsigned and must not be
    +    published as releases; they are useful only for build-time
    +    debugging.
     
         Signed-off-by: Victoria Dye <vdye@github.com>
    -    Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
    +    Assisted-by: Claude Opus 4.7
    +    Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .github/workflows/build-git-installers.yml (new) ##
     @@
     +name: build-git-installers
     +
    ++# This workflow used to run automatically on every `v*vfs*` tag push to
    ++# build and publish the official microsoft/git installers. The signed
    ++# release builds have moved to the Azure Pipeline in
    ++# `.azure-pipelines/release.yml`, which is what now runs on tag push.
    ++#
    ++# We no longer have access to a permissible code signing certificate
    ++# from this workflow, so the artifacts it produces would not be properly
    ++# code-signed and must not be published as releases. Keep the workflow
    ++# around behind a `workflow_dispatch:` trigger only, so it can still be
    ++# invoked manually for debugging or comparison purposes.
     +on:
    -+  push:
    -+    tags:
    -+      - 'v[0-9]*vfs*' # matches "v<number><any characters>vfs<any characters>"
    ++  workflow_dispatch:
    ++
    ++env:
    ++  DO_WIN_CODESIGN: ${{ secrets.WIN_CODESIGN_CERT_SECRET_NAME != '' && secrets.WIN_CODESIGN_PASS_SECRET_NAME != '' }}
    ++  DO_WIN_GPGSIGN: ${{ secrets.WIN_GPG_KEYGRIP_SECRET_NAME != '' && secrets.WIN_GPG_PRIVATE_SECRET_NAME != '' && secrets.WIN_GPG_PASSPHRASE_SECRET_NAME != '' }}
     +
     +jobs:
     +  # Check prerequisites for the workflow
    @@ .github/workflows/build-git-installers.yml (new)
     +          echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
     +        id: tag
     +      - name: Clone git
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +      - name: Validate the tag identified with trigger
     +        run: |
     +          die () {
    @@ .github/workflows/build-git-installers.yml (new)
     +          git config --global user.name "$USER_NAME" &&
     +          git config --global user.email "$USER_EMAIL" &&
     +          echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >>$GITHUB_ENV
    -+      - uses: git-for-windows/setup-git-for-windows-sdk@v1
    ++      - uses: git-for-windows/setup-git-for-windows-sdk@v2
     +        with:
     +          flavor: build-installers
     +          architecture: ${{ matrix.arch.name }}
    @@ .github/workflows/build-git-installers.yml (new)
     +          git remote add -f origin https://github.com/git-for-windows/git &&
     +          git fetch "https://github.com/${{github.repository}}" refs/tags/${tag_name}:refs/tags/${tag_name} &&
     +          git reset --hard ${tag_name}
    ++      - name: Log in to Azure
    ++        uses: azure/login@v3
    ++        with:
    ++          client-id: ${{ secrets.AZURE_CLIENT_ID }}
    ++          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    ++          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    ++      - name: Download code signing secrets
    ++        id: codesign-secrets
    ++        if: env.DO_WIN_CODESIGN == 'true'
    ++        uses: ./.github/actions/akv-secret
    ++        with:
    ++          vault: ${{ secrets.AZURE_VAULT }}
    ++          secrets: |
    ++            ${{ secrets.WIN_CODESIGN_CERT_SECRET_NAME }} base64> home/.sig/codesign.p12
    ++            ${{ secrets.WIN_CODESIGN_PASS_SECRET_NAME }}       > home/.sig/codesign.pass
     +      - name: Prepare home directory for code-signing
    -+        env:
    -+          CODESIGN_P12: ${{secrets.CODESIGN_P12}}
    -+          CODESIGN_PASS: ${{secrets.CODESIGN_PASS}}
    -+        if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != ''
    ++        if: ${{ steps.codesign-secrets.outcome == 'success' }}
     +        shell: bash
     +        run: |
    -+          cd home &&
    -+          mkdir -p .sig &&
    -+          echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >.sig/codesign.p12 &&
    -+          echo -n "$CODESIGN_PASS" >.sig/codesign.pass
     +          git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"'
    ++      - name: Download GPG secrets
    ++        id: gpg-secrets
    ++        if: env.DO_WIN_GPGSIGN == 'true'
    ++        uses: ./.github/actions/akv-secret
    ++        with:
    ++          vault: ${{ secrets.AZURE_VAULT }}
    ++          secrets: |
    ++            ${{ secrets.WIN_GPG_KEYGRIP_SECRET_NAME }}       > $output:keygrip
    ++            ${{ secrets.WIN_GPG_PRIVATE_SECRET_NAME }} base64> $output:private-key
    ++            ${{ secrets.WIN_GPG_PASSPHRASE_SECRET_NAME }}    > $output:passphrase
     +      - name: Prepare home directory for GPG signing
    -+        if: env.GPGKEY != ''
    ++        if: ${{ steps.gpg-secrets.outputs.keygrip != '' && steps.gpg-secrets.outputs.private-key != '' }}
     +        shell: bash
     +        run: |
     +          # This section ensures that the identity for the GPG key matches the git user identity, otherwise
     +          # signing will fail
     +
    -+          echo '${{secrets.PRIVGPGKEY}}' | tr % '\n' | gpg $GPG_OPTIONS --import &&
    -+          info="$(gpg --list-keys --with-colons "${GPGKEY%% *}" | cut -d : -f 1,10 | sed -n '/^uid/{s|uid:||p;q}')" &&
    ++          # Import the GPG private key
    ++          echo -n '${{ steps.gpg-secrets.outputs.private-key }}' | gpg $GPG_OPTIONS --import &&
    ++
    ++          info="$(gpg --list-keys --with-colons '${{ steps.gpg-secrets.outputs.keygrip }}' | cut -d : -f 1,10 | sed -n '/^uid/{s|uid:||p;q}')" &&
     +          git config --global user.name "${info% <*}" &&
     +          git config --global user.email "<${info#*<}"
    -+        env:
    -+          GPGKEY: ${{secrets.GPGKEY}}
     +      - name: Build mingw-w64-${{matrix.arch.toolchain}}-git
    -+        env:
    -+          GPGKEY: "${{secrets.GPGKEY}}"
     +        shell: bash
     +        run: |
     +          set -x
     +
    ++          # Build the GPGKEY variable
    ++          export GPGKEY="${{ steps.gpg-secrets.outputs.keygrip }} --passphrase '${{ steps.gpg-secrets.outputs.passphrase }}' --yes --batch --no-tty --pinentry-mode loopback --digest-algo SHA256" &&
    ++
     +          # Make sure that there is a `/usr/bin/git` that can be used by `makepkg-mingw`
     +          printf '#!/bin/sh\n\nexec /${{matrix.arch.mingwprefix}}/bin/git.exe "$@"\n' >/usr/bin/git &&
     +
     +          sh -x /usr/src/build-extra/please.sh build-mingw-w64-git --only-${{matrix.arch.name}} --build-src-pkg -o artifacts HEAD &&
    -+          if test -n "$GPGKEY"
    ++          if test -n "${{ steps.gpg-secrets.outputs.keygrip }}"
     +          then
     +            for tar in artifacts/*.tar*
     +            do
    @@ .github/workflows/build-git-installers.yml (new)
     +          git commit -s -m "mingw-w64-git: new version ($version)" PKGBUILD &&
     +          git bundle create "$b"/MINGW-packages.bundle origin/main..main)
     +      - name: Publish mingw-w64-${{matrix.arch.toolchain}}-git
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: "${{ matrix.arch.artifact }}"
     +          path: artifacts
    @@ .github/workflows/build-git-installers.yml (new)
     +    runs-on: ${{ matrix.arch.runner }}
     +    steps:
     +      - name: Download ${{ matrix.arch.artifact }}
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: ${{ matrix.arch.artifact }}
     +          path: ${{ matrix.arch.artifact }}
    -+      - uses: git-for-windows/setup-git-for-windows-sdk@v1
    ++      - uses: git-for-windows/setup-git-for-windows-sdk@v2
     +        with:
     +          flavor: build-installers
     +          architecture: ${{ matrix.arch.name }}
    @@ .github/workflows/build-git-installers.yml (new)
     +        shell: bash
     +        run: |
     +          git clone --filter=blob:none --single-branch -b main https://github.com/git-for-windows/build-extra /usr/src/build-extra
    ++      - name: Log in to Azure
    ++        uses: azure/login@v3
    ++        if: env.DO_WIN_CODESIGN == 'true'
    ++        with:
    ++          client-id: ${{ secrets.AZURE_CLIENT_ID }}
    ++          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    ++          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    ++      - name: Check out repository (for akv-secret Action)
    ++        if: env.DO_WIN_CODESIGN == 'true'
    ++        uses: actions/checkout@v6
    ++        with:
    ++          path: git
    ++      - name: Download code signing secrets
    ++        id: codesign-secrets
    ++        if: env.DO_WIN_CODESIGN == 'true'
    ++        uses: ./git/.github/actions/akv-secret
    ++        with:
    ++          vault: ${{ secrets.AZURE_VAULT }}
    ++          secrets: |
    ++            ${{ secrets.WIN_CODESIGN_CERT_SECRET_NAME }} base64> home/.sig/codesign.p12
    ++            ${{ secrets.WIN_CODESIGN_PASS_SECRET_NAME }}       > home/.sig/codesign.pass
     +      - name: Prepare home directory for code-signing
    -+        env:
    -+          CODESIGN_P12: ${{secrets.CODESIGN_P12}}
    -+          CODESIGN_PASS: ${{secrets.CODESIGN_PASS}}
    -+        if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != ''
    ++        if: ${{ steps.codesign-secrets.outcome == 'success' }}
     +        shell: bash
     +        run: |
    -+          mkdir -p home/.sig &&
    -+          echo -n "$CODESIGN_P12" | tr % '\n' | base64 -d >home/.sig/codesign.p12 &&
    -+          echo -n "$CODESIGN_PASS" >home/.sig/codesign.pass &&
     +          git config --global alias.signtool '!sh "/usr/src/build-extra/signtool.sh"'
     +      - name: Retarget auto-update to microsoft/git
     +        shell: bash
    @@ .github/workflows/build-git-installers.yml (new)
     +
     +          b=/usr/src/build-extra &&
     +
    -+          sed -i -e '/^ *InstallAutoUpdater();$/a\
    -+              CustomPostInstall();' \
    -+            -e '/^ *UninstallAutoUpdater();$/a\
    -+              CustomPostUninstall();' \
    -+            $b/installer/install.iss &&
    ++          sed -i "# First, find the autoupdater parts in the install/uninstall steps
    ++            /if IsComponentInstalled('autoupdate')/{
    ++              # slurp in the next two lines, where the call to InstallAutoUpdater()/UninstallAutoUpdater() happens
    ++              N
    ++              N
    ++              # insert the corresponding CustomPostInstall()/CustomPostUninstall() call before that block
    ++              s/^\\([ \t]*\\)\(.*\\)\\(Install\\|Uninstall\\)\\(AutoUpdater\\)/\\1CustomPost\\3();\\n\\1\\2\\3\\4/
    ++            }" $b/installer/install.iss &&
    ++          grep CustomPostInstall $b/installer/install.iss &&
    ++          grep CustomPostUninstall $b/installer/install.iss &&
     +
     +          cat >>$b/installer/helpers.inc.iss <<\EOF
     +
    @@ .github/workflows/build-git-installers.yml (new)
     +          fi &&
     +          openssl dgst -sha256 artifacts/${{matrix.type.fileprefix}}-*.exe | sed "s/.* //" >artifacts/sha-256.txt
     +      - name: Verify that .exe files are code-signed
    -+        if: env.CODESIGN_P12 != '' && env.CODESIGN_PASS != ''
    -+        shell: bash
    ++        if: env.DO_WIN_CODESIGN == 'true'
    ++        shell: pwsh
     +        run: |
    -+          PATH=$PATH:"/c/Program Files (x86)/Windows Kits/10/App Certification Kit/" \
    -+          signtool verify //pa artifacts/${{matrix.type.fileprefix}}-*.exe
    ++          $ret = 0
    ++          $files = Get-ChildItem -Path artifacts -Filter "${{matrix.type.fileprefix}}-*.exe"
    ++          foreach ($file in $files) {
    ++              $signature = Get-AuthenticodeSignature -FilePath $file.FullName
    ++              if ($signature.Status -eq 'Valid') {
    ++                  Write-Host "[ VALID ] $($file.FullName)"
    ++              } else {
    ++                  Write-Host "[INVALID] $($file.FullName)"
    ++                  Write-Host "  Message: $($signature.StatusMessage)"
    ++                  $ret = 1
    ++              }
    ++          }
    ++          exit $ret
     +      - name: Publish ${{matrix.type.name}}-${{matrix.arch.name}}
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: win-${{matrix.type.name}}-${{matrix.arch.name}}
     +          path: artifacts
  • 127: 66ce629 = 124: 33a635b help: special-case HOST_CPU universal

  • 129: 3ba4ddb ! 125: 4003842 release: add Mac OSX installer build

    @@ .github/workflows/build-git-installers.yml: jobs:
     +    environment: release
     +    steps:
     +      - name: Check out repository
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          path: git
     +
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          mv git/.github/macos-installer/disk-image/*.pkg git/.github/macos-installer/
     +
     +      - name: Upload artifacts
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: macos-artifacts
     +          path: |
  • 124: 8626621 = 126: c71dbb5 t5799: explicitly test gvfs-helper --fallback and --no-fallback

  • 125: 453f8c5 < -: ------------ release: create initial Windows installer build workflow

  • 131: 2c1973d ! 127: 9ea3858 release: build unsigned Ubuntu .deb package

    @@ .github/workflows/build-git-installers.yml: jobs:
     +          tar -C /__e/node20 -x --strip-components=1 -f /tmp/node.tar.gz
     +
     +      - name: Clone git
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          path: git
     +
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          mv "$PKGNAME.deb" "$GITHUB_WORKSPACE"
     +
     +      - name: Upload artifacts
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: linux-artifacts
     +          path: |
  • 126: 91fbb99 = 128: 85e3dda gvfs-helper: don't fallback with new config

  • 132: a63e353 ! 129: 121efe4 release: add signing step for .deb package

    @@ Commit message
         Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .github/workflows/build-git-installers.yml ##
    -@@ .github/workflows/build-git-installers.yml: on:
    -     tags:
    -       - 'v[0-9]*vfs*' # matches "v<number><any characters>vfs<any characters>"
    +@@ .github/workflows/build-git-installers.yml: name: build-git-installers
    + on:
    +   workflow_dispatch:
      
     +permissions:
     +  id-token: write # required for Azure login via OIDC
    @@ .github/workflows/build-git-installers.yml: jobs:
          strategy:
     @@ .github/workflows/build-git-installers.yml: jobs:
            - name: Upload artifacts
    -         uses: actions/upload-artifact@v6
    +         uses: actions/upload-artifact@v7
              with:
     -          name: linux-artifacts
     +          name: linux-unsigned-${{ matrix.arch.name }}
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
     +
     +      - name: Check out repository (for akv-secret Action)
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          path: git
     +
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          /usr/lib/gnupg2/gpg-preset-passphrase --preset '${{ steps.gpg-secrets.outputs.keygrip }}' <<<'${{ steps.gpg-secrets.outputs.passphrase }}'
     +
     +      - name: Download artifacts
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: linux-unsigned-${{ matrix.arch }}
     +
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          debsigs --sign=origin --verify --check microsoft-git_"$version"_${{ matrix.arch }}.deb
     +
     +      - name: Upload artifacts
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: linux-${{ matrix.arch }}
                path: |
  • 128: 51ffffe = 130: df4e01c test-gvfs-protocol: add cache_http_503 to mayhem

  • 133: bad6926 ! 131: 2eb0fc1 release: create draft GitHub release with packages & installers

    @@ .github/workflows/build-git-installers.yml: jobs:
     +        needs.windows_artifacts.result == 'success')
     +    steps:
     +      - name: Download Windows portable (x86_64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: win-portable-x86_64
     +          path: win-portable-x86_64
     +
     +      - name: Download Windows portable (aarch64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: win-portable-aarch64
     +          path: win-portable-aarch64
     +
     +      - name: Download Windows installer (x86_64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: win-installer-x86_64
     +          path: win-installer-x86_64
     +
     +      - name: Download Windows installer (aarch64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: win-installer-aarch64
     +          path: win-installer-aarch64
     +
     +      - name: Download macOS artifacts
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: macos-artifacts
     +          path: macos-artifacts
     +
     +      - name: Download Debian package (amd64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: linux-amd64
     +          path: deb-package
     +
     +      - name: Download Debian package (arm64)
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: linux-arm64
     +          path: deb-package
     +
    -+      - uses: actions/github-script@v6
    ++      - uses: actions/github-script@v9
     +        with:
     +          script: |
     +            const fs = require('fs');
  • 130: ffed10f = 132: ad9c258 t5799: add unit tests for new gvfs.fallback config setting

  • 135: 4f9ca28 = 133: 5f43759 update-microsoft-git: create barebones builtin

  • 137: 0656543 = 134: 60a36e0 update-microsoft-git: Windows implementation

  • 134: 6b1d000 ! 135: e77f72b build-git-installers: publish gpg public key

    @@ .github/workflows/build-git-installers.yml: jobs:
     +          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
     +
     +      - name: Check out repository (for akv-secret Action)
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          path: git
     +
    @@ .github/workflows/build-git-installers.yml: jobs:
     +          secrets: |
     +            ${{ secrets.LINUX_GPG_PUBLIC_SECRET_NAME }} base64> deb-package/msft-git-public.asc
     +
    -       - uses: actions/github-script@v6
    +       - uses: actions/github-script@v9
              with:
                script: |
  • 139: 6fffe5a = 136: e84b19d update-microsoft-git: use brew on macOS

  • 136: 9314704 = 137: edae852 release: continue pestering until user upgrades

  • 144: 7cbeb9c = 138: 3bc1b1a git_config_set_multivar_in_file_gently(): add a lock timeout

  • 141: d49fbb9 = 139: b58fe1a .github: reinstate ISSUE_TEMPLATE.md for microsoft/git

  • 138: fb2ee4c = 140: 6a932d7 dist: archive HEAD instead of HEAD^{tree}

  • 146: ef23cac = 141: b9adeee scalar: set the config write-lock timeout to 150ms

  • 143: 3fafd11 = 142: f0a72d3 .github: update PULL_REQUEST_TEMPLATE.md

  • 140: e107a79 = 143: ec3b2b8 release: include GIT_BUILT_FROM_COMMIT in MacOS build

  • 147: 3356b30 = 144: f9bc0cf scalar: add docs from microsoft/scalar

  • 145: 268e680 = 145: 9b8f89b Adjust README.md for microsoft/git

  • 142: 8eba308 ! 146: 08c324f release: add installer validation

    @@ .github/workflows/build-git-installers.yml: jobs:
     +    needs: [prereqs, windows_artifacts, create-macos-artifacts, create-linux-artifacts]
     +    steps:
     +      - name: Download artifacts
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: ${{ matrix.component.artifact }}
     +
  • 148: 0cedb2b = 147: 6c80e7d scalar (Windows): use forward slashes as directory separators

  • 149: 4d5f852 = 148: fac07dc scalar: add retry logic to run_git()

  • 150: 3804a81 = 149: 6143dc1 scalar: support the config command for backwards compatibility

  • 151: c23a0f1 = 150: 9b691b9 scalar: implement a minimal JSON parser

  • 152: 657cebb = 151: 42593de scalar clone: support GVFS-enabled remote repositories

  • 153: 58da6b9 = 152: d928194 test-gvfs-protocol: also serve smart protocol

  • 154: cd9da9c = 153: c7b53b1 gvfs-helper: add the endpoint command

  • 155: d7c9693 = 154: 85e7343 dir_inside_of(): handle directory separators correctly

  • 156: 7937a58 = 155: e964e30 scalar: disable authentication in unattended mode

  • 157: 4b26b6f = 156: 3aefdce abspath: make strip_last_path_component() global

  • 158: 35d09df ! 157: 488d413 scalar: do initialize gvfs.sharedCache

    @@ scalar.c: static int cmd_clone(int argc, const char **argv)
      		strbuf_addf(&buf, "init.defaultBranch=%s", branch);
     @@ scalar.c: static int cmd_clone(int argc, const char **argv)
      
    - 	setup_git_directory();
    + 	setup_git_directory(the_repository);
      
     +	repo_config(the_repository, git_default_config, NULL);
     +
  • 159: 290228b = 158: 9e2509b scalar diagnose: include shared cache info

  • 160: 62f99c5 = 159: ed82fd3 scalar: only try GVFS protocol on https:// URLs

  • 161: 4b56f3f = 160: 5b47076 scalar: verify that we can use a GVFS-enabled repository

  • 162: baf31be = 161: de5851e scalar: add the cache-server command

  • 163: 6e44511 = 162: 50a5992 scalar: add a test toggle to skip accessing the vsts/info endpoint

  • 164: 8b485bf = 163: db1e9da scalar: adjust documentation to the microsoft/git fork

  • 165: 975b79e = 164: e6e1b6b scalar: enable untracked cache unconditionally

  • 166: 82508f4 = 165: 40fee12 scalar: parse clone --no-fetch-commits-and-trees for backwards compatibility

  • 167: e56b327 = 166: 67f05a0 scalar: make GVFS Protocol a forced choice

  • 168: 9878e92 = 167: 7995e59 scalar: work around GVFS Protocol HTTP/2 failures

  • 169: 94a86dc = 168: 216c181 gvfs-helper-client: clean up server process(es)

  • 170: 3fabf95 = 169: 7717e76 scalar diagnose: accommodate Scalar's Functional Tests

  • 173: e70ed15 = 170: 853755f add/rm: allow adding sparse entries when virtual

  • 171: 166ed4d ! 171: cef60b9 ci: run Scalar's Functional Tests

    @@ .github/workflows/scalar-functional-tests.yml (new)
     +
     +    steps:
     +      - name: Check out Git's source code
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +
     +      - name: Setup build tools on Windows
     +        if: runner.os == 'Windows'
    -+        uses: git-for-windows/setup-git-for-windows-sdk@v1
    ++        uses: git-for-windows/setup-git-for-windows-sdk@v2
     +
     +      - name: Provide a minimal `install` on Windows
     +        if: runner.os == 'Windows'
    @@ .github/workflows/scalar-functional-tests.yml (new)
     +          case "$(scalar version 2>&1)" in *.vfs.*) echo Good;; *) exit 1;; esac
     +
     +      - name: Check out Scalar's source code
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          fetch-depth: 0 # Indicate full history so Nerdbank.GitVersioning works.
     +          path: scalar
    @@ .github/workflows/scalar-functional-tests.yml (new)
     +          mv $props.new $props
     +
     +      - name: Setup .NET
    -+        uses: actions/setup-dotnet@v4
    ++        uses: actions/setup-dotnet@v5
     +        with:
     +          dotnet-version: '9.0.306'
     +
    @@ .github/workflows/scalar-functional-tests.yml (new)
     +
     +      - name: Archive Trace2 Logs
     +        if: ( success() || failure() ) && ( steps.trace2_zip_unix.conclusion == 'success' || steps.trace2_zip_windows.conclusion == 'success' )
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: ${{ env.TRACE2_BASENAME }}.zip
     +          path: scalar/${{ env.TRACE2_BASENAME }}.zip
  • 174: 0176b10 = 172: 656fb01 sparse-checkout: add config to disable deleting dirs

  • 172: 3a0f593 = 173: 92f822d scalar: upgrade to newest FSMonitor config setting

  • 175: 16d9542 = 174: 81ce624 diff: ignore sparse paths in diffstat

  • 176: 34b71f5 = 175: d926318 repo-settings: enable sparse index by default

  • 177: b56ca58 = 176: df922a6 TO-UPSTREAM: sequencer: avoid progress when stderr is redirected

  • 178: 87b4a62 = 177: c8da0d2 TO-CHECK: t1092: use quiet mode for rebase tests

  • 179: 51a3f58 = 178: 89b9ad0 reset: fix mixed reset when using virtual filesystem

  • 180: 0d4c9a9 = 179: ed99b68 diff(sparse-index): verify with partially-sparse

  • 181: 1e1c953 = 180: 341fc32 stash: expand testing for git stash -u

  • 182: ba15888 = 181: bad405f sparse-index: add ensure_full_index_with_reason()

  • 183: 28b8b1f ! 182: 27ba56c treewide: add reasons for expanding index

    @@ builtin/ls-files.c: static void show_files(struct repository *repo, struct dir_s
     
      ## builtin/read-tree.c ##
     @@ builtin/read-tree.c: int cmd_read_tree(int argc,
    - 		setup_work_tree();
    + 		setup_work_tree(the_repository);
      
      	if (opts.skip_sparse_checkout)
     -		ensure_full_index(the_repository->index);
    @@ sparse-index.c: void clear_skip_worktree_from_present_files(struct index_state *
      }
     
      ## t/t1092-sparse-checkout-compatibility.sh ##
    -@@ t/t1092-sparse-checkout-compatibility.sh: test_expect_success 'sparse-index is not expanded: merge-ours' '
    - 	ensure_not_expanded merge -s ours merge-right
    +@@ t/t1092-sparse-checkout-compatibility.sh: test_expect_success 'sparse-index is not expanded: restore --source --staged' '
    + 	ensure_not_expanded restore --source update-folder1 --staged .
      '
      
     +test_expect_success 'ensure_full_index_with_reason' '
  • 184: 0cba1c3 = 183: 18d2c13 treewide: custom reasons for expanding index

  • 185: 095f791 = 184: 4c511cd sparse-index: add macro for unaudited expansions

  • 186: f234ddc = 185: 836b74d Docs: update sparse index plan with logging

  • 187: 3df78f8 = 186: c877295 sparse-index: log failure to clear skip-worktree

  • 188: a310bcc = 187: c0684f1 stash: use -f in checkout-index child process

  • 189: ed49c0e = 188: e490d06 sparse-index: do not copy hashtables during expansion

  • 190: 3169ba3 = 189: 1b52838 TO-UPSTREAM: sub-process: avoid leaking cmd

  • 191: 2a0d73e = 190: 76073be remote-curl: release filter options before re-setting them

  • 192: 20aaf05 = 191: 96bb6e2 transport: release object filter options

  • 193: e129f43 ! 192: 1b5d08d push: don't reuse deltas with path walk

    @@ builtin/push.c: int cmd_push(int argc,
     +	if (the_repository->settings.pack_use_path_walk)
     +		flags |= TRANSPORT_PUSH_NO_REUSE_DELTA;
     +
    - 	if (tags)
    - 		refspec_append(&rs, "refs/tags/*");
    + 	if (argc > 0)
    + 		repo = argv[0];
      
     
      ## send-pack.c ##
    @@ transport.c: static int git_transport_push(struct transport *transport, struct r
     +	args.no_reuse_delta = !!(flags & TRANSPORT_PUSH_NO_REUSE_DELTA);
      	args.push_options = transport->push_options;
      	args.url = transport->url;
    - 
    + 	args.negotiation_include = &transport->remote->negotiation_include;
     
      ## transport.h ##
     @@ transport.h: struct transport {
  • 194: a22c0b6 = 193: e9fc894 t7900-maintenance.sh: reset config between tests

  • 195: a3d92a3 = 194: e2b5f45 maintenance: add cache-local-objects maintenance task

  • 196: 27b19a5 = 195: 00f30c9 scalar.c: add cache-local-objects task

  • 197: e2d5332 ! 196: fd725c3 hooks: add custom post-command hook config

    @@ hook.c: void hook_free(void *p, const char *str UNUSED)
     +{
     +	struct strbuf path = STRBUF_INIT;
     +	const char *sid = tr2_sid_get();
    -+	char *slash = strchr(sid, '/');
    -+
    -+	/*
    -+	 * Name is based on top-level SID, so children can indicate that
    -+	 * the top-level process should run the post-command hook.
    -+	 */
    -+	if (slash)
    -+		*slash = 0;
    ++	const char *slash = strchrnul(sid, '/');
     +
     +	/*
     +	 * Do not write to hooks directory, as it could be redirected
     +	 * somewhere like the source tree.
     +	 */
    -+	repo_git_path_replace(r, &path, "info/index-change-%s.snt", sid);
    ++	repo_git_path_replace(r, &path, "info/index-change-%.*s.snt",
    ++			      (int)(slash - sid), sid);
     +
     +	return strbuf_detach(&path, NULL);
     +}
  • 198: 67995b0 ! 197: 9200e24 TO-UPSTREAM: Docs: fix asciidoc failures from short delimiters

    @@ Documentation/config/sendemail.adoc: sendmail;;
      sendemail.bcc::
      sendemail.cc::
     
    - ## Documentation/config/sideband.adoc ##
    -@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::
    - 	unwanted ANSI escape sequences from being sent to the terminal. Use
    - 	this config setting to override this behavior:
    - +
    ----
    -+----
    - 	color::
    - 		Allow ANSI color sequences, line feeds and horizontal tabs,
    - 		but mask all other control characters. This is the default.
    -@@ Documentation/config/sideband.adoc: sideband.allowControlCharacters::
    - 		horizontal tabs.
    - 	true::
    - 		Allow all control characters to be sent to the terminal.
    ----
    -+----
    -
      ## Documentation/config/ssh.adoc ##
     @@ Documentation/config/ssh.adoc: overridden via the environment variable `GIT_SSH_VARIANT`.
      The current command-line parameters used for each variant are as
  • 199: 1ef68d0 = 198: a4656ce hooks: make hook logic memory-leak free

  • 201: f6bf764 = 199: e061100 t0401: test post-command for alias, version, typo

  • 203: 885c9c6 = 200: eec6e46 hooks: better handle config without gitdir

  • 200: c18f47b ! 201: cef18fa cat_one_file(): make it easy to see that the size variable is initialized

    @@ builtin/cat-file.c: static int cat_one_file(int opt, const char *exp_type, const
      	struct object_id oid;
      	enum object_type type;
      	char *buf;
    --	unsigned long size;
    -+	unsigned long size = 0;
    +-	size_t size;
    ++	size_t size = 0;
      	struct object_context obj_context = {0};
      	struct object_info oi = OBJECT_INFO_INIT;
      	unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
  • 204: 09c8246 = 202: 7442ba8 revision: defensive programming

  • 202: b7b0458 = 203: 5665bcc fsck: avoid using an uninitialized variable

  • 205: 18eaf1a = 204: 756645e get_parent(): defensive programming

  • 218: f38cce3 = 205: fa69a03 load_revindex_from_disk(): avoid accessing uninitialized data

  • 206: 2e3161b = 206: 8a5f81c fetch-pack: defensive programming

  • 222: 6184a4d = 207: 3513e5b load_pack_mtimes_file(): avoid accessing uninitialized data

  • 209: 6ec7dc4 ! 208: bdd7bde codeql: run static analysis as part of CI builds

    @@ .github/workflows/codeql.yml (new)
     +
     +    steps:
     +      - name: Checkout repository
    -+        uses: actions/checkout@v3
    ++        uses: actions/checkout@v6
     +
     +      - name: Install dependencies
     +        run: ci/install-dependencies.sh
  • 211: e149087 ! 209: ff1beac codeql: publish the sarif file as build artifact

    @@ .github/workflows/codeql.yml: jobs:
     +        run: ls -la sarif-results
     +
     +      - name: publish sarif for debugging
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: sarif-results
     +          path: sarif-results
  • 207: b7cd525 = 210: 5028ac4 unparse_commit(): defensive programming

  • 214: e277e03 ! 211: 5771b17 codeql: disable a couple of non-critical queries for now

    @@ .github/codeql/codeql-config.yml (new)
     
      ## .github/workflows/codeql.yml ##
     @@ .github/workflows/codeql.yml: jobs:
    - 
    -     steps:
    -       - name: Checkout repository
    --        uses: actions/checkout@v3
    -+        uses: actions/checkout@v4
    - 
    -       - name: Install dependencies
    -         run: ci/install-dependencies.sh
    -@@ .github/workflows/codeql.yml: jobs:
              uses: github/codeql-action/init@v4
              with:
                languages: ${{ matrix.language }}
  • 208: 5b2b925 = 212: 81b8b5d verify_commit_graph(): defensive programming

  • 217: ef097fa = 213: 72fc726 date: help CodeQL understand that there are no leap-year issues here

  • 210: c559617 = 214: f8806f3 stash: defensive programming

  • 221: c32c244 = 215: e7531c7 help: help CodeQL understand that consuming envvars is okay here

  • 212: c43326c = 216: a068f57 stash: defensive programming

  • 213: 3d1585f < -: ------------ fetch: silence a CodeQL alert about a local variable's address' use after release

  • 225: 29e8ee6 = 217: a377224 ctype: help CodeQL understand that sane_istest() does not access array past end

  • 215: 4634cc6 = 218: f0ef8e7 push: defensive programming

  • 216: 8eac178 = 219: b57fa4c test-tool repository: check return value of lookup_commit()

  • 226: ade505c = 220: bd2a9ee ctype: accommodate for CodeQL misinterpreting the z in mallocz()

  • 219: d0b514e = 221: 9c9cb17 fetch: defensive programming

  • 220: 61484c8 = 222: 94a832a shallow: handle missing shallow commits gracefully

  • 227: ced0669 = 223: 356f748 strbuf_read: help with CodeQL misunderstanding that strbuf_read() does NUL-terminate correctly

  • 223: e98e25b = 224: f7294be inherit_tracking(): defensive programming

  • 224: 02dd28f = 225: 4d3fc58 commit-graph: suppress warning about using a stale stack addresses

  • 228: 6ea08af ! 226: d3ca7f7 codeql: also check JavaScript code

    @@ .github/workflows/codeql.yml: jobs:
            - name: Checkout repository
     @@ .github/workflows/codeql.yml: jobs:
            - name: publish sarif for debugging
    -         uses: actions/upload-artifact@v6
    +         uses: actions/upload-artifact@v7
              with:
     -          name: sarif-results
     +          name: sarif-results-${{ matrix.language }}
  • 229: 079a4fd = 227: 13a254e scalar: add run_git_argv

  • 230: 0be8fee = 228: e4449a3 scalar: add --ref-format option to scalar clone

  • 231: 9efd894 = 229: e46a87e gvfs-helper: skip collision check for loose objects

  • 232: 4fd0406 = 230: 572244c gvfs-helper: emit advice on transient errors

  • 233: b73eb04 = 231: 43c901d gvfs-helper: avoid collision check for packfiles

  • 234: 1fae09d = 232: c5642c8 t5799: update cache-server methods for multiple instances

  • 235: 99f5a12 = 233: bf4c4f4 gvfs-helper: override cache server for prefetch

  • 236: 56827ed = 234: 664df1b gvfs-helper: override cache server for get

  • 237: c55b37c = 235: 662dc1a gvfs-helper: override cache server for post

  • 238: 6f88546 = 236: d2c7cb5 t5799: add test for all verb-specific cache-servers together

  • 239: 366d8b7 = 237: ce45a38 lib-gvfs-helper: create helper script for protocol tests

  • 240: f959aa2 = 238: 311168e t579*: split t5799 into several parts

  • 241: cc2c471 = 239: 72573c7 scalar: add ---cache-server-url options

  • 242: 81888ba = 240: 89ad814 Restore previous errno after post command hook

  • 243: df551f8 = 241: f09077f t9210: differentiate origin and cache servers

  • 245: 4866686 = 242: f0e6c13 unpack-trees: skip lstats for deleted VFS entries in checkout

  • 246: f4a85c5 = 243: bfc2145 worktree: conditionally allow worktree on VFS-enabled repos

  • 244: 79c62b2 = 244: 885b65f gvfs-helper: send X-Session-Id headers

  • 247: 7db55f9 = 245: 7465381 gvfs-helper: create shared object cache if missing

  • 248: caf4f51 = 246: 177782f gvfs: add gvfs.sessionKey config

  • 249: 2a24af5 = 247: 63c62f4 gvfs: clear DIE_IF_CORRUPT in streaming incore fallback

  • 250: 1f90f17 ! 248: a22ad73 workflow: add release-vfsforgit to automate VFS for Git updates

    @@ .github/workflows/release-vfsforgit.yml (new)
     +          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
     +
     +      - name: Checkout (for akv-secret action)
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +        with:
     +          sparse-checkout: .github/actions
     +
  • 251: a485bab = 249: 8eb2b3a worktree remove: use GVFS_SUPPORTS_WORKTREES for skip-clean-check gate

  • 252: b124df1 ! 250: 56eaa37 ci: add new VFS for Git functional tests workflow

    @@ .github/workflows/vfs-functional-tests.yml (new)
     +
     +    steps:
     +      - name: Check out Git's source code
    -+        uses: actions/checkout@v4
    ++        uses: actions/checkout@v6
     +
     +      - name: Setup build tools
    -+        uses: git-for-windows/setup-git-for-windows-sdk@v1
    ++        uses: git-for-windows/setup-git-for-windows-sdk@v2
     +        with:
     +          architecture: ${{ matrix.architecture }}
     +
    @@ .github/workflows/vfs-functional-tests.yml (new)
     +          make -j5 DESTDIR="$GITHUB_WORKSPACE/MicrosoftGit/payload/${{ matrix.architecture }}" install
     +
     +      - name: Upload Git artifact
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: MicrosoftGit-${{ matrix.architecture }}
     +          path: MicrosoftGit
    @@ .github/workflows/vfs-functional-tests.yml (new)
     +
     +    steps:
     +      - name: Download x86_64 build
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: MicrosoftGit-x86_64
     +          path: MicrosoftGit
     +
     +      - name: Download aarch64 build
    -+        uses: actions/download-artifact@v4
    ++        uses: actions/download-artifact@v8
     +        with:
     +          name: MicrosoftGit-aarch64
     +          path: MicrosoftGit
    @@ .github/workflows/vfs-functional-tests.yml (new)
     +          BATCH
     +
     +      - name: Upload Git artifact
    -+        uses: actions/upload-artifact@v6
    ++        uses: actions/upload-artifact@v7
     +        with:
     +          name: MicrosoftGit
     +          path: MicrosoftGit
  • 253: 3282678 ! 251: 20399e3 azure-pipelines: add stub release pipeline for Azure

    @@ Metadata
      ## Commit message ##
         azure-pipelines: add stub release pipeline for Azure
     
    -    Add a stub pipeline for releases using Azure Pipelines.
    +    Add a release-pipeline scaffold for microsoft/git on Azure
    +    Pipelines, structured around a prereqs stage, a per-platform
    +    build stage with placeholder jobs, and a release stage that
    +    downloads the build artifacts and publishes them to a draft
    +    GitHub release. The per-platform build, signing, and validation
    +    logic lands in subsequent commits.
     
    -    The pipeline runs on Microsoft internal images/runners across:
    -     * Windows x64
    -     * Windows ARM64
    -     * macOS
    -     * Ubuntu x64
    -     * Ubuntu ARM64
    +    The pipeline targets Microsoft-internal 1ES-hosted images
    +    across Windows x64, Windows ARM64, macOS, Ubuntu x64, and
    +    Ubuntu ARM64. Windows and Linux matrix entries carry a
    +    `poolArch` dimension because the 1ES hosted pools select their
    +    image from `hostArchitecture`; an arm64 entry on a default x64
    +    pool would silently grab the wrong image. macOS uses the same
    +    matrix-parameter shape as Windows and Linux, so future macOS
    +    variants drop in the same way an extra Windows toolchain would.
     
    -    At the start of a run there is a prerequisite stage and pre-build
    -    validation. Today this does nothing, and should be updated to:
    -     * validate the current commit is tagged (annotated), and
    -     * capture the Git version, tag name and SHA.
    -
    -    Artifacts are uploaded from the build stage, and downloaded into the
    -    release stage later for uploading to a draft GitHub release.
    +    The prereqs stage derives the Git version, tag name, and tag
    +    SHA via resolve-version.sh and exposes them as pipeline
    +    variables for downstream stages to pick up. For that walk to
    +    find tags at all, the prereqs checkout uses fetchDepth: 0 /
    +    fetchTags: true. setup-git-bash.cmd is the Windows-side
    +    prerequisite that prepends Git Bash to PATH, since the bare
    +    hosted image does not provide a bash for Bash@3 tasks to find.
     
         ESRP signing to be added later.
     
         Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
    +    Assisted-by: Claude Opus 4.7
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .azure-pipelines/release.yml (new) ##
     @@
    @@ .azure-pipelines/release.yml (new)
     +      - id: windows_x64
     +        jobName: 'Windows (x64)'
     +        pool: GitClientPME-1ESHostedPool-intel-pc
    ++        poolArch: amd64
     +        image: win-x86_64-ado1es
     +        os: windows
     +        toolchain: x86_64
    @@ .azure-pipelines/release.yml (new)
     +      - id: windows_arm64
     +        jobName: 'Windows (ARM64)'
     +        pool: GitClientPME-1ESHostedPool-arm64-pc
    ++        poolArch: arm64
     +        image: win-arm64-ado1es
     +        os: windows
     +        toolchain: clang-aarch64
     +        mingwprefix: clangarm64
     +
    -+  # No matrix for macOS as we build both x64 and ARM64 in the same job
    -+  # and produce a universal binary.
    ++  - name: macos_matrix
    ++    type: object
    ++    default:
    ++      - id: macos_universal
    ++        jobName: 'macOS (x64 + ARM64)'
    ++        pool: 'Azure Pipelines'
    ++        # macOS-latest is an Intel x86_64 Mac Pro, which can't host the
    ++        # arm64 Homebrew the universal-binary build needs. We need to
    ++        # explictly target the newer Apple Silicon machines that can run
    ++        # both x86_64 and arm64 builds.
    ++        image: macOS-15-arm64
    ++        os: macos
     +
     +  - name: linux_matrix
     +    type: object
    @@ .azure-pipelines/release.yml (new)
     +      - id: linux_x64
     +        jobName: 'Linux (x64)'
     +        pool: GitClientPME-1ESHostedPool-intel-pc
    ++        poolArch: amd64
     +        image: ubuntu-x86_64-ado1es
     +        os: linux
     +        cc_arch: x86_64
    @@ .azure-pipelines/release.yml (new)
     +      - id: linux_arm64
     +        jobName: 'Linux (ARM64)'
     +        pool: GitClientPME-1ESHostedPool-arm64-pc
    ++        poolArch: arm64
     +        image: ubuntu-arm64-ado1es
     +        os: linux
     +        cc_arch: aarch64
    @@ .azure-pipelines/release.yml (new)
     +              image: ubuntu-x86_64-ado1es
     +              os: linux
     +            steps:
    ++              - checkout: self
    ++                fetchDepth: 0
    ++                fetchTags: true
     +              - task: Bash@3
     +                displayName: 'Resolve version and tag information'
     +                name: info
     +                inputs:
    -+                  targetType: inline
    -+                  script: |
    -+                    # TODO: determine git_version, tag_name, and tag_sha
    -+                    # TODO: error if the current commit is not an annotated tag
    -+                    git_version=TODO_GITVER
    -+                    tag_name=TODO_TAGNAME
    -+                    tag_sha=TODO_TAGSHA
    -+                    echo "##vso[task.setvariable variable=git_version;isOutput=true;isReadOnly=true]$git_version"
    -+                    echo "##vso[task.setvariable variable=tag_name;isOutput=true;isReadOnly=true]$tag_name"
    -+                    echo "##vso[task.setvariable variable=tag_sha;isOutput=true;isReadOnly=true]$tag_sha"
    ++                  targetType: filePath
    ++                  filePath: .azure-pipelines/scripts/resolve-version.sh
     +
     +      - stage: build
     +        displayName: 'Build'
    @@ .azure-pipelines/release.yml (new)
     +                name: ${{ dim.pool }}
     +                image: ${{ dim.image }}
     +                os: ${{ dim.os }}
    ++                hostArchitecture: ${{ dim.poolArch }}
     +              variables:
     +                tag_name: $[stageDependencies.prereqs.prebuild.outputs['info.tag_name']]
     +                tag_sha: $[stageDependencies.prereqs.prebuild.outputs['info.tag_sha']]
    @@ .azure-pipelines/release.yml (new)
     +                    artifactName: '${{ dim.id }}'
     +              steps:
     +                - checkout: self
    ++                # Add Git Bash to the PATH so Bash tasks can find it
    ++                - task: BatchScript@1
    ++                  displayName: 'Add Git Bash to PATH'
    ++                  inputs:
    ++                    filename: ./.azure-pipelines/scripts/windows/setup-git-bash.cmd
     +                # TODO: add tasks to set up Git for Windows SDK
     +                # TODO: add tasks to build Git and installers
     +                - script: |
     +                    echo $(mingwprefix)
     +                    echo $(toolchain)
    ++                    mkdir $(Build.ArtifactStagingDirectory)\app
    ++                    copy C:\Windows\System32\calc.exe $(Build.ArtifactStagingDirectory)\app\example1.exe
    ++                    copy C:\Windows\System32\calc.exe $(Build.ArtifactStagingDirectory)\app\example2.exe
    ++                    copy C:\Windows\System32\calc.exe $(Build.ArtifactStagingDirectory)\app\example3.exe
     +                  displayName: 'Dummy build'
     +                # TODO: put final artifacts under $(Build.ArtifactStagingDirectory)/_final
     +                - script: |
    -+                    echo "TODO" > $(Build.ArtifactStagingDirectory)/_final/placeholder.txt
    ++                    mkdir $(Build.ArtifactStagingDirectory)\_final
    ++                    xcopy /s /y $(Build.ArtifactStagingDirectory)\app $(Build.ArtifactStagingDirectory)\_final
    ++                  displayName: 'Dummy collect artifacts'
     +
     +          #
    -+          # macOS build job (universal)
    ++          # macOS build jobs
     +          #
    -+          - job: macos_universal
    -+            displayName: 'macOS (x64 + ARM64)'
    -+            pool:
    -+              name: 'Azure Pipelines'
    -+              image: macOS-latest
    -+              os: macos
    -+            variables:
    -+              tag_name: $[stageDependencies.prereqs.prebuild.outputs['info.tag_name']]
    -+              tag_sha: $[stageDependencies.prereqs.prebuild.outputs['info.tag_sha']]
    -+              git_version: $[stageDependencies.prereqs.prebuild.outputs['info.git_version']]
    -+            templateContext:
    -+              outputs:
    -+                - output: pipelineArtifact
    -+                  targetPath: '$(Build.ArtifactStagingDirectory)/_final'
    -+                  artifactName: 'macos_universal'
    -+            steps:
    -+              - checkout: self
    -+              # TODO: add tasks to set up build environment
    -+              # TODO: add tasks to build Git and installers
    -+              - script: |
    -+                  echo "Hello, Mac!"
    -+                displayName: 'Dummy build'
    -+              # TODO: put final artifacts under $(Build.ArtifactStagingDirectory)/_final
    -+              - script: |
    -+                  echo "TODO" > $(Build.ArtifactStagingDirectory)/_final/placeholder.txt
    ++          - ${{ each dim in parameters.macos_matrix }}:
    ++            - job: ${{ dim.id }}
    ++              displayName: ${{ dim.jobName }}
    ++              pool:
    ++                name: ${{ dim.pool }}
    ++                image: ${{ dim.image }}
    ++                os: ${{ dim.os }}
    ++              variables:
    ++                tag_name: $[stageDependencies.prereqs.prebuild.outputs['info.tag_name']]
    ++                tag_sha: $[stageDependencies.prereqs.prebuild.outputs['info.tag_sha']]
    ++                git_version: $[stageDependencies.prereqs.prebuild.outputs['info.git_version']]
    ++              templateContext:
    ++                outputs:
    ++                  - output: pipelineArtifact
    ++                    targetPath: '$(Build.ArtifactStagingDirectory)/_final'
    ++                    artifactName: '${{ dim.id }}'
    ++              steps:
    ++                - checkout: self
    ++                # TODO: add tasks to set up build environment
    ++                # TODO: add tasks to build Git and installers
    ++                - script: |
    ++                    echo "Hello, Mac!"
    ++                    mkdir -p $(Build.ArtifactStagingDirectory)/app
    ++                    cp /bin/echo $(Build.ArtifactStagingDirectory)/app/example
    ++                  displayName: 'Dummy build'
    ++                # TODO: put final artifacts under $(Build.ArtifactStagingDirectory)/_final
    ++                - script: |
    ++                    mkdir -p $(Build.ArtifactStagingDirectory)/_final
    ++                    cp -R $(Build.ArtifactStagingDirectory)/app/* $(Build.ArtifactStagingDirectory)/_final/
    ++                  displayName: 'Dummy collect artifacts'
     +
     +          #
     +          # Linux build jobs
    @@ .azure-pipelines/release.yml (new)
     +                name: ${{ dim.pool }}
     +                image: ${{ dim.image }}
     +                os: ${{ dim.os }}
    ++                hostArchitecture: ${{ dim.poolArch }}
     +              variables:
     +                tag_name: $[stageDependencies.prereqs.prebuild.outputs['info.tag_name']]
     +                tag_sha: $[stageDependencies.prereqs.prebuild.outputs['info.tag_sha']]
    @@ .azure-pipelines/release.yml (new)
     +                - script: |
     +                    echo $(cc_arch)
     +                    echo $(deb_arch)
    ++                    mkdir -p $(Build.ArtifactStagingDirectory)/app
    ++                    debroot=$(Build.ArtifactStagingDirectory)/pkgroot
    ++                    mkdir -p $debroot/DEBIAN
    ++                    cat > $debroot/DEBIAN/control <<CTRL
    ++                    Package: example
    ++                    Version: 1.0
    ++                    Architecture: $(deb_arch)
    ++                    Maintainer: test
    ++                    Description: dummy
    ++                    CTRL
    ++                    dpkg-deb --build $debroot $(Build.ArtifactStagingDirectory)/app/example_$(deb_arch).deb
     +                  displayName: 'Dummy build'
     +                # TODO: put final artifacts under $(Build.ArtifactStagingDirectory)/_final
     +                - script: |
    -+                    echo "TODO" > $(Build.ArtifactStagingDirectory)/_final/placeholder.txt
    ++                    mkdir -p $(Build.ArtifactStagingDirectory)/_final
    ++                    cp -R $(Build.ArtifactStagingDirectory)/app/* $(Build.ArtifactStagingDirectory)/_final/
    ++                  displayName: 'Dummy collect artifacts'
     +
     +      - stage: release
     +        displayName: 'Release'
     +        dependsOn: [prereqs, build]
     +        jobs:
    ++          #
    ++          # GitHub release publishing
    ++          #
     +          - job: github
     +            displayName: 'Publish GitHub release'
     +            condition: and(succeeded(), eq('${{ parameters.github }}', true))
    @@ .azure-pipelines/release.yml (new)
     +              type: releaseJob
     +              isProduction: true
     +              inputs:
    -+                - input: pipelineArtifact
    -+                  artifactName: 'windows_x64'
    -+                  targetPath: $(Pipeline.Workspace)/assets/windows_x64
    -+                - input: pipelineArtifact
    -+                  artifactName: 'windows_arm64'
    -+                  targetPath: $(Pipeline.Workspace)/assets/windows_arm64
    -+                - input: pipelineArtifact
    -+                  artifactName: 'macos_universal'
    -+                  targetPath: $(Pipeline.Workspace)/assets/macos_universal
    -+                - input: pipelineArtifact
    -+                  artifactName: 'linux_x64'
    -+                  targetPath: $(Pipeline.Workspace)/assets/linux_x64
    -+                - input: pipelineArtifact
    -+                  artifactName: 'linux_arm64'
    -+                  targetPath: $(Pipeline.Workspace)/assets/linux_arm64
    ++                - ${{ each dim in parameters.windows_matrix }}:
    ++                  - input: pipelineArtifact
    ++                    artifactName: '${{ dim.id }}'
    ++                    targetPath: $(Pipeline.Workspace)/assets/${{ dim.id }}
    ++                - ${{ each dim in parameters.macos_matrix }}:
    ++                  - input: pipelineArtifact
    ++                    artifactName: '${{ dim.id }}'
    ++                    targetPath: $(Pipeline.Workspace)/assets/${{ dim.id }}
    ++                - ${{ each dim in parameters.linux_matrix }}:
    ++                  - input: pipelineArtifact
    ++                    artifactName: '${{ dim.id }}'
    ++                    targetPath: $(Pipeline.Workspace)/assets/${{ dim.id }}
     +            steps:
     +              - task: GitHubRelease@1
     +                displayName: 'Create Draft GitHub Release'
    @@ .azure-pipelines/release.yml (new)
     +                    $(Pipeline.Workspace)/assets/linux_x64/*.tar.gz
     +                    $(Pipeline.Workspace)/assets/linux_arm64/*.deb
     +                    $(Pipeline.Workspace)/assets/linux_arm64/*.tar.gz
    +
    + ## .azure-pipelines/scripts/resolve-version.sh (new) ##
    +@@
    ++#!/bin/bash
    ++#
    ++# Resolve version and tag information from the current HEAD commit.
    ++# Validates that HEAD is an annotated version tag matching GIT-VERSION-GEN.
    ++#
    ++# Sets the following ADO output variables (via ##vso):
    ++#   git_version  - Version string without "v" prefix (e.g., 2.53.0.vfs.0.0)
    ++#   tag_name     - Full tag name (e.g., v2.53.0.vfs.0.0)
    ++#   tag_sha      - Commit SHA of HEAD
    ++#
    ++# Also updates the build number to include the tag name.
    ++#
    ++set -euo pipefail
    ++
    ++echo "HEAD: $(git rev-parse HEAD)"
    ++
    ++# Determine the tag pointing at HEAD
    ++tag_name=$(git describe --exact-match --match "v[0-9]*vfs*" HEAD 2>/dev/null) || {
    ++	echo "##vso[task.logissue type=error]HEAD is not tagged with a version tag"
    ++	exit 1
    ++}
    ++
    ++# Verify the tag is annotated (not lightweight)
    ++tag_type=$(git cat-file -t "refs/tags/$tag_name")
    ++if [ "$tag_type" != "tag" ]; then
    ++	echo "##vso[task.logissue type=error]Tag $tag_name is not annotated (type: $tag_type)"
    ++	exit 1
    ++fi
    ++
    ++tag_sha=$(git rev-parse HEAD)
    ++git_version="${tag_name#v}"
    ++
    ++# Verify the version matches GIT-VERSION-GEN
    ++make GIT-VERSION-FILE
    ++expected_version="${git_version//-rc/.rc}"
    ++actual_version=$(sed -n 's/^GIT_VERSION *= *//p' < GIT-VERSION-FILE)
    ++if [ "$expected_version" != "$actual_version" ]; then
    ++	echo "##vso[task.logissue type=error]GIT-VERSION-FILE ($actual_version) does not match tag $tag_name ($expected_version)"
    ++	exit 1
    ++fi
    ++
    ++echo "Git version: $git_version"
    ++echo "Tag name: $tag_name"
    ++echo "Tag SHA: $tag_sha"
    ++echo "##vso[task.setvariable variable=git_version;isOutput=true;isReadOnly=true]$git_version"
    ++echo "##vso[task.setvariable variable=tag_name;isOutput=true;isReadOnly=true]$tag_name"
    ++echo "##vso[task.setvariable variable=tag_sha;isOutput=true;isReadOnly=true]$tag_sha"
    ++echo "##vso[build.updatebuildnumber]${tag_name} (${BUILD_BUILDNUMBER:-unknown})"
    +
    + ## .azure-pipelines/scripts/windows/setup-git-bash.cmd (new) ##
    +@@
    ++@echo off
    ++setlocal enabledelayedexpansion
    ++set "agentgit=%AGENT_HOMEDIRECTORY%\externals\git"
    ++set "gitcopy=%AGENT_TEMPDIRECTORY%\git"
    ++echo Copying !agentgit! to !gitcopy!...
    ++xcopy /E /I /Q "!agentgit!" "!gitcopy!"
    ++if not exist "!gitcopy!\usr\bin\sh.exe" (
    ++	echo ##vso[task.logissue type=error]Could not find sh.exe at !gitcopy!\usr\bin\sh.exe
    ++	exit /b 1
    ++)
    ++echo Copying !gitcopy!\usr\bin\sh.exe to !gitcopy!\usr\bin\bash.exe...
    ++copy /Y "!gitcopy!\usr\bin\sh.exe" "!gitcopy!\usr\bin\bash.exe"
    ++echo ##vso[task.prependpath]!gitcopy!\usr\bin
  • 254: ed5c15a ! 252: 6c70eb1 diff: add renameThreshold configuration option

    @@ Commit message
         The value accepts the same format as -M: a percentage (e.g. 50%) or
         a fraction (e.g. 0.5). If unset, the default remains 50%.
     
    -    This also gives git-blame users control over the rename threshold
    -    for the first time, since blame has no -M threshold flag but inherits
    -    diff.renameThreshold via repo_diff_setup().
    -
         Assisted-by: Claude Opus 4.6
         Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## Documentation/config/diff.adoc ##
     @@ Documentation/config/diff.adoc: endif::git-diff[]
    @@ Documentation/config/diff.adoc: endif::git-diff[]
     +	percentage (e.g. `50%`), or a fraction between 0 and 1
     +	(e.g. `0.5`).  If not set, the default is 50%.  This setting
     +	has no effect if rename detection is turned off.
    -++
    -+This config setting is also respected by linkgit:git-blame[1];
    -+to limit blame to only consider exact renames, for example, set
    -+`diff.renameThreshold = 100%`.
     +
      `diff.renames`::
      	Whether and how Git detects renames.  If set to `false`,
    @@ diff.c
      static int diff_suppress_blank_empty;
      static enum git_colorbool diff_use_color_default = GIT_COLOR_UNKNOWN;
      static int diff_color_moved_default;
    -@@ diff.c: int git_diff_ui_config(const char *var, const char *value,
    - 		diff_detect_rename_default = git_config_rename(var, value);
    +@@ diff.c: int git_diff_basic_config(const char *var, const char *value,
      		return 0;
      	}
    + 
     +	if (!strcmp(var, "diff.renamethreshold")) {
     +		const char *arg = value;
     +		if (!value)
    @@ diff.c: int git_diff_ui_config(const char *var, const char *value,
     +			return error(_("invalid value for '%s': '%s'"), var, value);
     +		return 0;
     +	}
    - 	if (!strcmp(var, "diff.autorefreshindex")) {
    - 		diff_auto_refresh_index = git_config_bool(var, value);
    - 		return 0;
    ++
    + 	if (userdiff_config(var, value) < 0)
    + 		return -1;
    + 
     @@ diff.c: void repo_diff_setup(struct repository *r, struct diff_options *options)
      	options->add_remove = diff_addremove;
      	options->use_color = diff_use_color_default;
  • 255: 0d2fdc1 (upstream: 0d2fdc1) < -: ------------ ci: bump microsoft/setup-msbuild from v2 to v3

  • 256: 5d719b3 (upstream: 5d719b3) < -: ------------ ci: bump actions/{upload,download}-artifact to v7 and v8

  • 257: bfbe0db (upstream: bfbe0db) < -: ------------ ci: bump actions/github-script from v8 to v9

  • 258: 5694ca1 (upstream: 5694ca1) < -: ------------ ci: bump actions/checkout from v5 to v6

  • 259: cd920e9 (upstream: cd920e9) < -: ------------ Add an AGENTS.md file to help with AI-assisted debugging/development

  • 260: b6ee11c (upstream: b6ee11c) < -: ------------ fixup! Add an AGENTS.md file to help with AI-assisted debugging/development

  • 261: 448b5cf (upstream: 448b5cf) < -: ------------ fixup! Add an AGENTS.md file to help with AI-assisted debugging/development

  • 262: 497ffcd (upstream: 497ffcd) < -: ------------ fixup! Add an AGENTS.md file to help with AI-assisted debugging/development

  • 263: 46a110b (upstream: 46a110b) < -: ------------ fixup! Add an AGENTS.md file to help with AI-assisted debugging/development

  • 264: 63f8b95 (upstream: 63f8b95) < -: ------------ fixup! Add an AGENTS.md file to help with AI-assisted debugging/development

  • 265: 6713544 = 253: f0a7682 gvfs-helper: separate packfile extraction from indexing

  • 268: b9b36ab = 254: df89047 blame: add blame.renames, blame.renameThreshold, blame.renameLimit

  • 266: ae9e1e6 = 255: 941b447 gvfs-helper: run prefetch index-pack in parallel

  • 267: 25cd276 < -: ------------ amend! diff: add renameThreshold configuration option

  • 269: 21186cf (upstream: 21186cf) < -: ------------ alias: restore support for simple dotted aliases

  • 270: 39ba52a (upstream: 39ba52a) < -: ------------ fixup! ci: work around a problem with HTTP/2 vs libcurl v8.10.0

  • 271: c340e33 (upstream: c340e33) < -: ------------ fixup! revision: create mark_trees_uninteresting_dense()

  • 272: 97508e9 (upstream: 97508e9) < -: ------------ mingw: optionally use legacy (non-POSIX) delete semantics

  • 273: 12ebd5c (upstream: 12ebd5c) < -: ------------ maintenance(geometric): do release the .idx files before repacking

  • 274: 0a32e7b (upstream: 0a32e7b) < -: ------------ fixup! add: use preload-index and fscache for performance

  • 275: add7a58 (upstream: add7a58) < -: ------------ fixup! fscache: fscache takes an initial size

  • 276: cdc2178 < -: ------------ fixup! hooks: add custom post-command hook config

  • 277: 7b9a0a7 < -: ------------ fixup! Add virtual file system settings and hook proc

  • 278: 0752978 = 256: b7043da gvfs-helper: add gvfs.prefetchThreads config for parallel prefetch

  • 279: c6e8df1 (upstream: c6e8df1) < -: ------------ ci: bump git-for-windows/setup-git-for-windows-sdk from v1 to v2

  • 280: b9ccb66 (upstream: b9ccb66) < -: ------------ l10n: bump mshick/add-pr-comment from v2 to v3

  • 281: b797f03 (upstream: b797f03) < -: ------------ fixup! Add a GitHub workflow to verify that Git/Scalar work in Nano Server

  • 282: 9fe9a58 < -: ------------ fixup! release: create initial Windows installer build workflow

  • 283: 6d210a1 < -: ------------ fixup! codeql: run static analysis as part of CI builds

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

  • 285: 90ef05b < -: ------------ fixup! ci: run Scalar's Functional Tests

  • 286: 1383831 < -: ------------ fixup! ci: add new VFS for Git functional tests workflow

  • 287: 251478a (upstream: 74c3ea9) < -: ------------ build: tolerate use of _Generic from glibc 2.43 with Clang

  • 288: 2431f5e (upstream: 2431f5e) < -: ------------ shallow: fix relative deepen on non-shallow repositories

  • 289: 6e95b07 (upstream: 6e95b07) < -: ------------ builtin/maintenance: fix locking with "--detach"

  • 290: 29364f1 (upstream: 29364f1) < -: ------------ run-command: honor "gc.auto" for auto-maintenance

  • 291: 79971fc < -: ------------ amend! azure-pipelines: add stub release pipeline for Azure

  • 292: e782c90 = 257: bbc1404 azure-pipelines: add ESRP code signing

  • 293: 938529c = 258: ceb76bb azure-pipelines: allow overriding Git version

  • 294: 325ef64 = 259: 5338080 azure-pipelines: build, sign and stage the Linux Debian package

  • 295: 7fa9458 = 260: b805ee4 azure-pipelines: build, sign, notarize and stage the macOS installer

  • 296: 73af387 = 261: d0037b3 azure-pipelines: build, sign and stage the Windows installer

  • 297: a652f28 = 262: 285ba23 azure-pipelines: enable on tag push, default ESRP and GitHub release on

  • 298: 399666a < -: ------------ amend! release: create initial Windows installer build workflow

  • 300: 5ebe6f6 ! 263: 18cc779 release: binskim for Windows

    @@ Commit message
         build-extra git-wrapper launcher shims are all third-party content we
         cannot fix from this repo.
     
    +    Since the baseline that is added automatically does not conform to Git's
    +    whitespace rules, also add a `.config/.gitattributes` file to suppress
    +    those checks.
    +
         Assisted-by: Claude Opus 4.7
         Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
    +    Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
     
      ## .azure-pipelines/release.yml ##
     @@ .azure-pipelines/release.yml: extends:
    @@ .azure-pipelines/release.yml: extends:
                      # Validate the freshly built installer in-place: silently
                      # install Git-*.exe and assert that `git --version` reports
                      # the version we resolved at the prereqs stage. Folded into
    +
    + ## .config/.gitattributes (new) ##
    +@@
    ++* whitespace=-trail,-space,-incomplete
  • 301: e0445ac = 264: be3c793 release: suppress unfixable binskim findings

  • 302: 4e19046 = 265: 8be7f3d binskim: add baseline

  • 304: b6c0a4a ! 266: feab5d8 release: drop Azure CLI install on Windows/ARM64

    @@ Metadata
     Author: Matthew John Cheetham <mjcheetham@outlook.com>
     
      ## Commit message ##
    -    release: drop Azure CLI install on Windows/ARM64
    +    fixup! azure-pipelines: build, sign and stage the Windows installer
     
         The Windows ARM64 images now include the Azure CLI so we do not need to
         install it manually on each run. This brings ARM64 in line with x86_64,
         and saves 1-2 mins per run.
     
         Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
    +    Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
     
      ## .azure-pipelines/release.yml ##
     @@ .azure-pipelines/release.yml: extends:
  • 299: 200b063 = 267: 776633a checkout: preserve skip-worktree for virtual filesystem paths

  • 303: 6d20b6b < -: ------------ amend! release: binskim for Windows

  • -: ------------ > 268: 3a9d8f9 rust: pick a GCC-compatible Cargo target under MSYS2/MinGW

  • -: ------------ > 269: 55a3538 ci(vfs): install the GCC-compatible Rust target before building

  • -: ------------ > 270: e96b1d8 fixup! gvfs: add global command pre and post hook procs

  • -: ------------ > 271: 24ba8ba fixup! gvfs-helper: verify loose objects after write

  • -: ------------ > 272: e2a6129 fixup! t5799: add support for POST to return either a loose object or packfile

  • -: ------------ > 273: 5071116 amend! rust: pick a GCC-compatible Cargo target under MSYS2/MinGW

  • -: ------------ > 274: e69de00 fixup! sha1-file: add function to update existing loose object cache

  • -: ------------ > 275: 187e5b5 fixup! t9210: differentiate origin and cache servers

  • -: ------------ > 276: 9133d1c fixup! t9210: differentiate origin and cache servers

  • -: ------------ > 277: 94b5b6f fixup! status: add status serialization mechanism

derrickstolee and others added 30 commits June 12, 2026 11:55
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
It is possible that a loose object that is written from a GVFS protocol
"get object" request does not match the expected hash. Error out in this
case.

2021-10-30: The prototype for read_loose_object() changed in 31deb28 (fsck:
don't hard die on invalid object types, 2021-10-01) and 96e41f5 (fsck:
report invalid object type-path combinations, 2021-10-01).

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
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>
Teach helper/test-gvfs-protocol to be able to send corrupted
loose blobs.

Add unit test for gvfs-helper to detect receipt of a corrupted loose blob.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
`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>
Add a new JavaScript GitHub Action to download secrets from Azure Key
Vault using the `az` CLI, mask the secret values, and store them as:
 * outputs,
 * environment variables, or
 * files;

Values are all masked for safe consumption by other steps in a workflow.

Callers of this action can optionally perform base64 decoding of secret
values using the syntax: `INPUT base64> OUTPUT`.

It is assumed that the `az login` command has already been run prior to
this action being invoked.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Add a manual-only GitHub Actions workflow for building the
Windows installer (x86_64 plus portable Git), driven via
`workflow_dispatch:`. The production release path for the
official microsoft/git installers lives in
.azure-pipelines/release.yml; this workflow is kept around as a
fallback so the Windows installer can still be produced on
demand for debugging or comparison.

The build steps are pinned to `windows-2019` (rather than
`windows-latest`) to ensure the correct Visual Studio version
is used (verified in the pipeline via `type -p mspdb140.dll`),
and the SDK used is the `full` flavor rather than
`build-installers` due to a known (but not-yet-fixed) issue
downloading the `build-installers` flavor with the
`git-for-windows/setup-git-for-windows-sdk` Action.

There is no code-signing certificate available to this workflow,
so the artifacts it produces are unsigned and must not be
published as releases; they are useful only for build-time
debugging.

Signed-off-by: Victoria Dye <vdye@github.com>
Assisted-by: Claude Opus 4.7
Co-authored-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When building Git as a universal binary on macOS, the binary supports more than
one target architecture. This is a bit of a problem for the `HOST_CPU`
setting that is woefully unprepared for such a situation, as it wants to
show architecture hard-coded at build time.

In preparation for releasing universal builds, work around this by
special-casing `universal` and replacing it at run-time with the known
values `x86_64` or `arm64`.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
- include `scalar`
- build signed .dmg & .pkg for target OS version 10.6
- upload artifacts to workflow

Co-authored-by: Lessley Dennington <ldennington@github.com>
Construct 2 new unit tests to explicitly verify the use of
`--fallback` and `--no-fallback` arguments to `gvfs-helper`.

When a cache-server is enabled, `gvfs-helper` will try to fetch
objects from it rather than the origin server.  If the cache-server
fails (and all cache-server retry attempts have been exhausted),
`gvfs-helper` can optionally "fallback" and try to fetch the objects
from the origin server.  (The retry logic is also applied to the
origin server, if the origin server fails on the first request.)

Add new unit tests to verify that `gvfs-helper` respects both the
`--max-retries` and `--[no-]fallback` arguments.

We use the "http_503" mayhem feature of the `test_gvfs_protocol`
server to force a 503 response on all requests to the cache-server and
the origin server end-points.  We can then count the number of connection
requests that `gvfs-helper` makes to the server and confirm both the
per-server retries and whether fallback was attempted.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
- include `scalar`
- build & upload unsigned .deb package

Co-authored-by: Lessley Dennington <ldennington@github.com>
Co-authored-by: Sverre Johansen <sverre.johansen@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
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>
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>
Add a GitHub workflow that is triggered on the `release` event to
automatically update the `microsoft-git` Homebrew Cask on the
`microsoft/git` Tap.

A secret `HOMEBREW_TOKEN` with push permissions to the
`microsoft/homebrew-git` repository must exist. A pull request will be
created at the moment to allow for last minute manual verification.

Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.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>
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>
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>
In particular when multiple processes want to write to the config
simultaneously, it would come in handy to not fail immediately when
another process locked the config, but to gently try again.

This will help with Scalar's functional test suite which wants to
register multiple repositories for maintenance semi-simultaneously.

As not all code paths calling this function read the config (e.g. `git
config`), we have to read the config setting via
`git_config_get_ulong()`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
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.
dscho added 4 commits June 12, 2026 12:00
It's now harder to get to the loose object cache, intentionally so,
because it's somewhat of an internal implementation detail whether or
not the ODB is backed (partially) by a loose object backend.

For the time being, we're on the safe side, though: This functionality
is required for the GVFS helper, and VFS for Git does not (yet) need to
adapt to pluggable ODBs.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The test `'scalar clone: all verbs with different servers'` added by
this commit registers `per_test_cleanup` as a `test_when_finished`
hook and then starts gvfs-protocol server instances 1, 2, 3, *and 4*.
However, `per_test_cleanup` in `t/lib-gvfs-helper.sh` only iterates
over instances 0 through 3, so instance 4's pid file
(`pid-file-4.pid`) is never deleted.  The `test-gvfs-protocol.exe`
that backs instance 4 watches its pid file and shuts down only when
that file disappears, so it keeps running forever, listening on the
shifted-by-40000 port.

On Linux the leaked daemon is harmless: when its grandparent shell
exits, the daemon is reparented to init and the test process tree is
free to terminate.  On Windows, however, the subshell that
backgrounded the `test-gvfs-protocol.exe` inherits standard handles
to the parent `prove` worker, and those handles keep the subshell
alive as long as the daemon is alive.  The end result is that `make
test` for the slice containing `t9210-scalar.sh` never returns even
after every individual test script has produced its exit code file:
the parallel test runner is still waiting on the wedged subshell.
This is exactly what we observed in
https://github.com/microsoft/git/actions/runs/27409166978, where the
three Windows test slices that contain `t9210-scalar.sh` (`win test
(6)` and `win+VS test (6)` in prove-slice 7 of 10, plus `win+Meson
test (7)` in meson-slice 8 of 10) were stuck for several hours while
every other Windows test slice completed in five to twelve minutes.

Extend the cleanup loop to also stop instance 4 so the daemon can
exit and the subshell can drain.

Assisted-by: Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The origin server's stderr log was placed at
"OUT.server.$GIT_TEST_GVFS_PROTOCOL_ORIGIN_PORT.log", which matches
the "OUT.*" glob that `per_test_cleanup` in `t/lib-gvfs-helper.sh`
uses to wipe per-test output between tests.  The shared lib's intent
when it runs `rm -rf OUT.*` is to discard files like
"OUT.gvfs.server.log" and "OUT.server-1.log" through
"OUT.server-4.log" that it owns, plus per-test scratch like
"OUT.output" and "OUT.stderr".  It is not aware of t9210's separate
"origin" server, so once any test in t9210 registers
`per_test_cleanup` as a `test_when_finished` hook, the origin
server's log gets blown away as a side effect.

By the time `test_atexit` runs at the end of the script, it can
still find the pid-file (which lives at
"pid-file.$port.pid" and is outside the glob) and tear the origin
server down, but the subsequent `grep "Starting graceful shutdown"
"$ORIGIN_SERVER_LOG"` always fails because the log is gone, so the
loop times out and `test_atexit` returns 1.  That turns the whole
script into a failure (`Wstat: 256 (exited 1) Tests: 37 Failed: 0`)
even though every individual test passed.  See for example
https://github.com/microsoft/git/actions/runs/27424512013 where this
exit-1 cleanup symptom shows up across `win test (6)`,
`win+VS test (6)`, and every Linux containerized job once the
unrelated hang fix unblocks them.

The original intent of this commit was to namespace t9210's variables
so they no longer collide with the shared lib's; the file name still
collides with the shared lib's cleanup glob, which contradicts that
intent.  Move the log file out of the "OUT.*" glob by naming it
"server-origin.$port.log" instead.  The pid-file is already outside
the glob, so no rename is needed there.

Assisted-by: Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The test scripts `t7522-serialized-status.sh` and
`t7523-status-complete-untracked.sh` introduced by this commit
inspect `git status` output that includes the ignored section.

Since the upstream change "test-lib: allow bare repository access
when breaking changes are enabled", `test-lib.sh` writes a
`$HOME/.gitconfig` (containing `safe.bareRepository = all`) and adds
that file to `.git/info/exclude` whenever `WITH_BREAKING_CHANGES` is
enabled.  Because `$HOME` and the test repository's working tree
share the same trash directory, the `.gitconfig` file shows up as
`! .gitconfig` in any `git status --ignored` output.  Upstream Git
addressed this in its own status-related tests by filtering
`.gitconfig` from comparison input via a new `test_filter_gitconfig`
helper, but it did not (and could not) touch these microsoft/git
downstream tests.

In the `linux-breaking-changes (ubuntu:rolling)` CI job at
https://github.com/microsoft/git/actions/runs/27434672980 this
manifests as three failures in `t7522` (subtests 2, 4, and 5) and
one failure in `t7523`, all with the same diff:

	+! .gitconfig
	 ! ignored.ign
	 ! ignored_dir/

Apply the same `test_filter_gitconfig` treatment to the four
affected subtests here.  Only the subtests whose expected output
includes the ignored section need the filter; the remaining
subtests in `t7522` either skip `--ignored` on the deserialize side
or scope the output to a path that excludes `.gitconfig`, so they
are unaffected.

Verified in a WSL checkout of the branch tip (`9133d1cce3e2`) by
building with `WITH_BREAKING_CHANGES=YesPlease` and running both
scripts: before this change, `t7522` reports `# failed 3 among 16
test(s)` and `t7523` reports `not ok 2`; after this change, both
scripts report `# passed all`.

Assisted-by: Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho dscho self-assigned this Jun 15, 2026
Now that Rust is opt-out in upstream Git, we better install `cargo` (or
define `NO_RUST`, but the former will age better).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho dscho force-pushed the tentative/vfs-2.55.0-rc0 branch from 2828f27 to 25398eb Compare June 15, 2026 10:48
@dscho dscho requested a review from mjcheetham June 15, 2026 13:04
@dscho dscho marked this pull request as ready for review June 15, 2026 13:04

@derrickstolee derrickstolee left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lots of squashes made the range-diff big, but things look good!

@dscho

dscho commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Lots of squashes made the range-diff big, but things look good!

Right. I should have first squashed the fixups, created a clean/vfs-2.54.0, rebased that and then range-diff'ed those two. Sorry. Will do that with -rc1 (there's still plenty of squashing required, thanks to the odb/loose reaction work).

@mjcheetham mjcheetham left a comment

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 looks good to me!

dscho added 15 commits June 16, 2026 12:54
…taller

The macOS universal build compiles C code with `-arch arm64 -arch x86_64`
but `cargo build --release` only targets the host architecture (arm64 on
the Apple Silicon runner). The linker then ignores all arm64-only objects
from libgitcore.a when linking the x86_64 slice, resulting in undefined
symbols for `_decode_varint` and `_encode_varint`.

Build the Rust static library for both targets explicitly and lipo them
into a universal `target/release/libgitcore.a` before invoking `make
payload`, mirroring how the pipeline already produces a universal
libintl.a from two arch-specific Homebrew copies.
The Makefile now requires `cargo` to build `target/release/libgitcore.a`
(Rust is enabled by default since upstream's bc/rust-by-default topic),
but the 1ES Ubuntu build agents do not ship a Rust toolchain. The build
fails with:

    make: cargo: No such file or directory
    make: *** [Makefile:3057: target/release/libgitcore.a] Error 127

Add `cargo` to the apt-get install list, matching what was done for the
Scalar functional tests in 25398eb.
For now, we opt out of building with the Rust bits.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The version that is reported by the `git` executable on Linux does not
replace `-rc` by `.rc`, for some reason. We're not going to ship any RC
reason anyway, therefore this deviation from before does not matter.

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

Need to pre-gemerate the build flags so that the `libgitcore.a` is not
rebuilt.

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

Turns out that `make dist` _also_ builds Git. It's all a bit wasteful,
still, even if a lot of wasteful bits have been removed since moving
away from the original https://github.com/timcharper/git_osx_installer.

So we need to build the universal ("fat") libgitcore.a _twice_, too.

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

Turns out that `libgit.a` is a dependency of `libgitcore.a`; If the
former gets rebuilt, the latter is force-rebuilt. Strange but true.

Therefore, if we want to have a universal version of the latter and do
not want it to be overwritten with a non-universal one, we first have to
build the former and then the universal variant of the latter.

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

I have no idea why things are so different in Azure Pipelines than they
were in the GitHub workflow, but the `-rc` part of the file name
generated via `make dist` and friends no longer is converted to `.rc`,
as it had been before.

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

It looks as if even more places need to stop that `-rc` -> `.rc`
business.

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

Okay, this pre-generation of the universal libgitcore.a did not work.
Time for more invasive maneuvers.

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

Let's do this in one go, and fix the path to the x86_64 library.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Unless NO_RUST is defined, the varint encoder/decoder lives in the
RUST_LIB, which needs to be linked. Symptom:

cc [... -o contrib/credential/osxkeychain/git-credential-osxkeychain [...]
Undefined symbols for architecture x86_64:
  "_decode_varint", referenced from:
      _read_untracked_extension in libgit.a[x86_64][63](dir.o)
      _read_untracked_extension in libgit.a[x86_64][63](dir.o)
      _read_one_dir in libgit.a[x86_64][63](dir.o)
      _read_one_dir in libgit.a[x86_64][63](dir.o)
      _load_cache_entry_block in libgit.a[x86_64][174](read-cache.o)
  "_encode_varint", referenced from:
      _write_untracked_extension in libgit.a[x86_64][63](dir.o)
      _write_untracked_extension in libgit.a[x86_64][63](dir.o)
      _write_untracked_extension in libgit.a[x86_64][63](dir.o)
      _write_one_dir in libgit.a[x86_64][63](dir.o)
      _write_one_dir in libgit.a[x86_64][63](dir.o)
      _do_write_index in libgit.a[x86_64][174](read-cache.o)
ld: symbol(s) not found for architecture x86_64

Instead of trying to play games to add `GITLIBS` while filtering out
`common-main.o`, replace the `$(LIB_FILE) $(EXTLIBS)` construct with the
much shorter `$(LIBS)` construct that _already_ filters out
`common-main.o` and adds the Rust library when needed.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho dscho merged commit 6329c29 into vfs-2.55.0-rc0 Jun 16, 2026
258 checks passed
@dscho dscho deleted the tentative/vfs-2.55.0-rc0 branch June 16, 2026 20:48
@dscho

dscho commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Lots of squashes made the range-diff big, but things look good!

Right. I should have first squashed the fixups, created a clean/vfs-2.54.0, rebased that and then range-diff'ed those two. Sorry. Will do that with -rc1 (there's still plenty of squashing required, thanks to the odb/loose reaction work).

And here it is: https://github.com/microsoft/git/compare/vfs-2.55.0-rc0..microsoft:git:clean-vfs-2.55.0-rc0

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