Merge branch 'js/ci-ghwf-dedup-tests'

The logic to skip testing on the tagged commit and the tag itself
was not quite consistent which led to failure of Windows test
tasks.  It has been revamped to consistently skip revisions that
have already been tested, based on the tree object of the revision.

* js/ci-ghwf-dedup-tests:
  ci: do not skip tagged revisions in GitHub workflows
  ci: skip GitHub workflow runs for already-tested commits/trees
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0a9acb6..a940997 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -191,7 +191,7 @@
         Expand-Archive -DestinationPath . -Force
     - name: add msbuild to PATH
-      uses: microsoft/setup-msbuild@v1.0.0
+      uses: microsoft/setup-msbuild@v1
     - name: copy dlls to root
       shell: powershell
       run: |
diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt
index 3ba49e8..f3d9566 100644
--- a/Documentation/git-bisect-lk2009.txt
+++ b/Documentation/git-bisect-lk2009.txt
@@ -473,7 +473,7 @@
 2) starting from the "good" ends of the graph, associate to each
-commit the number of ancestors it has plus one
+   commit the number of ancestors it has plus one
 For example with the following graph where H is the "bad" commit and A
 and D are some parents of some "good" commits:
@@ -514,7 +514,7 @@
 4) the best bisection point is the commit with the highest associated
+   number
 So in the above example the best bisection point is commit C.
@@ -580,8 +580,8 @@
 Let's also suppose that we have a cleaned up graph like one after step
 1) in the bisection algorithm above. This means that we can measure
-the information we get in terms of number of commit we can remove from
-the graph..
+   the information we get in terms of number of commit we can remove
+   from the graph..
 And let's take a commit X in the graph.
@@ -689,18 +689,18 @@
 6) sort the commit by decreasing associated value
 7) if the first commit has not been skipped, we can return it and stop
+   here
 8) otherwise filter out all the skipped commits in the sorted list
 9) use a pseudo random number generator (PRNG) to generate a random
-number between 0 and 1
+   number between 0 and 1
 10) multiply this random number with its square root to bias it toward
+    0
 11) multiply the result by the number of commits in the filtered list
-to get an index into this list
+    to get an index into this list
 12) return the commit at the computed index
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index a7f9bc9..6077ff0 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -209,7 +209,7 @@
 	Use \0 as the delimiter for pathnames in the output, and print
 	them verbatim. Without this option, pathnames with "unusual"
 	characters are quoted as explained for the configuration
-	variable core.quotePath (see git-config(1)).
+	variable core.quotePath (see linkgit:git-config[1]).
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index f35f70f..59ecda6 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -70,7 +70,7 @@
 If this is reinitialization, the repository will be moved to the specified path.
--b <branch-name::
+-b <branch-name>::
 Use the specified name for the initial branch in the newly created repository.
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 9659abb..ea73386 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -10,7 +10,7 @@
 'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
+'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <url>
 'git remote rename' <old> <new>
 'git remote remove' <name>
 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
@@ -35,7 +35,7 @@
 	Be a little more verbose and show remote url after name.
-	NOTE: This must be placed between `remote` and `subcommand`.
+	NOTE: This must be placed between `remote` and subcommand.
@@ -46,7 +46,7 @@
-Adds a remote named <name> for the repository at
+Add a remote named <name> for the repository at
 <url>.  The command `git fetch <name>` can then be used to create and
 update remote-tracking branches <name>/<branch>.
