Merge tag 'l10n-2.28.0-rnd1' of https://www.github.com/git-l10n/git-po into master

l10n-2.28.0-rnd1

* tag 'l10n-2.28.0-rnd1' of https://www.github.com/git-l10n/git-po:
  l10n: es: 2.28.0 round 1
  l10n: de.po: Update German translation for Git v2.28.0
  l10n: de.po: fix grammar
  l10n: zh_CN: for git v2.28.0 l10n round 1
  l10n: zh_TW.po: v2.28.0 round 1 (0 untranslated)
  l10n: vi.po: correct "ident line" translation
  l10n: vi.po(4931t): Updated translation for v2.28.0
  l10n: fr v2.28.0 round 1
  l10n: sv.po: Update Swedish translation (4931t0f0u)
  l10n: it.po: update the Italian translation for Git 2.28.0 round 1
  l10n: tr: v2.28.0 round 1
  l10n: git.pot: v2.28.0 round 1 (70 new, 14 removed)
  l10n: Update Catalan translation
diff --git a/Documentation/RelNotes/2.28.0.txt b/Documentation/RelNotes/2.28.0.txt
index c664771..6baf781 100644
--- a/Documentation/RelNotes/2.28.0.txt
+++ b/Documentation/RelNotes/2.28.0.txt
@@ -9,15 +9,6 @@
  * "fetch.writeCommitGraph" is deemed to be still a bit too risky and
    is no longer part of the "feature.experimental" set.
 
- * It used to be that setting extensions.* configuration variables
-   alone, while leaving core.repositoryFormatVersion=0, made these
-   settings effective, which was a wrong thing to do.  In version 0,
-   there was no special meaning in extensions.* configuration
-   variables.  This has been corrected.  If you need these repository
-   extensions to be effective, the core.repositoryFormatVersion
-   variable needs to be updated to 1 after vetting these extensions.*
-   variables are set correctly.
-
 
 UI, Workflows & Features
 
@@ -52,6 +43,8 @@
    prepared to work with "set -u" but recent changes got a bit more
    sloppy.  This has been corrected.
 
+ * "git gui" now allows opening work trees from the start-up dialog.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -118,6 +111,14 @@
 
  * The effort to avoid using test_must_fail on non-git command continues.
 
+ * In 2.28-rc0, we corrected a bug that some repository extensions are
+   honored by mistake even in a version 0 repositories (these
+   configuration variables in extensions.* namespace were supposed to
+   have special meaning in repositories whose version numbers are 1 or
+   higher), but this was a bit too big a change.  The behaviour in
+   recent versions of Git where certain extensions.* were honored by
+   mistake even in version 0 repositories has been restored.
+
 
 Fixes since v2.27
 -----------------
@@ -213,6 +214,14 @@
    a path in the working tree.
    (merge 35e6e212fd mt/entry-fstat-fallback-fix later to maint).
 
+ * When an aliased command, whose output is piped to a pager by git,
+   gets killed by a signal, the pager got into a funny state, which
+   has been corrected (again).
+   (merge c0d73a59c9 ta/wait-on-aliased-commands-upon-signal later to maint).
+
+ * The code to produce progress output from "git commit-graph --write"
+   had a few breakages, which have been fixed.
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 2c31a7aa44 jx/pkt-line-doc-count-fix later to maint).
    (merge d63ae31962 cb/t5608-cleanup later to maint).
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 1018110..727f24d 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -63,13 +63,7 @@
 	This is to view the changes between two arbitrary
 	<commit>.
 
-'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
-
-	This is synonymous to the previous form.  If <commit> on
-	one side is omitted, it will have the same effect as
-	using HEAD instead.
-
-'git diff' [<options>] <commit> [<commit>...] <commit> [--] [<path>...]::
+'git diff' [<options>] <commit> <commit>... <commit> [--] [<path>...]::
 
 	This form is to view the results of a merge commit.  The first
 	listed <commit> must be the merge itself; the remaining two or
@@ -78,6 +72,13 @@
 	For instance, if `master` names a merge commit, `git diff master
 	master^@` gives the same combined diff as `git show master`.
 
+'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
+
+	This is synonymous to the earlier form (without the "..") for
+	viewing the changes between two arbitrary <commit>.  If <commit> on
+	one side is omitted, it will have the same effect as
+	using HEAD instead.
+
 'git diff' [<options>] <commit>\...<commit> [--] [<path>...]::
 
 	This form is to view the changes on the branch containing
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 6424711..31b601e 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -404,8 +404,8 @@
 `git send-pack` on the other end, so you can simply `echo` messages
 for the user.
 
-ref-transaction
-~~~~~~~~~~~~~~~
+reference-transaction
+~~~~~~~~~~~~~~~~~~~~~
 
 This hook is invoked by any Git command that performs reference
 updates. It executes whenever a reference transaction is prepared,
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index 2db7ba7..47cf97f 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -292,7 +292,7 @@
 
 
 Branch management for next and seen after a feature release
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 After a feature release, the integration branch 'next' may optionally be
 rewound and rebuilt from the tip of 'master' using the surviving
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 7b0cfeb..bcb0e9e 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.27.GIT
+DEF_VER=v2.28-rc2
 
 LF='
 '
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index f6797e2..16c9f61 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -251,7 +251,7 @@
 			}
 		}
 
-
+		stop_progress(&progress);
 	}
 
 	if (write_commit_graph(odb,
@@ -264,8 +264,6 @@
 cleanup:
 	string_list_clear(&pack_indexes, 0);
 	strbuf_release(&buf);
-	if (progress)
-		stop_progress(&progress);
 	return result;
 }
 
diff --git a/cache.h b/cache.h
index 126ec56..6544264 100644
--- a/cache.h
+++ b/cache.h
@@ -1042,7 +1042,6 @@
 	int worktree_config;
 	int is_bare;
 	int hash_algo;
-	int has_extensions;
 	char *work_tree;
 	struct string_list unknown_extensions;
 };
diff --git a/commit-graph.c b/commit-graph.c
index 328ab06..1af68c2 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1149,23 +1149,14 @@
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	uint32_t cur_pos = 0;
-	struct progress *progress = NULL;
-	int i = 0;
-
-	if (ctx->report_progress)
-		progress = start_delayed_progress(
-			_("Writing changed paths Bloom filters index"),
-			ctx->commits.nr);
 
 	while (list < last) {
 		struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
 		cur_pos += filter->len;
-		display_progress(progress, ++i);
+		display_progress(ctx->progress, ++ctx->progress_cnt);
 		hashwrite_be32(f, cur_pos);
 		list++;
 	}
-
-	stop_progress(&progress);
 }
 
 static void write_graph_chunk_bloom_data(struct hashfile *f,
@@ -1174,13 +1165,6 @@
 {
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
-	struct progress *progress = NULL;
-	int i = 0;
-
-	if (ctx->report_progress)
-		progress = start_delayed_progress(
-			_("Writing changed paths Bloom filters data"),
-			ctx->commits.nr);
 
 	hashwrite_be32(f, settings->hash_version);
 	hashwrite_be32(f, settings->num_hashes);
@@ -1188,12 +1172,10 @@
 
 	while (list < last) {
 		struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
-		display_progress(progress, ++i);
+		display_progress(ctx->progress, ++ctx->progress_cnt);
 		hashwrite(f, filter->data, filter->len * sizeof(unsigned char));
 		list++;
 	}
-
-	stop_progress(&progress);
 }
 
 static int oid_compare(const void *_a, const void *_b)
@@ -1423,12 +1405,13 @@
 			_("Collecting referenced commits"), 0);
 
 	for_each_ref(add_ref_to_set, &data);