@@ -109,13 +109,13 @@
 With `-a` or `--auto`, the remote is queried to determine its `HEAD`, then the
 symbolic-ref `refs/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote
-`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set
+`HEAD` is pointed at `next`, `git remote set-head origin -a` will set
 the symbolic-ref `refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will
 only work if `refs/remotes/origin/next` already exists; if not it must be
 fetched first.
-Use `<branch>` to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., "git
-remote set-head origin master" will set the symbolic-ref `refs/remotes/origin/HEAD` to
+Use `<branch>` to set the symbolic-ref `refs/remotes/<name>/HEAD` explicitly. e.g., `git
+remote set-head origin master` will set the symbolic-ref `refs/remotes/origin/HEAD` to
 `refs/remotes/origin/master`. This will only work if
 `refs/remotes/origin/master` already exists; if not it must be fetched first.
@@ -127,7 +127,7 @@
 after the initial setup for a remote.
 The named branches will be interpreted as if specified with the
-`-t` option on the 'git remote add' command line.
+`-t` option on the `git remote add` command line.
 With `--add`, instead of replacing the list of currently tracked
 branches, adds to that list.
@@ -181,16 +181,16 @@
 See the PRUNING section of linkgit:git-fetch[1] for what it'll prune
 depending on various configuration.
-With `--dry-run` option, report what branches will be pruned, but do not
+With `--dry-run` option, report what branches would be pruned, but do not
 actually prune them.
 Fetch updates for remotes or remote groups in the repository as defined by
-remotes.<group>.  If neither group nor remote is specified on the command line,
+`remotes.<group>`. If neither group nor remote is specified on the command line,
 the configuration parameter remotes.default will be used; if
 remotes.default is not defined, all remotes which do not have the
-configuration parameter remote.<name>.skipDefaultUpdate set to true will
+configuration parameter `remote.<name>.skipDefaultUpdate` set to true will
 be updated.  (See linkgit:git-config[1]).
 With `--prune` option, run pruning against all the remotes that are updated.
diff --git a/Makefile b/Makefile
index 5311b1d..95571ee 100644
--- a/Makefile
+++ b/Makefile
@@ -820,8 +820,8 @@
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
-GENERATED_H += config-list.h
 GENERATED_H += command-list.h
+GENERATED_H += config-list.h
 LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
 	$(FIND) . \
@@ -998,9 +998,9 @@
 LIB_OBJS += split-index.o
 LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
-LIB_OBJS += strvec.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
+LIB_OBJS += strvec.o
 LIB_OBJS += sub-process.o
 LIB_OBJS += submodule-config.o
 LIB_OBJS += submodule.o
@@ -1066,15 +1066,15 @@
 BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
-BUILTIN_OBJS += builtin/credential-cache.o
-BUILTIN_OBJS += builtin/credential-cache--daemon.o
-BUILTIN_OBJS += builtin/credential-store.o
 BUILTIN_OBJS += builtin/column.o
 BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
+BUILTIN_OBJS += builtin/credential-cache--daemon.o
+BUILTIN_OBJS += builtin/credential-cache.o
+BUILTIN_OBJS += builtin/credential-store.o
 BUILTIN_OBJS += builtin/credential.o
 BUILTIN_OBJS += builtin/describe.o
 BUILTIN_OBJS += builtin/diff-files.o
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 8acd078..0d03cb4 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -159,10 +159,6 @@
 #define deepest_delta_lock()	lock_mutex(&deepest_delta_mutex)
 #define deepest_delta_unlock()	unlock_mutex(&deepest_delta_mutex)
-static pthread_mutex_t type_cas_mutex;
-#define type_cas_lock()		lock_mutex(&type_cas_mutex)
-#define type_cas_unlock()	unlock_mutex(&type_cas_mutex)
 static pthread_key_t key;
 static inline void lock_mutex(pthread_mutex_t *mutex)
@@ -186,7 +182,6 @@
 	pthread_mutex_init(&counter_mutex, NULL);
 	pthread_mutex_init(&work_mutex, NULL);
-	pthread_mutex_init(&type_cas_mutex, NULL);
 	if (show_stat)
 		pthread_mutex_init(&deepest_delta_mutex, NULL);
 	pthread_key_create(&key, NULL);
@@ -209,7 +204,6 @@
-	pthread_mutex_destroy(&type_cas_mutex);
 	if (show_stat)
 	for (i = 0; i < nr_threads; i++)
@@ -894,18 +888,15 @@
- * Walk from current node up
- * to top parent if necessary to deflate the node. In normal
- * situation, its parent node would be already deflated, so it just
- * needs to apply delta.
+ * Ensure that this node has been reconstructed and return its contents.
- * In the worst case scenario, parent node is no longer deflated because
- * we're running out of delta_base_cache_limit; we need to re-deflate
- * parents, possibly up to the top base.
- *
- * All deflated objects here are subject to be freed if we exceed
- * delta_base_cache_limit, just like in find_unresolved_deltas(), we
- * just need to make sure the last node is not freed.
+ * In the typical and best case, this node would already be reconstructed
+ * (through the invocation to resolve_delta() in threaded_second_pass()) and it
+ * would not be pruned. However, if pruning of this node was necessary due to
+ * reaching delta_base_cache_limit, this function will find the closest
+ * ancestor with reconstructed data that has not been pruned (or if there is
+ * none, the ultimate base object), and reconstruct each node in the delta
+ * chain in order to generate the reconstructed data for this node.
 static void *get_base_data(struct base_data *c)
@@ -1028,6 +1019,10 @@
 		struct object_entry *child_obj;
 		struct base_data *child;
+		counter_lock();
+		display_progress(progress, nr_resolved_deltas);
+		counter_unlock();
 		if (list_empty(&work_head)) {
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index de5ad73..c30896c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1308,7 +1308,7 @@
 	const char *const git_submodule_helper_usage[] = {
-		N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
+		N_("git submodule--helper summary [<options>] [<commit>] [--] [<path>]"),
diff --git a/compat/mingw.h b/compat/mingw.h
index e6fe810..af8eddd 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -606,7 +606,7 @@
  * Call this function to open a new MinTTY (this assumes you are in Git for
  * Windows' SDK) with a GDB that attaches to the current process right away.
-extern void open_in_gdb(void);
+void open_in_gdb(void);
  * Used by Pthread API implementation for Windows
diff --git a/git.c b/git.c
index f1e8b56..4bdcdad 100644
--- a/git.c
+++ b/git.c
@@ -638,6 +638,25 @@
+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
+	const char *name;
+	int i;
+	/*
+	 * Callers can ask for a subset of the commands based on a certain
+	 * prefix, which is then dropped from the added names. The names in
+	 * the `commands[]` array do not have the `git-` prefix, though,
+	 * therefore we must expect the `prefix` to at least start with `git-`.
+	 */
+	if (!skip_prefix(prefix, "git-", &prefix))
+		BUG("prefix '%s' must start with 'git-'", prefix);
+	for (i = 0; i < ARRAY_SIZE(commands); i++)
+		if (skip_prefix(commands[i].cmd, prefix, &name))
+			add_cmdname(cmds, name, strlen(name));
 static void strip_extension(const char **argv)
diff --git a/help.c b/help.c
index 4e2468a..919cbb9 100644
--- a/help.c
+++ b/help.c
@@ -263,6 +263,8 @@
 	const char *env_path = getenv("PATH");
 	const char *exec_path = git_exec_path();
+	load_builtin_commands(prefix, main_cmds);
 	if (exec_path) {
 		list_commands_in_dir(main_cmds, exec_path, prefix);
 		QSORT(main_cmds->names, main_cmds->cnt, cmdname_compare);
diff --git a/help.h b/help.h
index dc02458..5871e93 100644
--- a/help.h
+++ b/help.h
@@ -32,6 +32,7 @@
 void load_command_list(const char *prefix,
 		       struct cmdnames *main_cmds,
 		       struct cmdnames *other_cmds);
+void load_builtin_commands(const char *prefix, struct cmdnames *cmds);
 void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
 void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
diff --git a/t/ b/t/
index c92e553..7c9d687 100755
--- a/t/
+++ b/t/
@@ -277,4 +277,11 @@
 	grep "^warning:.* expected .tagger. line" err
+test_expect_success 'index-pack -v --stdin produces progress for both phases' '
+	pack=$(git pack-objects --all pack </dev/null) &&
+	GIT_PROGRESS_DELAY=0 git index-pack -v --stdin <pack-$pack.pack 2>err &&
+	test_i18ngrep "Receiving objects" err &&
+	test_i18ngrep "Resolving deltas" err
diff --git a/t/ b/t/
index 7e928af..af0385f 100755
--- a/t/
+++ b/t/
@@ -282,10 +282,9 @@
 	test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
 			--signed --atomic --porcelain \
-			dst noop ff noff >out 2>&1 &&
+			dst noop ff noff >out 2>err &&
-	test_i18ngrep ! "gpg failed to sign" out &&
-	sed -n -e "/^To dst/,$ p" out >actual &&
+	test_i18ngrep ! "gpg failed to sign" err &&
 	cat >expect <<-EOF &&
 	To dst
 	=	refs/heads/noop:refs/heads/noop	[up to date]
@@ -293,7 +292,7 @@
 	!	refs/heads/noff:refs/heads/noff	[rejected] (non-fast-forward)
-	test_i18ncmp expect actual
+	test_cmp expect out