+
+	stop_progress(&data.progress);
+
 	result = write_commit_graph(odb, NULL, &commits,
 				    flags, split_opts);
 
 	oidset_clear(&commits);
-	if (data.progress)
-		stop_progress(&data.progress);
 	return result;
 }
 
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index e6cd546..16260ba 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -433,7 +433,7 @@
 	local sparse=""
 	if [ -z "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
 	   [ -z "${GIT_PS1_OMITSPARSESTATE}" ] &&
-	   [ "$(git config --bool core.sparseCheckout)" == "true" ]; then
+	   [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
 		sparse="|SPARSE"
 	fi
 
@@ -542,7 +542,7 @@
 		fi
 
 		if [ -n "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
-		   [ "$(git config --bool core.sparseCheckout)" == "true" ]; then
+		   [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
 			h="?"
 		fi
 
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index e54f3e6..af1fee7 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -357,31 +357,10 @@
 	if {$outdir_var ne ""} {
 		upvar 1 $outdir_var outdir
 	}
-	if {[file isfile $path]} {
-		set fp [open $path r]
-		gets $fp line
-		close $fp
-		if {[regexp "^gitdir: (.+)$" $line line link_target]} {
-			set path [file join [file dirname $path] $link_target]
-			set path [file normalize $path]
-		}
+	if {[catch {set outdir [git rev-parse --resolve-git-dir $path]}]} {
+		return 0
 	}
-
-	if {[file exists [file join $path HEAD]]
-	 && [file exists [file join $path objects]]
-	 && [file exists [file join $path config]]} {
-		set outdir $path
-		return 1
-	}
-	if {[is_Cygwin]} {
-		if {[file exists [file join $path HEAD]]
-		 && [file exists [file join $path objects.lnk]]
-		 && [file exists [file join $path config.lnk]]} {
-			set outdir $path
-			return 1
-		}
-	}
-	return 0
+	return 1
 }
 
 proc _objdir {path} {
diff --git a/git.c b/git.c
index 2f021b9..6cd887b 100644
--- a/git.c
+++ b/git.c
@@ -346,6 +346,8 @@
 			commit_pager_choice();
 
 			child.use_shell = 1;
+			child.clean_on_exit = 1;
+			child.wait_after_clean = 1;
 			child.trace2_child_class = "shell_alias";
 			argv_array_push(&child.args, alias_string + 1);
 			argv_array_pushv(&child.args, (*argv) + 1);
@@ -767,7 +769,7 @@
 			 * OK to return. Otherwise, we just pass along the status code.
 			 */
 			i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
-						  RUN_CLEAN_ON_EXIT, "git_alias");
+						  RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, "git_alias");
 			if (i >= 0 || errno != ENOENT)
 				exit(i);
 			die("could not execute builtin %s", **argv);
diff --git a/run-command.c b/run-command.c
index 9b3a57d..a735e38 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1039,6 +1039,7 @@
 	cmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
 	cmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0;
 	cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;
+	cmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;
 	cmd.dir = dir;
 	cmd.env = env;
 	cmd.trace2_child_class = tr2_class;
diff --git a/run-command.h b/run-command.h
index 191dfcd..ef3071a 100644
--- a/run-command.h
+++ b/run-command.h
@@ -229,6 +229,7 @@
 #define RUN_SILENT_EXEC_FAILURE 8
 #define RUN_USING_SHELL 16
 #define RUN_CLEAN_ON_EXIT 32
+#define RUN_WAIT_AFTER_CLEAN 64
 
 /**
  * Convenience functions that encapsulate a sequence of
diff --git a/setup.c b/setup.c
index dbac2ea..3a81307 100644
--- a/setup.c
+++ b/setup.c
@@ -455,7 +455,6 @@
 	if (strcmp(var, "core.repositoryformatversion") == 0)
 		data->version = git_config_int(var, value);
 	else if (skip_prefix(var, "extensions.", &ext)) {
-		data->has_extensions = 1;
 		/*
 		 * record any known extensions here; otherwise,
 		 * we fall through to recording it as unknown, and
@@ -507,15 +506,9 @@
 		die("%s", err.buf);
 	}
 
-	if (candidate->version >= 1) {
-		repository_format_precious_objects = candidate->precious_objects;
-		set_repository_format_partial_clone(candidate->partial_clone);
-		repository_format_worktree_config = candidate->worktree_config;
-	} else {
-		repository_format_precious_objects = 0;
-		set_repository_format_partial_clone(NULL);
-		repository_format_worktree_config = 0;
-	}
+	repository_format_precious_objects = candidate->precious_objects;
+	set_repository_format_partial_clone(candidate->partial_clone);
+	repository_format_worktree_config = candidate->worktree_config;
 	string_list_clear(&candidate->unknown_extensions, 0);
 
 	if (repository_format_worktree_config) {
@@ -559,13 +552,16 @@
 	if (repo_fmt.version >= target_version)
 		return 0;
 
-	if (verify_repository_format(&repo_fmt, &err) < 0 ||
-	    (!repo_fmt.version && repo_fmt.has_extensions)) {
-		warning("unable to upgrade repository format from %d to %d: %s",
-			repo_fmt.version, target_version, err.buf);
+	if (verify_repository_format(&repo_fmt, &err) < 0) {
+		error("cannot upgrade repository format from %d to %d: %s",
+		      repo_fmt.version, target_version, err.buf);
 		strbuf_release(&err);
 		return -1;
 	}
+	if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
+		return error("cannot upgrade repository format: "
+			     "unknown extension %s",
+			     repo_fmt.unknown_extensions.items[0].string);
 
 	strbuf_addf(&repo_version, "%d", target_version);
 	git_config_set("core.repositoryformatversion", repo_version.buf);
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 463dc3a..6aa0f31 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -42,14 +42,25 @@
 	test_cmp_config -C client 1 core.repositoryformatversion
 '
 
-test_expect_success 'convert shallow clone to partial clone must fail with any extension' '
+test_expect_success 'convert to partial clone with noop extension' '
 	rm -fr server client &&
 	test_create_repo server &&
 	test_commit -C server my_commit 1 &&
 	test_commit -C server my_commit2 1 &&
 	git clone --depth=1 "file://$(pwd)/server" client &&
 	test_cmp_config -C client 0 core.repositoryformatversion &&
-	git -C client config extensions.partialclone origin &&
+	git -C client config extensions.noop true &&
+	git -C client fetch --unshallow --filter="blob:none"
+'
+
+test_expect_success 'converting to partial clone fails with unrecognized extension' '
+	rm -fr server client &&
+	test_create_repo server &&
+	test_commit -C server my_commit 1 &&
+	test_commit -C server my_commit2 1 &&
+	git clone --depth=1 "file://$(pwd)/server" client &&
+	test_cmp_config -C client 0 core.repositoryformatversion &&
+	git -C client config extensions.nonsense true &&
 	test_must_fail git -C client fetch --unshallow --filter="blob:none"
 '
 
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index b6aa04b..4c07341 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -870,7 +870,7 @@
 	>.git/config.lock &&
 	git branch locked &&
 	test_must_fail git branch --set-upstream-to locked 2>err &&
-	test_i18ngrep "could not lock config file .git/config: File exists" err
+	test_i18ngrep "could not lock config file .git/config" err
 '
 
 test_expect_success 'use --set-upstream-to modify HEAD' '
@@ -901,7 +901,7 @@
 	git branch --set-upstream-to locked &&
 	>.git/config.lock &&
 	test_must_fail git branch --unset-upstream 2>err &&
-	test_i18ngrep "could not lock config file .git/config: File exists" err
+	test_i18ngrep "could not lock config file .git/config" err
 '
 
 test_expect_success 'test --unset-upstream on HEAD' '